• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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