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 <stdio.h>
20
21 #include <algorithm>
22 #include <cctype>
23 #include <cstdlib>
24 #include <filesystem>
25 #include <fstream>
26 #include <functional>
27 #include <map>
28 #include <string>
29 #include <utility>
30 #include <vector>
31
32 #include "Collation.h"
33 #include "frameworks/proto_logging/stats/atom_field_options.pb.h"
34 #include "settings_provider.h"
35
36 namespace android {
37 namespace stats_log_api_gen {
38
39 namespace fs = std::filesystem;
40
41 using std::ifstream;
42 using std::map;
43 using std::string;
44 using std::vector;
45
46 /**
47 * Inlining this method because "android-base/strings.h" is not available on
48 * google3.
49 */
Split(const string & s,const string & delimiters)50 static vector<string> Split(const string& s, const string& delimiters) {
51 vector<string> result;
52
53 size_t base = 0;
54 size_t found;
55 while (true) {
56 found = s.find_first_of(delimiters, base);
57 result.push_back(s.substr(base, found - base));
58 if (found == s.npos) break;
59 base = found + 1;
60 }
61
62 return result;
63 }
64
write_native_histogram_helper_signature(FILE * out,const string & atomName,const string & fieldName)65 static void write_native_histogram_helper_signature(FILE* out, const string& atomName,
66 const string& fieldName) {
67 fprintf(out,
68 "std::unique_ptr<%s> "
69 "create_%s__%s_histogram()",
70 HISTOGRAM_STEM.c_str(), atomName.c_str(), fieldName.c_str());
71 }
72
write_native_histogram_helper_definition(FILE * out,const string & atomName,const string & fieldName,const os::statsd::HistogramBinOption & histBinOption)73 static int write_native_histogram_helper_definition(
74 FILE* out, const string& atomName, const string& fieldName,
75 const os::statsd::HistogramBinOption& histBinOption) {
76 int errorCount = 0;
77
78 // Print method signature.
79 write_native_histogram_helper_signature(out, atomName, fieldName);
80 fprintf(out, " {\n");
81
82 fprintf(out, "%*sreturn %s::create", 8, "", HISTOGRAM_STEM.c_str());
83 if (histBinOption.has_generated_bins()) {
84 const os::statsd::HistogramBinOption::GeneratedBins& genBins =
85 histBinOption.generated_bins();
86 switch (genBins.strategy()) {
87 case os::statsd::HistogramBinOption::GeneratedBins::LINEAR:
88 fprintf(out, "Linear");
89 break;
90 case os::statsd::HistogramBinOption::GeneratedBins::EXPONENTIAL:
91 fprintf(out, "Exponential");
92 break;
93 default:
94 errorCount++;
95 }
96 fprintf(out, "Bins(%f, %f, %d);\n", genBins.min(), genBins.max(), genBins.count());
97 } else if (histBinOption.has_explicit_bins()) {
98 const os::statsd::HistogramBinOption::ExplicitBins& explicitBins =
99 histBinOption.explicit_bins();
100 fprintf(out, "ExplicitBins({");
101 const char* separator = "";
102 for (const float bin : explicitBins.bin()) {
103 fprintf(out, "%s%f", separator, bin);
104 separator = ", ";
105 }
106 fprintf(out, "});\n");
107 }
108 fprintf(out, "}\n\n");
109
110 return errorCount;
111 }
112
write_java_histogram_helper(FILE * out,const string & atomName,const string & fieldName,const os::statsd::HistogramBinOption & histBinOption,const bool staticMethods)113 static int write_java_histogram_helper(FILE* out, const string& atomName, const string& fieldName,
114 const os::statsd::HistogramBinOption& histBinOption,
115 const bool staticMethods) {
116 int errorCount = 0;
117
118 // Print method signature.
119 const char* methodPrefix = staticMethods ? "static " : "";
120 fprintf(out, "%*spublic %s%s create%s_%sHistogram() {\n", 4, "", methodPrefix,
121 HISTOGRAM_STEM.c_str(), snake_to_pascal(atomName).c_str(),
122 snake_to_pascal(fieldName).c_str());
123
124 fprintf(out, "%*sreturn %s.create", 8, "", HISTOGRAM_STEM.c_str());
125 if (histBinOption.has_generated_bins()) {
126 const os::statsd::HistogramBinOption::GeneratedBins& genBins =
127 histBinOption.generated_bins();
128 switch (genBins.strategy()) {
129 case os::statsd::HistogramBinOption::GeneratedBins::LINEAR:
130 fprintf(out, "Linear");
131 break;
132 case os::statsd::HistogramBinOption::GeneratedBins::EXPONENTIAL:
133 fprintf(out, "Exponential");
134 break;
135 default:
136 errorCount++;
137 }
138 fprintf(out, "Bins(%ff, %ff, %d);\n", genBins.min(), genBins.max(), genBins.count());
139 } else if (histBinOption.has_explicit_bins()) {
140 const os::statsd::HistogramBinOption::ExplicitBins& explicitBins =
141 histBinOption.explicit_bins();
142 fprintf(out, "ExplicitBins(");
143 const char* separator = "";
144 for (const float bin : explicitBins.bin()) {
145 fprintf(out, "%s%ff", separator, bin);
146 separator = ", ";
147 }
148 fprintf(out, ");\n");
149 }
150 fprintf(out, "%*s}\n\n", 4, "");
151
152 return errorCount;
153 }
154
write_src_header(FILE * out,const fs::path & filePath)155 static int write_src_header(FILE* out, const fs::path& filePath) {
156 ifstream fileStream(filePath);
157 if (!fileStream.is_open()) {
158 fprintf(stderr, "Could not open file: %s", filePath.c_str());
159 return 1;
160 }
161
162 string line;
163 bool atImports = false;
164 while (std::getline(fileStream, line)) {
165 if (line == "// HEADER_BEGIN") {
166 atImports = true;
167 } else if (line == "// HEADER_END") {
168 break;
169 } else if (atImports) {
170 fprintf(out, "%s\n", line.c_str());
171 }
172 }
173 fileStream.close();
174
175 return 0;
176 }
177
write_src_body(FILE * out,const fs::path & filePath,int indent,const std::function<bool (string & firstLine)> & firstLineTransformer)178 static int write_src_body(FILE* out, const fs::path& filePath, int indent,
179 const std::function<bool(string& firstLine)>& firstLineTransformer) {
180 ifstream fileStream(filePath);
181 if (!fileStream.is_open()) {
182 fprintf(stderr, "Could not open file: %s\n", filePath.c_str());
183 return 1;
184 }
185
186 string line;
187 bool atClassDef = false;
188
189 while (std::getline(fileStream, line)) {
190 if (line == "// BODY_BEGIN") {
191 std::getline(fileStream, line);
192 if (firstLineTransformer && !firstLineTransformer(line)) {
193 fprintf(stderr, "First line transform failed: %s\n", filePath.c_str());
194 return 1;
195 }
196 fprintf(out, "%*s%s\n", indent, "", line.c_str());
197 atClassDef = true;
198 } else if (line == "// BODY_END") {
199 break;
200 } else if (atClassDef) {
201 fprintf(out, "%*s%s\n", indent, "", line.c_str());
202 }
203 }
204 fileStream.close();
205
206 return 0;
207 }
208
write_srcs_bodies(FILE * out,const char * path,int indent,const vector<string> & excludeList,const std::function<bool (string & firstLine)> & firstLineTransformer)209 static int write_srcs_bodies(FILE* out, const char* path, int indent,
210 const vector<string>& excludeList,
211 const std::function<bool(string& firstLine)>& firstLineTransformer) {
212 int errors = 0;
213 const string fullPath = get_data_dir_path(path);
214 for (const fs::path& filePath : fs::directory_iterator(fullPath)) {
215 // Inline source bodies from filePath if it's not in excludeList.
216 if (std::find(excludeList.begin(), excludeList.end(), filePath.stem()) ==
217 excludeList.end()) {
218 errors += write_src_body(out, filePath, indent, firstLineTransformer);
219 }
220 }
221
222 return errors;
223 }
224
make_java_class_static(string & line)225 static bool make_java_class_static(string& line) {
226 const size_t pos = line.find(' ');
227 if (pos == string::npos) {
228 return false;
229 }
230 line.insert(pos, " static");
231 return true;
232 }
233
build_non_chained_decl_map(const Atoms & atoms,std::map<int,AtomDeclSet::const_iterator> * decl_map)234 void build_non_chained_decl_map(const Atoms& atoms,
235 std::map<int, AtomDeclSet::const_iterator>* decl_map) {
236 for (AtomDeclSet::const_iterator atomIt = atoms.non_chained_decls.begin();
237 atomIt != atoms.non_chained_decls.end(); atomIt++) {
238 decl_map->insert(std::make_pair((*atomIt)->code, atomIt));
239 }
240 }
241
get_annotation_id_constants(const string & prefix)242 const map<AnnotationId, AnnotationStruct>& get_annotation_id_constants(const string& prefix) {
243 static const map<AnnotationId, AnnotationStruct>* ANNOTATION_ID_CONSTANTS =
244 new map<AnnotationId, AnnotationStruct>{
245 {ANNOTATION_ID_IS_UID, AnnotationStruct(prefix + "IS_UID", API_S)},
246 {ANNOTATION_ID_TRUNCATE_TIMESTAMP,
247 AnnotationStruct(prefix + "TRUNCATE_TIMESTAMP", API_S)},
248 {ANNOTATION_ID_PRIMARY_FIELD,
249 AnnotationStruct(prefix + "PRIMARY_FIELD", API_S)},
250 {ANNOTATION_ID_EXCLUSIVE_STATE,
251 AnnotationStruct(prefix + "EXCLUSIVE_STATE", API_S)},
252 {ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID,
253 AnnotationStruct(prefix + "PRIMARY_FIELD_FIRST_UID", API_S)},
254 {ANNOTATION_ID_DEFAULT_STATE,
255 AnnotationStruct(prefix + "DEFAULT_STATE", API_S)},
256 {ANNOTATION_ID_TRIGGER_STATE_RESET,
257 AnnotationStruct(prefix + "TRIGGER_STATE_RESET", API_S)},
258 {ANNOTATION_ID_STATE_NESTED, AnnotationStruct(prefix + "STATE_NESTED", API_S)},
259 {ANNOTATION_ID_RESTRICTION_CATEGORY,
260 AnnotationStruct(prefix + "RESTRICTION_CATEGORY", API_U)},
261 {ANNOTATION_ID_FIELD_RESTRICTION_PERIPHERAL_DEVICE_INFO,
262 AnnotationStruct(prefix + "FIELD_RESTRICTION_PERIPHERAL_DEVICE_INFO", API_U)},
263 {ANNOTATION_ID_FIELD_RESTRICTION_APP_USAGE,
264 AnnotationStruct(prefix + "FIELD_RESTRICTION_APP_USAGE", API_U)},
265 {ANNOTATION_ID_FIELD_RESTRICTION_APP_ACTIVITY,
266 AnnotationStruct(prefix + "FIELD_RESTRICTION_APP_ACTIVITY", API_U)},
267 {ANNOTATION_ID_FIELD_RESTRICTION_HEALTH_CONNECT,
268 AnnotationStruct(prefix + "FIELD_RESTRICTION_HEALTH_CONNECT", API_U)},
269 {ANNOTATION_ID_FIELD_RESTRICTION_ACCESSIBILITY,
270 AnnotationStruct(prefix + "FIELD_RESTRICTION_ACCESSIBILITY", API_U)},
271 {ANNOTATION_ID_FIELD_RESTRICTION_SYSTEM_SEARCH,
272 AnnotationStruct(prefix + "FIELD_RESTRICTION_SYSTEM_SEARCH", API_U)},
273 {ANNOTATION_ID_FIELD_RESTRICTION_USER_ENGAGEMENT,
274 AnnotationStruct(prefix + "FIELD_RESTRICTION_USER_ENGAGEMENT", API_U)},
275 {ANNOTATION_ID_FIELD_RESTRICTION_AMBIENT_SENSING,
276 AnnotationStruct(prefix + "FIELD_RESTRICTION_AMBIENT_SENSING", API_U)},
277 {ANNOTATION_ID_FIELD_RESTRICTION_DEMOGRAPHIC_CLASSIFICATION,
278 AnnotationStruct(prefix + "FIELD_RESTRICTION_DEMOGRAPHIC_CLASSIFICATION",
279 API_U)},
280 };
281
282 return *ANNOTATION_ID_CONSTANTS;
283 }
284
get_java_build_version_code(int apiLevel)285 string get_java_build_version_code(int apiLevel) {
286 switch (apiLevel) {
287 case API_Q:
288 return "Build.VERSION_CODES.Q";
289 case API_R:
290 return "Build.VERSION_CODES.R";
291 case API_S:
292 return "Build.VERSION_CODES.S";
293 case API_S_V2:
294 return "Build.VERSION_CODES.S_V2";
295 case API_T:
296 return "Build.VERSION_CODES.TIRAMISU";
297 case API_U:
298 return "Build.VERSION_CODES.UPSIDE_DOWN_CAKE";
299 default:
300 return "Build.VERSION_CODES.CUR_DEVELOPMENT";
301 }
302 }
303
get_restriction_category_str(int annotationValue)304 string get_restriction_category_str(int annotationValue) {
305 switch (annotationValue) {
306 case os::statsd::RestrictionCategory::RESTRICTION_DIAGNOSTIC:
307 return "RESTRICTION_CATEGORY_DIAGNOSTIC";
308 case os::statsd::RestrictionCategory::RESTRICTION_SYSTEM_INTELLIGENCE:
309 return "RESTRICTION_CATEGORY_SYSTEM_INTELLIGENCE";
310 case os::statsd::RestrictionCategory::RESTRICTION_AUTHENTICATION:
311 return "RESTRICTION_CATEGORY_AUTHENTICATION";
312 case os::statsd::RestrictionCategory::RESTRICTION_FRAUD_AND_ABUSE:
313 return "RESTRICTION_CATEGORY_FRAUD_AND_ABUSE";
314 default:
315 return "";
316 }
317 }
318
319 /**
320 * Turn lower and camel case into upper case with underscores.
321 */
make_constant_name(const string & str)322 string make_constant_name(const string& str) {
323 string result;
324 const int N = str.size();
325 bool underscore_next = false;
326 for (int i = 0; i < N; i++) {
327 char c = str[i];
328 if (c >= 'A' && c <= 'Z') {
329 if (underscore_next) {
330 result += '_';
331 underscore_next = false;
332 }
333 } else if (c >= 'a' && c <= 'z') {
334 c = 'A' + c - 'a';
335 underscore_next = true;
336 } else if (c == '_') {
337 underscore_next = false;
338 }
339 result += c;
340 }
341 return result;
342 }
343
344 /**
345 * Convert snake_case to PascalCase
346 */
snake_to_pascal(const string & snake)347 string snake_to_pascal(const string& snake) {
348 string pascal;
349 bool capitalize = true;
350 for (const char c : snake) {
351 if (c == '_') {
352 capitalize = true;
353 } else if (capitalize) {
354 pascal += std::toupper(c);
355 capitalize = false;
356 } else {
357 pascal += c;
358 }
359 }
360 return pascal;
361 }
362
cpp_type_name(java_type_t type,bool isVendorAtomLogging)363 const char* cpp_type_name(java_type_t type, bool isVendorAtomLogging) {
364 switch (type) {
365 case JAVA_TYPE_BOOLEAN:
366 return "bool";
367 case JAVA_TYPE_INT: // Fallthrough.
368 case JAVA_TYPE_ENUM:
369 return "int32_t";
370 case JAVA_TYPE_LONG:
371 return "int64_t";
372 case JAVA_TYPE_FLOAT:
373 return "float";
374 case JAVA_TYPE_DOUBLE:
375 return "double";
376 case JAVA_TYPE_STRING:
377 return "char const*";
378 case JAVA_TYPE_BYTE_ARRAY:
379 return isVendorAtomLogging ? "const std::vector<uint8_t>&" : "const BytesField&";
380 case JAVA_TYPE_BOOLEAN_ARRAY:
381 return isVendorAtomLogging ? "const std::vector<bool>&" : "const bool*";
382 case JAVA_TYPE_INT_ARRAY: // Fallthrough.
383 case JAVA_TYPE_ENUM_ARRAY:
384 return "const std::vector<int32_t>&";
385 case JAVA_TYPE_LONG_ARRAY:
386 return "const std::vector<int64_t>&";
387 case JAVA_TYPE_FLOAT_ARRAY:
388 return "const std::vector<float>&";
389 case JAVA_TYPE_STRING_ARRAY:
390 return "const std::vector<char const*>&";
391 case JAVA_TYPE_DOUBLE_ARRAY:
392 return "const std::vector<double>&";
393 default:
394 return "UNKNOWN";
395 }
396 }
397
java_type_name(java_type_t type)398 const char* java_type_name(java_type_t type) {
399 switch (type) {
400 case JAVA_TYPE_BOOLEAN:
401 return "boolean";
402 case JAVA_TYPE_INT: // Fallthrough.
403 case JAVA_TYPE_ENUM:
404 return "int";
405 case JAVA_TYPE_LONG:
406 return "long";
407 case JAVA_TYPE_FLOAT:
408 return "float";
409 case JAVA_TYPE_DOUBLE:
410 return "double";
411 case JAVA_TYPE_STRING:
412 return "java.lang.String";
413 case JAVA_TYPE_BYTE_ARRAY:
414 return "byte[]";
415 case JAVA_TYPE_BOOLEAN_ARRAY:
416 return "boolean[]";
417 case JAVA_TYPE_INT_ARRAY: // Fallthrough.
418 case JAVA_TYPE_ENUM_ARRAY:
419 return "int[]";
420 case JAVA_TYPE_LONG_ARRAY:
421 return "long[]";
422 case JAVA_TYPE_FLOAT_ARRAY:
423 return "float[]";
424 case JAVA_TYPE_STRING_ARRAY:
425 return "java.lang.String[]";
426 case JAVA_TYPE_DOUBLE_ARRAY:
427 return "double[]";
428 default:
429 return "UNKNOWN";
430 }
431 }
432
433 // Does not include AttributionChain type.
is_repeated_field(java_type_t type)434 bool is_repeated_field(java_type_t type) {
435 switch (type) {
436 case JAVA_TYPE_BOOLEAN_ARRAY:
437 case JAVA_TYPE_INT_ARRAY:
438 case JAVA_TYPE_FLOAT_ARRAY:
439 case JAVA_TYPE_LONG_ARRAY:
440 case JAVA_TYPE_STRING_ARRAY:
441 case JAVA_TYPE_ENUM_ARRAY:
442 return true;
443 default:
444 return false;
445 }
446 }
447
contains_repeated_field(const vector<java_type_t> & signature)448 static bool contains_repeated_field(const vector<java_type_t>& signature) {
449 for (const java_type_t& javaType : signature) {
450 if (is_repeated_field(javaType)) {
451 return true;
452 }
453 }
454 return false;
455 }
456
is_primitive_field(java_type_t type)457 bool is_primitive_field(java_type_t type) {
458 switch (type) {
459 case JAVA_TYPE_BOOLEAN:
460 case JAVA_TYPE_INT:
461 case JAVA_TYPE_LONG:
462 case JAVA_TYPE_FLOAT:
463 case JAVA_TYPE_STRING:
464 case JAVA_TYPE_ENUM:
465 return true;
466 default:
467 return false;
468 }
469 }
470
471 // Native
472 // Writes namespaces for the cpp and header files
write_namespace(FILE * out,const string & cppNamespaces)473 void write_namespace(FILE* out, const string& cppNamespaces) {
474 const vector<string> cppNamespaceVec = Split(cppNamespaces, ",");
475 for (const string& cppNamespace : cppNamespaceVec) {
476 fprintf(out, "namespace %s {\n", cppNamespace.c_str());
477 }
478 }
479
480 // Writes namespace closing brackets for cpp and header files.
write_closing_namespace(FILE * out,const string & cppNamespaces)481 void write_closing_namespace(FILE* out, const string& cppNamespaces) {
482 vector<string> cppNamespaceVec = Split(cppNamespaces, ",");
483 for (auto it = cppNamespaceVec.rbegin(); it != cppNamespaceVec.rend(); ++it) {
484 fprintf(out, "} // namespace %s\n", it->c_str());
485 }
486 }
487
write_cpp_usage(FILE * out,const string & method_name,const string & atom_code_name,const AtomDecl & atom,const AtomDecl & attributionDecl,bool isVendorAtomLogging=false)488 static void write_cpp_usage(FILE* out, const string& method_name, const string& atom_code_name,
489 const AtomDecl& atom, const AtomDecl& attributionDecl,
490 bool isVendorAtomLogging = false) {
491 const char* delimiterStr = method_name.find('(') == string::npos ? "(" : " ";
492 fprintf(out, " * Usage: %s%s%s", method_name.c_str(), delimiterStr, atom_code_name.c_str());
493
494 for (vector<AtomField>::const_iterator field = atom.fields.begin(); field != atom.fields.end();
495 field++) {
496 if (field->javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
497 for (const auto& chainField : attributionDecl.fields) {
498 if (chainField.javaType == JAVA_TYPE_STRING) {
499 fprintf(out, ", const std::vector<%s>& %s", cpp_type_name(chainField.javaType),
500 chainField.name.c_str());
501 } else {
502 fprintf(out, ", const %s* %s, size_t %s_length",
503 cpp_type_name(chainField.javaType), chainField.name.c_str(),
504 chainField.name.c_str());
505 }
506 }
507 } else {
508 fprintf(out, ", %s %s", cpp_type_name(field->javaType, isVendorAtomLogging),
509 field->name.c_str());
510 }
511 }
512 fprintf(out, ");\n");
513 }
514
write_native_atom_constants(FILE * out,const Atoms & atoms,const AtomDecl & attributionDecl,const string & methodName,bool isVendorAtomLogging)515 void write_native_atom_constants(FILE* out, const Atoms& atoms, const AtomDecl& attributionDecl,
516 const string& methodName, bool isVendorAtomLogging) {
517 fprintf(out, "/**\n");
518 fprintf(out, " * Constants for atom codes.\n");
519 fprintf(out, " */\n");
520 fprintf(out, "enum {\n");
521
522 std::map<int, AtomDeclSet::const_iterator> atom_code_to_non_chained_decl_map;
523 build_non_chained_decl_map(atoms, &atom_code_to_non_chained_decl_map);
524
525 size_t i = 0;
526 // Print atom constants
527 for (AtomDeclSet::const_iterator atomIt = atoms.decls.begin(); atomIt != atoms.decls.end();
528 atomIt++) {
529 const string constant = make_constant_name((*atomIt)->name);
530 fprintf(out, "\n");
531 fprintf(out, " /**\n");
532 fprintf(out, " * %s %s\n", (*atomIt)->message.c_str(), (*atomIt)->name.c_str());
533 write_cpp_usage(out, methodName, constant, **atomIt, attributionDecl, isVendorAtomLogging);
534
535 auto non_chained_decl = atom_code_to_non_chained_decl_map.find((*atomIt)->code);
536 if (non_chained_decl != atom_code_to_non_chained_decl_map.end()) {
537 write_cpp_usage(out, methodName + "_non_chained", constant, **non_chained_decl->second,
538 attributionDecl, isVendorAtomLogging);
539 }
540 fprintf(out, " */\n");
541 char const* const comma = (i == atoms.decls.size() - 1) ? "" : ",";
542 fprintf(out, " %s = %d%s\n", constant.c_str(), (*atomIt)->code, comma);
543 i++;
544 }
545 fprintf(out, "\n");
546 fprintf(out, "};\n");
547 fprintf(out, "\n");
548 }
549
write_native_atom_enums(FILE * out,const Atoms & atoms)550 void write_native_atom_enums(FILE* out, const Atoms& atoms) {
551 // Print constants for the enum values.
552 fprintf(out, "//\n");
553 fprintf(out, "// Constants for enum values\n");
554 fprintf(out, "//\n\n");
555 for (AtomDeclSet::const_iterator atomIt = atoms.decls.begin(); atomIt != atoms.decls.end();
556 atomIt++) {
557 for (vector<AtomField>::const_iterator field = (*atomIt)->fields.begin();
558 field != (*atomIt)->fields.end(); field++) {
559 if (field->javaType == JAVA_TYPE_ENUM || field->javaType == JAVA_TYPE_ENUM_ARRAY) {
560 fprintf(out, "// Values for %s.%s\n", (*atomIt)->message.c_str(),
561 field->name.c_str());
562 for (map<int, string>::const_iterator value = field->enumValues.begin();
563 value != field->enumValues.end(); value++) {
564 fprintf(out, "const int32_t %s__%s__%s = %d;\n",
565 make_constant_name((*atomIt)->message).c_str(),
566 make_constant_name(field->name).c_str(),
567 make_constant_name(value->second).c_str(), value->first);
568 }
569 fprintf(out, "\n");
570 }
571 }
572 }
573 }
574
write_native_method_signature(FILE * out,const string & signaturePrefix,const vector<java_type_t> & signature,const AtomDecl & attributionDecl,const string & closer,bool isVendorAtomLogging)575 void write_native_method_signature(FILE* out, const string& signaturePrefix,
576 const vector<java_type_t>& signature,
577 const AtomDecl& attributionDecl, const string& closer,
578 bool isVendorAtomLogging) {
579 fprintf(out, "%sint32_t code", signaturePrefix.c_str());
580 int argIndex = 1;
581 for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
582 arg++) {
583 if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
584 for (const auto& chainField : attributionDecl.fields) {
585 if (chainField.javaType == JAVA_TYPE_STRING) {
586 fprintf(out, ", const std::vector<%s>& %s",
587 cpp_type_name(chainField.javaType, isVendorAtomLogging),
588 chainField.name.c_str());
589 } else {
590 fprintf(out, ", const %s* %s, size_t %s_length",
591 cpp_type_name(chainField.javaType, isVendorAtomLogging),
592 chainField.name.c_str(), chainField.name.c_str());
593 }
594 }
595 } else {
596 fprintf(out, ", %s arg%d", cpp_type_name(*arg, isVendorAtomLogging), argIndex);
597
598 if (*arg == JAVA_TYPE_BOOLEAN_ARRAY && !isVendorAtomLogging) {
599 fprintf(out, ", size_t arg%d_length", argIndex);
600 }
601 }
602 argIndex++;
603 }
604 fprintf(out, ")%s\n", closer.c_str());
605 }
606
write_native_method_header(FILE * out,const string & methodName,const SignatureInfoMap & signatureInfoMap,const AtomDecl & attributionDecl,bool isVendorAtomLogging)607 void write_native_method_header(FILE* out, const string& methodName,
608 const SignatureInfoMap& signatureInfoMap,
609 const AtomDecl& attributionDecl,
610 bool isVendorAtomLogging) {
611 for (const auto& [signature, _] : signatureInfoMap) {
612 string closer = contains_repeated_field(signature) ?
613 "\n__INTRODUCED_IN(__ANDROID_API_T__);" : ";";
614 write_native_method_signature(out, methodName, signature, attributionDecl, closer,
615 isVendorAtomLogging);
616 }
617 }
618
write_native_header_preamble(FILE * out,const string & cppNamespace,bool includePull,bool includeHistogram,bool bootstrap,bool isVendorAtomLogging)619 void write_native_header_preamble(FILE* out, const string& cppNamespace, bool includePull,
620 bool includeHistogram, bool bootstrap, bool isVendorAtomLogging) {
621 // Print prelude
622 fprintf(out, "// This file is autogenerated\n");
623 fprintf(out, "\n");
624 fprintf(out, "#pragma once\n");
625 fprintf(out, "\n");
626 fprintf(out, "#include <stdint.h>\n");
627 fprintf(out, "#include <vector>\n");
628 fprintf(out, "#include <map>\n");
629 fprintf(out, "#include <set>\n");
630 fprintf(out, "#include <memory>\n");
631 if (includePull) {
632 fprintf(out, "#include <stats_pull_atom_callback.h>\n");
633 }
634
635 #ifdef CC_INCLUDE_HDRS_DIR
636 const vector<string> excludeList =
637 includeHistogram ? vector<string>{} : vector<string>{HISTOGRAM_STEM};
638 write_srcs_header(out, CC_INCLUDE_HDRS_DIR, excludeList);
639 #else
640 (void)includeHistogram; // suppress unused parameter error
641 #endif
642
643 if (isVendorAtomLogging) {
644 fprintf(out, "#include <aidl/android/frameworks/stats/VendorAtom.h>\n");
645 }
646 if (!bootstrap && !isVendorAtomLogging) {
647 fprintf(out, "#include <stddef.h>\n");
648 fprintf(out, "\n");
649 fprintf(out, "#ifndef __ANDROID_API_T__\n");
650 fprintf(out, "#define __ANDROID_API_T__ 33\n");
651 fprintf(out, "#endif\n");
652 fprintf(out, "#ifndef __INTRODUCED_IN\n");
653 fprintf(out, "#define __INTRODUCED_IN(api_level)\n");
654 fprintf(out, "#endif\n");
655 }
656 fprintf(out, "\n");
657
658 write_namespace(out, cppNamespace);
659 fprintf(out, "\n");
660 fprintf(out, "/*\n");
661 fprintf(out, " * API For logging statistics events.\n");
662 fprintf(out, " */\n");
663 fprintf(out, "\n");
664 }
665
write_native_header_epilogue(FILE * out,const string & cppNamespace)666 void write_native_header_epilogue(FILE* out, const string& cppNamespace) {
667 write_closing_namespace(out, cppNamespace);
668 }
669
670 // Java
write_java_atom_codes(FILE * out,const Atoms & atoms)671 void write_java_atom_codes(FILE* out, const Atoms& atoms) {
672 fprintf(out, " // Constants for atom codes.\n");
673
674 std::map<int, AtomDeclSet::const_iterator> atom_code_to_non_chained_decl_map;
675 build_non_chained_decl_map(atoms, &atom_code_to_non_chained_decl_map);
676
677 // Print constants for the atom codes.
678 for (AtomDeclSet::const_iterator atomIt = atoms.decls.begin(); atomIt != atoms.decls.end();
679 atomIt++) {
680 const string constant = make_constant_name((*atomIt)->name);
681 fprintf(out, "\n");
682 fprintf(out, " /**\n");
683 fprintf(out, " * %s %s<br>\n", (*atomIt)->message.c_str(), (*atomIt)->name.c_str());
684 write_java_usage(out, "write", constant, **atomIt);
685 auto non_chained_decl = atom_code_to_non_chained_decl_map.find((*atomIt)->code);
686 if (non_chained_decl != atom_code_to_non_chained_decl_map.end()) {
687 write_java_usage(out, "write_non_chained", constant, **(non_chained_decl->second));
688 }
689 fprintf(out, " */\n");
690 fprintf(out, " public static final int %s = %d;\n", constant.c_str(), (*atomIt)->code);
691 }
692 fprintf(out, "\n");
693 }
694
write_java_enum_values(FILE * out,const Atoms & atoms)695 void write_java_enum_values(FILE* out, const Atoms& atoms) {
696 fprintf(out, " // Constants for enum values.\n\n");
697 for (AtomDeclSet::const_iterator atomIt = atoms.decls.begin(); atomIt != atoms.decls.end();
698 atomIt++) {
699 for (vector<AtomField>::const_iterator field = (*atomIt)->fields.begin();
700 field != (*atomIt)->fields.end(); field++) {
701 if (field->javaType == JAVA_TYPE_ENUM || field->javaType == JAVA_TYPE_ENUM_ARRAY) {
702 fprintf(out, " // Values for %s.%s\n", (*atomIt)->message.c_str(),
703 field->name.c_str());
704 for (map<int, string>::const_iterator value = field->enumValues.begin();
705 value != field->enumValues.end(); value++) {
706 fprintf(out, " public static final int %s__%s__%s = %d;\n",
707 make_constant_name((*atomIt)->message).c_str(),
708 make_constant_name(field->name).c_str(),
709 make_constant_name(value->second).c_str(), value->first);
710 }
711 fprintf(out, "\n");
712 }
713 }
714 }
715 }
716
write_java_method_signature(FILE * out,const vector<java_type_t> & signature,const AtomDecl & attributionDecl)717 int write_java_method_signature(FILE* out, const vector<java_type_t>& signature,
718 const AtomDecl& attributionDecl) {
719 int argIndex = 1;
720 for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
721 arg++) {
722 if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
723 if (attributionDecl.fields.empty()) {
724 fprintf(stderr, "Encountered incompatible attribution chain atom definition");
725 return 1;
726 }
727 for (const auto& chainField : attributionDecl.fields) {
728 fprintf(out, ", %s[] %s", java_type_name(chainField.javaType),
729 chainField.name.c_str());
730 }
731 } else {
732 fprintf(out, ", %s arg%d", java_type_name(*arg), argIndex);
733 }
734 argIndex++;
735 }
736 return 0;
737 }
738
write_java_usage(FILE * out,const string & method_name,const string & atom_code_name,const AtomDecl & atom)739 void write_java_usage(FILE* out, const string& method_name, const string& atom_code_name,
740 const AtomDecl& atom) {
741 fprintf(out, " * Usage: StatsLog.%s(StatsLog.%s", method_name.c_str(),
742 atom_code_name.c_str());
743 for (vector<AtomField>::const_iterator field = atom.fields.begin(); field != atom.fields.end();
744 field++) {
745 if (field->javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
746 fprintf(out, ", android.os.WorkSource workSource");
747 } else if (field->javaType == JAVA_TYPE_BYTE_ARRAY) {
748 fprintf(out, ", byte[] %s", field->name.c_str());
749 } else {
750 fprintf(out, ", %s %s", java_type_name(field->javaType), field->name.c_str());
751 }
752 }
753 fprintf(out, ");<br>\n");
754 }
755
write_java_non_chained_methods(FILE * out,const SignatureInfoMap & signatureInfoMap,const bool staticMethods)756 int write_java_non_chained_methods(FILE* out, const SignatureInfoMap& signatureInfoMap,
757 const bool staticMethods) {
758 const char* methodPrefix = staticMethods ? "static " : "";
759 for (auto signatureInfoMapIt = signatureInfoMap.begin();
760 signatureInfoMapIt != signatureInfoMap.end(); signatureInfoMapIt++) {
761 // Print method signature.
762 fprintf(out, " public %svoid write_non_chained(int code", methodPrefix);
763 vector<java_type_t> signature = signatureInfoMapIt->first;
764 int argIndex = 1;
765 for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
766 arg++) {
767 if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
768 fprintf(stderr, "Non chained signatures should not have attribution chains.\n");
769 return 1;
770 } else {
771 fprintf(out, ", %s arg%d", java_type_name(*arg), argIndex);
772 }
773 argIndex++;
774 }
775 fprintf(out, ") {\n");
776
777 fprintf(out, " write(code");
778 argIndex = 1;
779 for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
780 arg++) {
781 // First two args are uid and tag of attribution chain.
782 if (argIndex == 1) {
783 fprintf(out, ", new int[] {arg%d}", argIndex);
784 } else if (argIndex == 2) {
785 fprintf(out, ", new java.lang.String[] {arg%d}", argIndex);
786 } else {
787 fprintf(out, ", arg%d", argIndex);
788 }
789 argIndex++;
790 }
791 fprintf(out, ");\n");
792 fprintf(out, " }\n");
793 fprintf(out, "\n");
794 }
795 return 0;
796 }
797
write_java_work_source_methods(FILE * out,const SignatureInfoMap & signatureInfoMap)798 int write_java_work_source_methods(FILE* out, const SignatureInfoMap& signatureInfoMap) {
799 fprintf(out, " // WorkSource methods.\n");
800 for (auto signatureInfoMapIt = signatureInfoMap.begin();
801 signatureInfoMapIt != signatureInfoMap.end(); signatureInfoMapIt++) {
802 vector<java_type_t> signature = signatureInfoMapIt->first;
803 // Determine if there is Attribution in this signature.
804 int attributionArg = -1;
805 int argIndexMax = 0;
806 for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
807 arg++) {
808 argIndexMax++;
809 if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
810 if (attributionArg > -1) {
811 fprintf(stderr, "An atom contains multiple AttributionNode fields.\n");
812 fprintf(stderr, "This is not supported. Aborting WorkSource method writing.\n");
813 fprintf(out,
814 "\n// Invalid for WorkSource: more than one attribution "
815 "chain.\n");
816 return 1;
817 }
818 attributionArg = argIndexMax;
819 }
820 }
821 if (attributionArg < 0) {
822 continue;
823 }
824
825 fprintf(out, "\n");
826 // Method header (signature)
827 fprintf(out, " public static void write(int code");
828 int argIndex = 1;
829 for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
830 arg++) {
831 if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
832 fprintf(out, ", android.os.WorkSource ws");
833 } else {
834 fprintf(out, ", %s arg%d", java_type_name(*arg), argIndex);
835 }
836 argIndex++;
837 }
838 fprintf(out, ") {\n");
839
840 // write_non_chained() component. TODO: Remove when flat uids are no longer
841 // needed.
842 fprintf(out, " for (int i = 0; i < ws.size(); ++i) {\n");
843 fprintf(out, " write_non_chained(code");
844 for (int argIndex = 1; argIndex <= argIndexMax; argIndex++) {
845 if (argIndex == attributionArg) {
846 fprintf(out, ", ws.getUid(i), ws.getPackageName(i)");
847 } else {
848 fprintf(out, ", arg%d", argIndex);
849 }
850 }
851 fprintf(out, ");\n");
852 fprintf(out, " }\n"); // close for-loop
853
854 // write() component.
855 fprintf(out,
856 " java.util.List<android.os.WorkSource.WorkChain> workChains = "
857 "ws.getWorkChains();\n");
858 fprintf(out, " if (workChains != null) {\n");
859 fprintf(out,
860 " for (android.os.WorkSource.WorkChain wc : workChains) "
861 "{\n");
862 fprintf(out, " write(code");
863 for (int argIndex = 1; argIndex <= argIndexMax; argIndex++) {
864 if (argIndex == attributionArg) {
865 fprintf(out, ", wc.getUids(), wc.getTags()");
866 } else {
867 fprintf(out, ", arg%d", argIndex);
868 }
869 }
870 fprintf(out, ");\n");
871 fprintf(out, " }\n"); // close for-loop
872 fprintf(out, " }\n"); // close if
873 fprintf(out, " }\n"); // close method
874 }
875 return 0;
876 }
877
contains_restricted(const AtomDeclSet & atomDeclSet)878 static bool contains_restricted(const AtomDeclSet& atomDeclSet) {
879 for (const auto& decl : atomDeclSet) {
880 if (decl->restricted) {
881 return true;
882 }
883 }
884 return false;
885 }
886
get_max_requires_api_level(int minApiLevel,const AtomDeclSet * atomDeclSet,const vector<java_type_t> & signature)887 int get_max_requires_api_level(int minApiLevel, const AtomDeclSet* atomDeclSet,
888 const vector<java_type_t>& signature) {
889 if (atomDeclSet != nullptr && contains_restricted(*atomDeclSet)) {
890 return API_U;
891 }
892 if (contains_repeated_field(signature)) {
893 return API_T;
894 }
895 if (minApiLevel <= API_Q) {
896 return API_Q; // for StatsLog.writeRaw()
897 }
898 return 0;
899 }
900
get_annotations(int argIndex,const FieldNumberToAtomDeclSet & fieldNumberToAtomDeclSet)901 AtomDeclSet get_annotations(int argIndex,
902 const FieldNumberToAtomDeclSet& fieldNumberToAtomDeclSet) {
903 const FieldNumberToAtomDeclSet::const_iterator fieldNumberToAtomDeclSetIt =
904 fieldNumberToAtomDeclSet.find(argIndex);
905 if (fieldNumberToAtomDeclSet.end() == fieldNumberToAtomDeclSetIt) {
906 return AtomDeclSet();
907 }
908 return fieldNumberToAtomDeclSetIt->second;
909 }
910
has_histograms(const AtomDeclSet & decls)911 bool has_histograms(const AtomDeclSet& decls) {
912 return std::find_if_not(decls.begin(), decls.end(), [](shared_ptr<AtomDecl> decl) {
913 return decl->fieldNameToHistBinOption.empty();
914 }) != decls.end();
915 }
916
write_native_histogram_helper_declarations(FILE * out,const AtomDeclSet & atomDeclSet)917 void write_native_histogram_helper_declarations(FILE* out, const AtomDeclSet& atomDeclSet) {
918 for (const shared_ptr<AtomDecl>& atomDecl : atomDeclSet) {
919 for (const auto& [fieldName, histBinOption] : atomDecl->fieldNameToHistBinOption) {
920 write_native_histogram_helper_signature(out, atomDecl->name, fieldName);
921 fprintf(out, ";\n");
922 }
923 }
924 fprintf(out, "\n");
925 }
926
write_native_histogram_helper_definitions(FILE * out,const AtomDeclSet & atomDeclSet)927 int write_native_histogram_helper_definitions(FILE* out, const AtomDeclSet& atomDeclSet) {
928 int errors = 0;
929 for (const shared_ptr<AtomDecl>& atomDecl : atomDeclSet) {
930 for (const auto& [fieldName, histBinOption] : atomDecl->fieldNameToHistBinOption) {
931 errors += write_native_histogram_helper_definition(out, atomDecl->name, fieldName,
932 histBinOption);
933 }
934 }
935 return errors;
936 }
937
write_srcs_header(FILE * out,const char * path,const vector<string> & excludeList)938 int write_srcs_header(FILE* out, const char* path, const vector<string>& excludeList) {
939 int errors = 0;
940 const string fullPath = get_data_dir_path(path);
941 for (const fs::path& filePath : fs::directory_iterator(fullPath)) {
942 // Add headers from filePath if it's not in excludeList.
943 if (std::find(excludeList.begin(), excludeList.end(), filePath.stem()) ==
944 excludeList.end()) {
945 errors += write_src_header(out, filePath);
946 }
947 }
948
949 return errors;
950 }
951
write_java_srcs_classes(FILE * out,const char * path,const vector<string> & excludeList)952 int write_java_srcs_classes(FILE* out, const char* path, const vector<string>& excludeList) {
953 return write_srcs_bodies(out, path, 4 /* indent */, excludeList, make_java_class_static);
954 }
955
write_cc_srcs_classes(FILE * out,const char * path,const vector<string> & excludeList)956 int write_cc_srcs_classes(FILE* out, const char* path, const vector<string>& excludeList) {
957 return write_srcs_bodies(out, path, 0 /* indent */, excludeList, nullptr /* nameTransformer */);
958 }
959
write_java_histogram_helpers(FILE * out,const AtomDeclSet & atomDeclSet,const bool staticMethods)960 int write_java_histogram_helpers(FILE* out, const AtomDeclSet& atomDeclSet,
961 const bool staticMethods) {
962 int errors = 0;
963 for (const shared_ptr<AtomDecl>& atomDecl : atomDeclSet) {
964 for (const auto& [fieldName, histBinOption] : atomDecl->fieldNameToHistBinOption) {
965 errors += write_java_histogram_helper(out, atomDecl->name, fieldName, histBinOption,
966 staticMethods);
967 }
968 }
969 return errors;
970 }
971
972 } // namespace stats_log_api_gen
973 } // namespace android
974