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 <map>
22 #include <string>
23 #include <utility>
24 #include <vector>
25 
26 #include "Collation.h"
27 #include "frameworks/proto_logging/stats/atom_field_options.pb.h"
28 
29 namespace android {
30 namespace stats_log_api_gen {
31 
32 using std::map;
33 using std::string;
34 using std::vector;
35 
36 /**
37  * Inlining this method because "android-base/strings.h" is not available on
38  * google3.
39  */
Split(const string & s,const string & delimiters)40 static vector<string> Split(const string& s, const string& delimiters) {
41     vector<string> result;
42 
43     size_t base = 0;
44     size_t found;
45     while (true) {
46         found = s.find_first_of(delimiters, base);
47         result.push_back(s.substr(base, found - base));
48         if (found == s.npos) break;
49         base = found + 1;
50     }
51 
52     return result;
53 }
54 
build_non_chained_decl_map(const Atoms & atoms,std::map<int,AtomDeclSet::const_iterator> * decl_map)55 void build_non_chained_decl_map(const Atoms& atoms,
56                                 std::map<int, AtomDeclSet::const_iterator>* decl_map) {
57     for (AtomDeclSet::const_iterator atomIt = atoms.non_chained_decls.begin();
58          atomIt != atoms.non_chained_decls.end(); atomIt++) {
59         decl_map->insert(std::make_pair((*atomIt)->code, atomIt));
60     }
61 }
62 
get_annotation_id_constants(const string & prefix)63 const map<AnnotationId, AnnotationStruct>& get_annotation_id_constants(const string& prefix) {
64     static const map<AnnotationId, AnnotationStruct>* ANNOTATION_ID_CONSTANTS =
65             new map<AnnotationId, AnnotationStruct>{
66                     {ANNOTATION_ID_IS_UID, AnnotationStruct(prefix + "IS_UID", API_S)},
67                     {ANNOTATION_ID_TRUNCATE_TIMESTAMP,
68                      AnnotationStruct(prefix + "TRUNCATE_TIMESTAMP", API_S)},
69                     {ANNOTATION_ID_PRIMARY_FIELD,
70                      AnnotationStruct(prefix + "PRIMARY_FIELD", API_S)},
71                     {ANNOTATION_ID_EXCLUSIVE_STATE,
72                      AnnotationStruct(prefix + "EXCLUSIVE_STATE", API_S)},
73                     {ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID,
74                      AnnotationStruct(prefix + "PRIMARY_FIELD_FIRST_UID", API_S)},
75                     {ANNOTATION_ID_DEFAULT_STATE,
76                      AnnotationStruct(prefix + "DEFAULT_STATE", API_S)},
77                     {ANNOTATION_ID_TRIGGER_STATE_RESET,
78                      AnnotationStruct(prefix + "TRIGGER_STATE_RESET", API_S)},
79                     {ANNOTATION_ID_STATE_NESTED, AnnotationStruct(prefix + "STATE_NESTED", API_S)},
80                     {ANNOTATION_ID_RESTRICTION_CATEGORY,
81                      AnnotationStruct(prefix + "RESTRICTION_CATEGORY", API_U)},
82                     {ANNOTATION_ID_FIELD_RESTRICTION_PERIPHERAL_DEVICE_INFO,
83                      AnnotationStruct(prefix + "FIELD_RESTRICTION_PERIPHERAL_DEVICE_INFO", API_U)},
84                     {ANNOTATION_ID_FIELD_RESTRICTION_APP_USAGE,
85                      AnnotationStruct(prefix + "FIELD_RESTRICTION_APP_USAGE", API_U)},
86                     {ANNOTATION_ID_FIELD_RESTRICTION_APP_ACTIVITY,
87                      AnnotationStruct(prefix + "FIELD_RESTRICTION_APP_ACTIVITY", API_U)},
88                     {ANNOTATION_ID_FIELD_RESTRICTION_HEALTH_CONNECT,
89                      AnnotationStruct(prefix + "FIELD_RESTRICTION_HEALTH_CONNECT", API_U)},
90                     {ANNOTATION_ID_FIELD_RESTRICTION_ACCESSIBILITY,
91                      AnnotationStruct(prefix + "FIELD_RESTRICTION_ACCESSIBILITY", API_U)},
92                     {ANNOTATION_ID_FIELD_RESTRICTION_SYSTEM_SEARCH,
93                      AnnotationStruct(prefix + "FIELD_RESTRICTION_SYSTEM_SEARCH", API_U)},
94                     {ANNOTATION_ID_FIELD_RESTRICTION_USER_ENGAGEMENT,
95                      AnnotationStruct(prefix + "FIELD_RESTRICTION_USER_ENGAGEMENT", API_U)},
96                     {ANNOTATION_ID_FIELD_RESTRICTION_AMBIENT_SENSING,
97                      AnnotationStruct(prefix + "FIELD_RESTRICTION_AMBIENT_SENSING", API_U)},
98                     {ANNOTATION_ID_FIELD_RESTRICTION_DEMOGRAPHIC_CLASSIFICATION,
99                      AnnotationStruct(prefix + "FIELD_RESTRICTION_DEMOGRAPHIC_CLASSIFICATION",
100                                       API_U)},
101             };
102 
103     return *ANNOTATION_ID_CONSTANTS;
104 }
105 
get_java_build_version_code(int apiLevel)106 string get_java_build_version_code(int apiLevel) {
107     switch (apiLevel) {
108         case API_Q:
109             return "Build.VERSION_CODES.Q";
110         case API_R:
111             return "Build.VERSION_CODES.R";
112         case API_S:
113             return "Build.VERSION_CODES.S";
114         case API_S_V2:
115             return "Build.VERSION_CODES.S_V2";
116         case API_T:
117             return "Build.VERSION_CODES.TIRAMISU";
118         case API_U:
119             return "Build.VERSION_CODES.UPSIDE_DOWN_CAKE";
120         default:
121             return "Build.VERSION_CODES.CUR_DEVELOPMENT";
122     }
123 }
124 
get_restriction_category_str(int annotationValue)125 string get_restriction_category_str(int annotationValue) {
126     switch (annotationValue) {
127         case os::statsd::RestrictionCategory::RESTRICTION_DIAGNOSTIC:
128             return "RESTRICTION_CATEGORY_DIAGNOSTIC";
129         case os::statsd::RestrictionCategory::RESTRICTION_SYSTEM_INTELLIGENCE:
130             return "RESTRICTION_CATEGORY_SYSTEM_INTELLIGENCE";
131         case os::statsd::RestrictionCategory::RESTRICTION_AUTHENTICATION:
132             return "RESTRICTION_CATEGORY_AUTHENTICATION";
133         case os::statsd::RestrictionCategory::RESTRICTION_FRAUD_AND_ABUSE:
134             return "RESTRICTION_CATEGORY_FRAUD_AND_ABUSE";
135         default:
136             return "";
137     }
138 }
139 
140 /**
141  * Turn lower and camel case into upper case with underscores.
142  */
make_constant_name(const string & str)143 string make_constant_name(const string& str) {
144     string result;
145     const int N = str.size();
146     bool underscore_next = false;
147     for (int i = 0; i < N; i++) {
148         char c = str[i];
149         if (c >= 'A' && c <= 'Z') {
150             if (underscore_next) {
151                 result += '_';
152                 underscore_next = false;
153             }
154         } else if (c >= 'a' && c <= 'z') {
155             c = 'A' + c - 'a';
156             underscore_next = true;
157         } else if (c == '_') {
158             underscore_next = false;
159         }
160         result += c;
161     }
162     return result;
163 }
164 
cpp_type_name(java_type_t type,bool isVendorAtomLogging)165 const char* cpp_type_name(java_type_t type, bool isVendorAtomLogging) {
166     switch (type) {
167         case JAVA_TYPE_BOOLEAN:
168             return "bool";
169         case JAVA_TYPE_INT:  // Fallthrough.
170         case JAVA_TYPE_ENUM:
171             return "int32_t";
172         case JAVA_TYPE_LONG:
173             return "int64_t";
174         case JAVA_TYPE_FLOAT:
175             return "float";
176         case JAVA_TYPE_DOUBLE:
177             return "double";
178         case JAVA_TYPE_STRING:
179             return "char const*";
180         case JAVA_TYPE_BYTE_ARRAY:
181             return isVendorAtomLogging ? "const std::vector<uint8_t>&" : "const BytesField&";
182         case JAVA_TYPE_BOOLEAN_ARRAY:
183             return isVendorAtomLogging ? "const std::vector<bool>&" : "const bool*";
184         case JAVA_TYPE_INT_ARRAY:  // Fallthrough.
185         case JAVA_TYPE_ENUM_ARRAY:
186             return "const std::vector<int32_t>&";
187         case JAVA_TYPE_LONG_ARRAY:
188             return "const std::vector<int64_t>&";
189         case JAVA_TYPE_FLOAT_ARRAY:
190             return "const std::vector<float>&";
191         case JAVA_TYPE_STRING_ARRAY:
192             return "const std::vector<char const*>&";
193         case JAVA_TYPE_DOUBLE_ARRAY:
194             return "const std::vector<double>&";
195         default:
196             return "UNKNOWN";
197     }
198 }
199 
java_type_name(java_type_t type)200 const char* java_type_name(java_type_t type) {
201     switch (type) {
202         case JAVA_TYPE_BOOLEAN:
203             return "boolean";
204         case JAVA_TYPE_INT:  // Fallthrough.
205         case JAVA_TYPE_ENUM:
206             return "int";
207         case JAVA_TYPE_LONG:
208             return "long";
209         case JAVA_TYPE_FLOAT:
210             return "float";
211         case JAVA_TYPE_DOUBLE:
212             return "double";
213         case JAVA_TYPE_STRING:
214             return "java.lang.String";
215         case JAVA_TYPE_BYTE_ARRAY:
216             return "byte[]";
217         case JAVA_TYPE_BOOLEAN_ARRAY:
218             return "boolean[]";
219         case JAVA_TYPE_INT_ARRAY:  // Fallthrough.
220         case JAVA_TYPE_ENUM_ARRAY:
221             return "int[]";
222         case JAVA_TYPE_LONG_ARRAY:
223             return "long[]";
224         case JAVA_TYPE_FLOAT_ARRAY:
225             return "float[]";
226         case JAVA_TYPE_STRING_ARRAY:
227             return "java.lang.String[]";
228         case JAVA_TYPE_DOUBLE_ARRAY:
229             return "double[]";
230         default:
231             return "UNKNOWN";
232     }
233 }
234 
235 // Does not include AttributionChain type.
is_repeated_field(java_type_t type)236 bool is_repeated_field(java_type_t type) {
237     switch (type) {
238         case JAVA_TYPE_BOOLEAN_ARRAY:
239         case JAVA_TYPE_INT_ARRAY:
240         case JAVA_TYPE_FLOAT_ARRAY:
241         case JAVA_TYPE_LONG_ARRAY:
242         case JAVA_TYPE_STRING_ARRAY:
243         case JAVA_TYPE_ENUM_ARRAY:
244             return true;
245         default:
246             return false;
247     }
248 }
249 
is_primitive_field(java_type_t type)250 bool is_primitive_field(java_type_t type) {
251     switch (type) {
252         case JAVA_TYPE_BOOLEAN:
253         case JAVA_TYPE_INT:
254         case JAVA_TYPE_LONG:
255         case JAVA_TYPE_FLOAT:
256         case JAVA_TYPE_STRING:
257         case JAVA_TYPE_ENUM:
258             return true;
259         default:
260             return false;
261     }
262 }
263 
264 // Native
265 // Writes namespaces for the cpp and header files
write_namespace(FILE * out,const string & cppNamespaces)266 void write_namespace(FILE* out, const string& cppNamespaces) {
267     const vector<string> cppNamespaceVec = Split(cppNamespaces, ",");
268     for (const string& cppNamespace : cppNamespaceVec) {
269         fprintf(out, "namespace %s {\n", cppNamespace.c_str());
270     }
271 }
272 
273 // Writes namespace closing brackets for cpp and header files.
write_closing_namespace(FILE * out,const string & cppNamespaces)274 void write_closing_namespace(FILE* out, const string& cppNamespaces) {
275     vector<string> cppNamespaceVec = Split(cppNamespaces, ",");
276     for (auto it = cppNamespaceVec.rbegin(); it != cppNamespaceVec.rend(); ++it) {
277         fprintf(out, "} // namespace %s\n", it->c_str());
278     }
279 }
280 
write_cpp_usage(FILE * out,const string & method_name,const string & atom_code_name,const AtomDecl & atom,const AtomDecl & attributionDecl,bool isVendorAtomLogging=false)281 static void write_cpp_usage(FILE* out, const string& method_name, const string& atom_code_name,
282                             const AtomDecl& atom, const AtomDecl& attributionDecl,
283                             bool isVendorAtomLogging = false) {
284     const char* delimiterStr = method_name.find('(') == string::npos ? "(" : " ";
285     fprintf(out, "     * Usage: %s%s%s", method_name.c_str(), delimiterStr, atom_code_name.c_str());
286 
287     for (vector<AtomField>::const_iterator field = atom.fields.begin(); field != atom.fields.end();
288          field++) {
289         if (field->javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
290             for (const auto& chainField : attributionDecl.fields) {
291                 if (chainField.javaType == JAVA_TYPE_STRING) {
292                     fprintf(out, ", const std::vector<%s>& %s", cpp_type_name(chainField.javaType),
293                             chainField.name.c_str());
294                 } else {
295                     fprintf(out, ", const %s* %s, size_t %s_length",
296                             cpp_type_name(chainField.javaType), chainField.name.c_str(),
297                             chainField.name.c_str());
298                 }
299             }
300         } else {
301             fprintf(out, ", %s %s", cpp_type_name(field->javaType, isVendorAtomLogging),
302                     field->name.c_str());
303         }
304     }
305     fprintf(out, ");\n");
306 }
307 
write_native_atom_constants(FILE * out,const Atoms & atoms,const AtomDecl & attributionDecl,const string & methodName,bool isVendorAtomLogging)308 void write_native_atom_constants(FILE* out, const Atoms& atoms, const AtomDecl& attributionDecl,
309                                  const string& methodName, bool isVendorAtomLogging) {
310     fprintf(out, "/**\n");
311     fprintf(out, " * Constants for atom codes.\n");
312     fprintf(out, " */\n");
313     fprintf(out, "enum {\n");
314 
315     std::map<int, AtomDeclSet::const_iterator> atom_code_to_non_chained_decl_map;
316     build_non_chained_decl_map(atoms, &atom_code_to_non_chained_decl_map);
317 
318     size_t i = 0;
319     // Print atom constants
320     for (AtomDeclSet::const_iterator atomIt = atoms.decls.begin(); atomIt != atoms.decls.end();
321          atomIt++) {
322         const string constant = make_constant_name((*atomIt)->name);
323         fprintf(out, "\n");
324         fprintf(out, "    /**\n");
325         fprintf(out, "     * %s %s\n", (*atomIt)->message.c_str(), (*atomIt)->name.c_str());
326         write_cpp_usage(out, methodName, constant, **atomIt, attributionDecl, isVendorAtomLogging);
327 
328         auto non_chained_decl = atom_code_to_non_chained_decl_map.find((*atomIt)->code);
329         if (non_chained_decl != atom_code_to_non_chained_decl_map.end()) {
330             write_cpp_usage(out, methodName + "_non_chained", constant, **non_chained_decl->second,
331                             attributionDecl, isVendorAtomLogging);
332         }
333         fprintf(out, "     */\n");
334         char const* const comma = (i == atoms.decls.size() - 1) ? "" : ",";
335         fprintf(out, "    %s = %d%s\n", constant.c_str(), (*atomIt)->code, comma);
336         i++;
337     }
338     fprintf(out, "\n");
339     fprintf(out, "};\n");
340     fprintf(out, "\n");
341 }
342 
write_native_atom_enums(FILE * out,const Atoms & atoms)343 void write_native_atom_enums(FILE* out, const Atoms& atoms) {
344     // Print constants for the enum values.
345     fprintf(out, "//\n");
346     fprintf(out, "// Constants for enum values\n");
347     fprintf(out, "//\n\n");
348     for (AtomDeclSet::const_iterator atomIt = atoms.decls.begin(); atomIt != atoms.decls.end();
349          atomIt++) {
350         for (vector<AtomField>::const_iterator field = (*atomIt)->fields.begin();
351              field != (*atomIt)->fields.end(); field++) {
352             if (field->javaType == JAVA_TYPE_ENUM || field->javaType == JAVA_TYPE_ENUM_ARRAY) {
353                 fprintf(out, "// Values for %s.%s\n", (*atomIt)->message.c_str(),
354                         field->name.c_str());
355                 for (map<int, string>::const_iterator value = field->enumValues.begin();
356                      value != field->enumValues.end(); value++) {
357                     fprintf(out, "const int32_t %s__%s__%s = %d;\n",
358                             make_constant_name((*atomIt)->message).c_str(),
359                             make_constant_name(field->name).c_str(),
360                             make_constant_name(value->second).c_str(), value->first);
361                 }
362                 fprintf(out, "\n");
363             }
364         }
365     }
366 }
367 
write_native_method_signature(FILE * out,const string & signaturePrefix,const vector<java_type_t> & signature,const AtomDecl & attributionDecl,const string & closer,bool isVendorAtomLogging)368 void write_native_method_signature(FILE* out, const string& signaturePrefix,
369                                           const vector<java_type_t>& signature,
370                                           const AtomDecl& attributionDecl, const string& closer,
371                                           bool isVendorAtomLogging) {
372     fprintf(out, "%sint32_t code", signaturePrefix.c_str());
373     int argIndex = 1;
374     for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
375          arg++) {
376         if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
377             for (const auto& chainField : attributionDecl.fields) {
378                 if (chainField.javaType == JAVA_TYPE_STRING) {
379                     fprintf(out, ", const std::vector<%s>& %s",
380                             cpp_type_name(chainField.javaType, isVendorAtomLogging),
381                             chainField.name.c_str());
382                 } else {
383                     fprintf(out, ", const %s* %s, size_t %s_length",
384                             cpp_type_name(chainField.javaType, isVendorAtomLogging),
385                             chainField.name.c_str(), chainField.name.c_str());
386                 }
387             }
388         } else {
389             fprintf(out, ", %s arg%d", cpp_type_name(*arg, isVendorAtomLogging), argIndex);
390 
391             if (*arg == JAVA_TYPE_BOOLEAN_ARRAY && !isVendorAtomLogging) {
392                 fprintf(out, ", size_t arg%d_length", argIndex);
393             }
394         }
395         argIndex++;
396     }
397     fprintf(out, ")%s\n", closer.c_str());
398 }
399 
write_native_method_header(FILE * out,const string & methodName,const SignatureInfoMap & signatureInfoMap,const AtomDecl & attributionDecl,bool isVendorAtomLogging)400 void write_native_method_header(FILE* out, const string& methodName,
401                                        const SignatureInfoMap& signatureInfoMap,
402                                        const AtomDecl& attributionDecl,
403                                        bool isVendorAtomLogging) {
404     for (const auto& [signature, _] : signatureInfoMap) {
405         write_native_method_signature(out, methodName, signature, attributionDecl, ";",
406                                       isVendorAtomLogging);
407     }
408 }
409 
write_native_header_preamble(FILE * out,const string & cppNamespace,bool includePull,bool isVendorAtomLogging)410 void write_native_header_preamble(FILE* out, const string& cppNamespace, bool includePull,
411                                      bool isVendorAtomLogging) {
412     // Print prelude
413     fprintf(out, "// This file is autogenerated\n");
414     fprintf(out, "\n");
415     fprintf(out, "#pragma once\n");
416     fprintf(out, "\n");
417     fprintf(out, "#include <stdint.h>\n");
418     fprintf(out, "#include <vector>\n");
419     fprintf(out, "#include <map>\n");
420     fprintf(out, "#include <set>\n");
421     if (includePull) {
422         fprintf(out, "#include <stats_pull_atom_callback.h>\n");
423     }
424 
425     if (isVendorAtomLogging) {
426         fprintf(out, "#include <aidl/android/frameworks/stats/VendorAtom.h>\n");
427     }
428 
429     fprintf(out, "\n");
430 
431     write_namespace(out, cppNamespace);
432     fprintf(out, "\n");
433     fprintf(out, "/*\n");
434     fprintf(out, " * API For logging statistics events.\n");
435     fprintf(out, " */\n");
436     fprintf(out, "\n");
437 }
438 
write_native_header_epilogue(FILE * out,const string & cppNamespace)439 void write_native_header_epilogue(FILE* out, const string& cppNamespace) {
440     write_closing_namespace(out, cppNamespace);
441 }
442 
443 // Java
write_java_atom_codes(FILE * out,const Atoms & atoms)444 void write_java_atom_codes(FILE* out, const Atoms& atoms) {
445     fprintf(out, "    // Constants for atom codes.\n");
446 
447     std::map<int, AtomDeclSet::const_iterator> atom_code_to_non_chained_decl_map;
448     build_non_chained_decl_map(atoms, &atom_code_to_non_chained_decl_map);
449 
450     // Print constants for the atom codes.
451     for (AtomDeclSet::const_iterator atomIt = atoms.decls.begin(); atomIt != atoms.decls.end();
452          atomIt++) {
453         const string constant = make_constant_name((*atomIt)->name);
454         fprintf(out, "\n");
455         fprintf(out, "    /**\n");
456         fprintf(out, "     * %s %s<br>\n", (*atomIt)->message.c_str(), (*atomIt)->name.c_str());
457         write_java_usage(out, "write", constant, **atomIt);
458         auto non_chained_decl = atom_code_to_non_chained_decl_map.find((*atomIt)->code);
459         if (non_chained_decl != atom_code_to_non_chained_decl_map.end()) {
460             write_java_usage(out, "write_non_chained", constant, **(non_chained_decl->second));
461         }
462         fprintf(out, "     */\n");
463         fprintf(out, "    public static final int %s = %d;\n", constant.c_str(), (*atomIt)->code);
464     }
465     fprintf(out, "\n");
466 }
467 
write_java_enum_values(FILE * out,const Atoms & atoms)468 void write_java_enum_values(FILE* out, const Atoms& atoms) {
469     fprintf(out, "    // Constants for enum values.\n\n");
470     for (AtomDeclSet::const_iterator atomIt = atoms.decls.begin(); atomIt != atoms.decls.end();
471          atomIt++) {
472         for (vector<AtomField>::const_iterator field = (*atomIt)->fields.begin();
473              field != (*atomIt)->fields.end(); field++) {
474             if (field->javaType == JAVA_TYPE_ENUM || field->javaType == JAVA_TYPE_ENUM_ARRAY) {
475                 fprintf(out, "    // Values for %s.%s\n", (*atomIt)->message.c_str(),
476                         field->name.c_str());
477                 for (map<int, string>::const_iterator value = field->enumValues.begin();
478                      value != field->enumValues.end(); value++) {
479                     fprintf(out, "    public static final int %s__%s__%s = %d;\n",
480                             make_constant_name((*atomIt)->message).c_str(),
481                             make_constant_name(field->name).c_str(),
482                             make_constant_name(value->second).c_str(), value->first);
483                 }
484                 fprintf(out, "\n");
485             }
486         }
487     }
488 }
489 
write_java_method_signature(FILE * out,const vector<java_type_t> & signature,const AtomDecl & attributionDecl)490 int write_java_method_signature(FILE* out, const vector<java_type_t>& signature,
491                                 const AtomDecl& attributionDecl) {
492     int argIndex = 1;
493     for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
494          arg++) {
495         if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
496             if (attributionDecl.fields.empty()) {
497                 fprintf(stderr, "Encountered incompatible attribution chain atom definition");
498                 return 1;
499             }
500             for (const auto& chainField : attributionDecl.fields) {
501                 fprintf(out, ", %s[] %s", java_type_name(chainField.javaType),
502                         chainField.name.c_str());
503             }
504         } else {
505             fprintf(out, ", %s arg%d", java_type_name(*arg), argIndex);
506         }
507         argIndex++;
508     }
509     return 0;
510 }
511 
write_java_usage(FILE * out,const string & method_name,const string & atom_code_name,const AtomDecl & atom)512 void write_java_usage(FILE* out, const string& method_name, const string& atom_code_name,
513                       const AtomDecl& atom) {
514     fprintf(out, "     * Usage: StatsLog.%s(StatsLog.%s", method_name.c_str(),
515             atom_code_name.c_str());
516     for (vector<AtomField>::const_iterator field = atom.fields.begin(); field != atom.fields.end();
517          field++) {
518         if (field->javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
519             fprintf(out, ", android.os.WorkSource workSource");
520         } else if (field->javaType == JAVA_TYPE_BYTE_ARRAY) {
521             fprintf(out, ", byte[] %s", field->name.c_str());
522         } else {
523             fprintf(out, ", %s %s", java_type_name(field->javaType), field->name.c_str());
524         }
525     }
526     fprintf(out, ");<br>\n");
527 }
528 
write_java_non_chained_methods(FILE * out,const SignatureInfoMap & signatureInfoMap)529 int write_java_non_chained_methods(FILE* out, const SignatureInfoMap& signatureInfoMap) {
530     for (auto signatureInfoMapIt = signatureInfoMap.begin();
531          signatureInfoMapIt != signatureInfoMap.end(); signatureInfoMapIt++) {
532         // Print method signature.
533         fprintf(out, "    public static void write_non_chained(int code");
534         vector<java_type_t> signature = signatureInfoMapIt->first;
535         int argIndex = 1;
536         for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
537              arg++) {
538             if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
539                 fprintf(stderr, "Non chained signatures should not have attribution chains.\n");
540                 return 1;
541             } else {
542                 fprintf(out, ", %s arg%d", java_type_name(*arg), argIndex);
543             }
544             argIndex++;
545         }
546         fprintf(out, ") {\n");
547 
548         fprintf(out, "        write(code");
549         argIndex = 1;
550         for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
551              arg++) {
552             // First two args are uid and tag of attribution chain.
553             if (argIndex == 1) {
554                 fprintf(out, ", new int[] {arg%d}", argIndex);
555             } else if (argIndex == 2) {
556                 fprintf(out, ", new java.lang.String[] {arg%d}", argIndex);
557             } else {
558                 fprintf(out, ", arg%d", argIndex);
559             }
560             argIndex++;
561         }
562         fprintf(out, ");\n");
563         fprintf(out, "    }\n");
564         fprintf(out, "\n");
565     }
566     return 0;
567 }
568 
write_java_work_source_methods(FILE * out,const SignatureInfoMap & signatureInfoMap)569 int write_java_work_source_methods(FILE* out, const SignatureInfoMap& signatureInfoMap) {
570     fprintf(out, "    // WorkSource methods.\n");
571     for (auto signatureInfoMapIt = signatureInfoMap.begin();
572          signatureInfoMapIt != signatureInfoMap.end(); signatureInfoMapIt++) {
573         vector<java_type_t> signature = signatureInfoMapIt->first;
574         // Determine if there is Attribution in this signature.
575         int attributionArg = -1;
576         int argIndexMax = 0;
577         for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
578              arg++) {
579             argIndexMax++;
580             if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
581                 if (attributionArg > -1) {
582                     fprintf(stderr, "An atom contains multiple AttributionNode fields.\n");
583                     fprintf(stderr, "This is not supported. Aborting WorkSource method writing.\n");
584                     fprintf(out,
585                             "\n// Invalid for WorkSource: more than one attribution "
586                             "chain.\n");
587                     return 1;
588                 }
589                 attributionArg = argIndexMax;
590             }
591         }
592         if (attributionArg < 0) {
593             continue;
594         }
595 
596         fprintf(out, "\n");
597         // Method header (signature)
598         fprintf(out, "    public static void write(int code");
599         int argIndex = 1;
600         for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
601              arg++) {
602             if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
603                 fprintf(out, ", android.os.WorkSource ws");
604             } else {
605                 fprintf(out, ", %s arg%d", java_type_name(*arg), argIndex);
606             }
607             argIndex++;
608         }
609         fprintf(out, ") {\n");
610 
611         // write_non_chained() component. TODO: Remove when flat uids are no longer
612         // needed.
613         fprintf(out, "        for (int i = 0; i < ws.size(); ++i) {\n");
614         fprintf(out, "            write_non_chained(code");
615         for (int argIndex = 1; argIndex <= argIndexMax; argIndex++) {
616             if (argIndex == attributionArg) {
617                 fprintf(out, ", ws.getUid(i), ws.getPackageName(i)");
618             } else {
619                 fprintf(out, ", arg%d", argIndex);
620             }
621         }
622         fprintf(out, ");\n");
623         fprintf(out, "        }\n");  // close for-loop
624 
625         // write() component.
626         fprintf(out,
627                 "        java.util.List<android.os.WorkSource.WorkChain> workChains = "
628                 "ws.getWorkChains();\n");
629         fprintf(out, "        if (workChains != null) {\n");
630         fprintf(out,
631                 "            for (android.os.WorkSource.WorkChain wc : workChains) "
632                 "{\n");
633         fprintf(out, "                write(code");
634         for (int argIndex = 1; argIndex <= argIndexMax; argIndex++) {
635             if (argIndex == attributionArg) {
636                 fprintf(out, ", wc.getUids(), wc.getTags()");
637             } else {
638                 fprintf(out, ", arg%d", argIndex);
639             }
640         }
641         fprintf(out, ");\n");
642         fprintf(out, "            }\n");  // close for-loop
643         fprintf(out, "        }\n");      // close if
644         fprintf(out, "    }\n");          // close method
645     }
646     return 0;
647 }
648 
contains_restricted(const AtomDeclSet & atomDeclSet)649 static bool contains_restricted(const AtomDeclSet& atomDeclSet) {
650     for (const auto& decl : atomDeclSet) {
651         if (decl->restricted) {
652             return true;
653         }
654     }
655     return false;
656 }
657 
contains_repeated_field(const vector<java_type_t> & signature)658 static bool contains_repeated_field(const vector<java_type_t>& signature) {
659     for (const java_type_t& javaType : signature) {
660         switch (javaType) {
661             case JAVA_TYPE_BOOLEAN_ARRAY:
662             case JAVA_TYPE_INT_ARRAY:
663             case JAVA_TYPE_FLOAT_ARRAY:
664             case JAVA_TYPE_LONG_ARRAY:
665             case JAVA_TYPE_STRING_ARRAY:
666                 return true;
667             default:
668                 break;
669         }
670     }
671     return false;
672 }
673 
get_max_requires_api_level(int minApiLevel,const AtomDeclSet * atomDeclSet,const vector<java_type_t> & signature)674 int get_max_requires_api_level(int minApiLevel, const AtomDeclSet* atomDeclSet,
675                                const vector<java_type_t>& signature) {
676     if (atomDeclSet != nullptr && contains_restricted(*atomDeclSet)) {
677         return API_U;
678     }
679     if (contains_repeated_field(signature)) {
680         return API_T;
681     }
682     if (minApiLevel <= API_Q) {
683         return API_Q;  // for StatsLog.writeRaw()
684     }
685     return 0;
686 }
687 
get_annotations(int argIndex,const FieldNumberToAtomDeclSet & fieldNumberToAtomDeclSet)688 AtomDeclSet get_annotations(int argIndex,
689                             const FieldNumberToAtomDeclSet& fieldNumberToAtomDeclSet) {
690     const FieldNumberToAtomDeclSet::const_iterator fieldNumberToAtomDeclSetIt =
691             fieldNumberToAtomDeclSet.find(argIndex);
692     if (fieldNumberToAtomDeclSet.end() == fieldNumberToAtomDeclSetIt) {
693         return AtomDeclSet();
694     }
695     return fieldNumberToAtomDeclSetIt->second;
696 }
697 
698 }  // namespace stats_log_api_gen
699 }  // namespace android
700