1 /*
2  * Copyright (C) 2023, 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 "java_writer_vendor.h"
18 
19 #include <stdio.h>
20 
21 #include <algorithm>
22 #include <map>
23 #include <memory>
24 #include <set>
25 #include <string>
26 #include <vector>
27 
28 #include "Collation.h"
29 #include "utils.h"
30 
31 namespace android {
32 namespace stats_log_api_gen {
33 
write_vendor_annotation_int(FILE * out,const string & annotationName,int value,const char * indent)34 static void write_vendor_annotation_int(FILE* out, const string& annotationName, int value,
35                                         const char* indent) {
36     fprintf(out, "%s{\n", indent);
37     fprintf(out, "%s    Annotation annotation = new Annotation();\n", indent);
38     fprintf(out, "%s    annotation.annotationId = %s;\n", indent, annotationName.c_str());
39     fprintf(out, "%s    annotation.value = AnnotationValue.intValue(%d);\n", indent, value);
40     fprintf(out, "%s    annotations[annotationIdx] = annotation;\n", indent);
41     fprintf(out, "%s    annotationIdx++;\n", indent);
42     fprintf(out, "%s}\n", indent);
43 }
44 
write_vendor_annotation_int_constant(FILE * out,const string & annotationName,const string & constantName,const char * indent)45 static void write_vendor_annotation_int_constant(FILE* out, const string& annotationName,
46                                                  const string& constantName, const char* indent) {
47     fprintf(out, "%s{\n", indent);
48     fprintf(out, "%s    Annotation annotation = new Annotation();\n", indent);
49     fprintf(out, "%s    annotation.annotationId = %s;\n", indent, annotationName.c_str());
50     fprintf(out, "%s    annotation.value = AnnotationValue.intValue(%s);\n", indent,
51             constantName.c_str());
52     fprintf(out, "%s    annotations[annotationIdx] = annotation;\n", indent);
53     fprintf(out, "%s    annotationIdx++;\n", indent);
54     fprintf(out, "%s}\n", indent);
55 }
56 
write_vendor_annotation_bool(FILE * out,const string & annotationName,bool value,const char * indent)57 static void write_vendor_annotation_bool(FILE* out, const string& annotationName, bool value,
58                                          const char* indent) {
59     fprintf(out, "%s{\n", indent);
60     fprintf(out, "%s    Annotation annotation = new Annotation();\n", indent);
61     fprintf(out, "%s    annotation.annotationId = %s;\n", indent, annotationName.c_str());
62     fprintf(out, "%s    annotation.value = AnnotationValue.boolValue(%s);\n", indent,
63             value ? "true" : "false");
64     fprintf(out, "%s    annotations[annotationIdx] = annotation;\n", indent);
65     fprintf(out, "%s    annotationIdx++;\n", indent);
66     fprintf(out, "%s}\n", indent);
67 }
68 
write_value_annotations_array_init(FILE * out,const AtomDeclSet & atomDeclSet,set<string> & processedAtomNames)69 static bool write_value_annotations_array_init(FILE* out, const AtomDeclSet& atomDeclSet,
70                                                set<string>& processedAtomNames) {
71     const char* indent = "        ";
72 
73     for (const shared_ptr<AtomDecl>& atomDecl : atomDeclSet) {
74         const string atomConstant = make_constant_name(atomDecl->name);
75         if (processedAtomNames.find(atomConstant) != processedAtomNames.end()) {
76             continue;
77         }
78         processedAtomNames.insert(atomConstant);
79         fprintf(out, "%sif (%s == atomId) {\n", indent, atomConstant.c_str());
80         fprintf(out, "%s    fieldsAnnotations = new ArrayList<AnnotationSet>();\n", indent);
81         fprintf(out, "%s}\n", indent);
82     }
83     return true;
84 }
85 
write_annotations_vendor_for_field(FILE * out,int argIndex,const AtomDeclSet & atomDeclSet)86 static bool write_annotations_vendor_for_field(FILE* out, int argIndex,
87                                                const AtomDeclSet& atomDeclSet) {
88     const char* indent = "        ";
89     const char* indent2 = "            ";
90     const char* indent3 = "               ";
91 
92     const map<AnnotationId, AnnotationStruct>& annotationIdConstants =
93             get_annotation_id_constants(ANNOTATION_CONSTANT_NAME_VENDOR_PREFIX);
94 
95     for (const shared_ptr<AtomDecl>& atomDecl : atomDeclSet) {
96         const string atomConstant = make_constant_name(atomDecl->name);
97         fprintf(out, "%sif (%s == atomId) {\n", indent, atomConstant.c_str());
98 
99         const AnnotationSet& annotations = atomDecl->fieldNumberToAnnotations.at(argIndex);
100         // calculate annotations amount upfront
101         // search for a trigger reset state & default state annotation
102         int resetState = -1;
103         int defaultState = -1;
104         for (const shared_ptr<Annotation>& annotation : annotations) {
105             switch (annotation->type) {
106                 case ANNOTATION_TYPE_INT:
107                     if (ANNOTATION_ID_TRIGGER_STATE_RESET == annotation->annotationId) {
108                         resetState = annotation->value.intValue;
109                     } else if (ANNOTATION_ID_DEFAULT_STATE == annotation->annotationId) {
110                         defaultState = annotation->value.intValue;
111                     }
112                     break;
113                 default:
114                     break;
115             }
116         }
117 
118         const int annotationsCount = std::count_if(
119                 annotations.begin(), annotations.end(),
120                 [](const shared_ptr<Annotation>& annotation) {
121                     return (annotation->type == ANNOTATION_TYPE_INT &&
122                             (ANNOTATION_ID_TRIGGER_STATE_RESET != annotation->annotationId &&
123                              ANNOTATION_ID_DEFAULT_STATE != annotation->annotationId)) ||
124                            annotation->type == ANNOTATION_TYPE_BOOL;
125                 });
126 
127         fprintf(out, "%sint annotationsCount = %d;\n", indent2, annotationsCount);
128         if (defaultState != -1 && resetState != -1) {
129             fprintf(out, "%sif (arg%d == %d) {\n", indent2, argIndex, resetState);
130             fprintf(out, "%s    annotationsCount++;\n", indent2);
131             fprintf(out, "%s}\n", indent2);
132         }
133         fprintf(out, "%sAnnotation[] annotations = new Annotation[annotationsCount];\n", indent2);
134         fprintf(out, "%sint annotationIdx = 0;\n", indent2);
135 
136         for (const shared_ptr<Annotation>& annotation : annotations) {
137             const AnnotationStruct& annotationConstant =
138                     annotationIdConstants.at(annotation->annotationId);
139             switch (annotation->type) {
140                 case ANNOTATION_TYPE_INT:
141                     if (ANNOTATION_ID_RESTRICTION_CATEGORY == annotation->annotationId) {
142                         write_vendor_annotation_int_constant(
143                                 out, annotationConstant.name,
144                                 get_restriction_category_str(annotation->value.intValue), indent2);
145                     } else if (ANNOTATION_ID_TRIGGER_STATE_RESET != annotation->annotationId &&
146                                ANNOTATION_ID_DEFAULT_STATE != annotation->annotationId) {
147                         write_vendor_annotation_int(out, annotationConstant.name,
148                                                     annotation->value.intValue, indent2);
149                     }
150                     break;
151                 case ANNOTATION_TYPE_BOOL:
152                     write_vendor_annotation_bool(out, annotationConstant.name,
153                                                  annotation->value.boolValue, indent2);
154                     break;
155                 default:
156                     break;
157             }
158         }
159         if (defaultState != -1 && resetState != -1) {
160             const AnnotationStruct& annotationConstant =
161                     annotationIdConstants.at(ANNOTATION_ID_TRIGGER_STATE_RESET);
162             fprintf(out, "%sif (arg%d == %d)", indent2, argIndex, resetState);
163             write_vendor_annotation_int(out, annotationConstant.name, defaultState, indent2);
164         }
165 
166         if (argIndex == ATOM_ID_FIELD_NUMBER) {
167             fprintf(out, "%satomAnnotations = annotations;\n", indent2);
168         } else {
169             const int valueIndex = argIndex - 2;
170             fprintf(out, "%sif (annotationsCount > 0) {\n", indent2);
171             fprintf(out, "%sAnnotationSet field%dAnnotations = new AnnotationSet();\n", indent3,
172                     valueIndex);
173             fprintf(out, "%sfield%dAnnotations.valueIndex = %d;\n", indent3, valueIndex,
174                     valueIndex);
175             fprintf(out, "%sfield%dAnnotations.annotations = annotations;\n", indent3, valueIndex);
176             fprintf(out, "%sfieldsAnnotations.add(field%dAnnotations);\n", indent3, valueIndex);
177             fprintf(out, "%s}\n", indent2);
178         }
179         fprintf(out, "%s}\n", indent);
180     }
181 
182     return true;
183 }
184 
write_method_body_vendor(FILE * out,const vector<java_type_t> & signature,const FieldNumberToAtomDeclSet & fieldNumberToAtomDeclSet)185 static int write_method_body_vendor(FILE* out, const vector<java_type_t>& signature,
186                                     const FieldNumberToAtomDeclSet& fieldNumberToAtomDeclSet) {
187     const char* indent = "        ";
188 
189     // Write atom code.
190     fprintf(out, "%sVendorAtom atom = new VendorAtom();\n", indent);
191     fprintf(out, "%satom.reverseDomainName = arg1;\n", indent);
192     fprintf(out, "%satom.atomId = atomId;\n", indent);
193     fprintf(out, "%satom.values = new VendorAtomValue[%ld];\n", indent, signature.size() - 1);
194 
195     for (int argIndex = 2; argIndex <= signature.size(); argIndex++) {
196         const java_type_t& argType = signature[argIndex - 1];
197         const int atomValueIndex = argIndex - 2;
198         switch (argType) {
199             case JAVA_TYPE_ATTRIBUTION_CHAIN: {
200                 fprintf(stderr, "Found attribution chain - not supported.\n");
201                 return 1;
202             }
203             case JAVA_TYPE_BYTE_ARRAY:
204                 fprintf(out, "%satom.values[%d] = VendorAtomValue.byteArrayValue(arg%d);\n", indent,
205                         atomValueIndex, argIndex);
206                 break;
207             case JAVA_TYPE_BOOLEAN:
208                 fprintf(out, "%satom.values[%d] = VendorAtomValue.boolValue(arg%d);\n", indent,
209                         atomValueIndex, argIndex);
210                 break;
211             case JAVA_TYPE_INT:
212                 [[fallthrough]];
213             case JAVA_TYPE_ENUM:
214                 fprintf(out, "%satom.values[%d] = VendorAtomValue.intValue(arg%d);\n", indent,
215                         atomValueIndex, argIndex);
216                 break;
217             case JAVA_TYPE_FLOAT:
218                 fprintf(out, "%satom.values[%d] = VendorAtomValue.floatValue(arg%d);\n", indent,
219                         atomValueIndex, argIndex);
220                 break;
221             case JAVA_TYPE_LONG:
222                 fprintf(out, "%satom.values[%d] = VendorAtomValue.longValue(arg%d);\n", indent,
223                         atomValueIndex, argIndex);
224                 break;
225             case JAVA_TYPE_STRING:
226                 fprintf(out, "%satom.values[%d] = VendorAtomValue.stringValue(arg%d);\n", indent,
227                         atomValueIndex, argIndex);
228                 break;
229             case JAVA_TYPE_BOOLEAN_ARRAY:
230                 fprintf(out, "%satom.values[%d] = VendorAtomValue.repeatedBoolValue(arg%d);\n",
231                         indent, atomValueIndex, argIndex);
232                 break;
233             case JAVA_TYPE_INT_ARRAY:
234                 [[fallthrough]];
235             case JAVA_TYPE_ENUM_ARRAY:
236                 fprintf(out, "%satom.values[%d] = VendorAtomValue.repeatedIntValue(arg%d);\n",
237                         indent, atomValueIndex, argIndex);
238                 break;
239             case JAVA_TYPE_FLOAT_ARRAY:
240                 fprintf(out, "%satom.values[%d] = VendorAtomValue.repeatedFloatValue(arg%d);\n",
241                         indent, atomValueIndex, argIndex);
242                 break;
243             case JAVA_TYPE_LONG_ARRAY:
244                 fprintf(out, "%satom.values[%d] = VendorAtomValue.repeatedLongValue(arg%d);\n",
245                         indent, atomValueIndex, argIndex);
246                 break;
247             case JAVA_TYPE_STRING_ARRAY:
248                 fprintf(out, "%satom.values[%d] = VendorAtomValue.repeatedStringValue(arg%d);\n",
249                         indent, atomValueIndex, argIndex);
250                 break;
251             default:
252                 // Unsupported types: OBJECT, DOUBLE
253                 fprintf(stderr, "Encountered unsupported type.\n");
254                 return 1;
255         }
256     }
257 
258     // check will be there an atom for this signature with atom level annotations
259     const AtomDeclSet atomAnnotations =
260             get_annotations(ATOM_ID_FIELD_NUMBER, fieldNumberToAtomDeclSet);
261     if (atomAnnotations.size()) {
262         fprintf(out, "%sAnnotation[] atomAnnotations = null;\n", indent);
263         write_annotations_vendor_for_field(out, ATOM_ID_FIELD_NUMBER, atomAnnotations);
264         fprintf(out, "%sif (atomAnnotations != null && atomAnnotations.length > 0) {\n", indent);
265         fprintf(out, "%s    atom.atomAnnotations = atomAnnotations;\n", indent);
266         fprintf(out, "%s}\n", indent);
267     }
268 
269     // Create fieldsAnnotations instance only in case if there is an atom fields with annotation
270     // for this signature
271     bool atomWithFieldsAnnotation = false;
272     for (int argIndex = 2; argIndex <= signature.size(); argIndex++) {
273         if (get_annotations(argIndex, fieldNumberToAtomDeclSet).size() > 0) {
274             atomWithFieldsAnnotation = true;
275             break;
276         }
277     }
278 
279     if (atomWithFieldsAnnotation) {
280         fprintf(out, "%sArrayList<AnnotationSet> fieldsAnnotations = null;\n", indent);
281         set<string> processedAtomNames;
282         for (int argIndex = 2; argIndex <= signature.size(); argIndex++) {
283             const AtomDeclSet fieldAnnotations =
284                     get_annotations(argIndex, fieldNumberToAtomDeclSet);
285             write_value_annotations_array_init(out, fieldAnnotations, processedAtomNames);
286         }
287 
288         for (int argIndex = 2; argIndex <= signature.size(); argIndex++) {
289             const AtomDeclSet fieldAnnotations =
290                     get_annotations(argIndex, fieldNumberToAtomDeclSet);
291             write_annotations_vendor_for_field(out, argIndex, fieldAnnotations);
292         }
293         fprintf(out, "%sif (fieldsAnnotations != null && fieldsAnnotations.size() > 0) {\n",
294                 indent);
295         // Converting ArrayList<AnnotationSet> to annotationSet[]
296         fprintf(out,
297                 "%s    atom.valuesAnnotations = new AnnotationSet[fieldsAnnotations.size()];\n",
298                 indent);
299         fprintf(out,
300                 "%s    atom.valuesAnnotations = "
301                 "fieldsAnnotations.toArray(atom.valuesAnnotations);\n",
302                 indent);
303         fprintf(out, "%s}\n", indent);
304     }
305 
306     fprintf(out, "%sreturn atom;\n", indent);
307 
308     return 0;
309 }
310 
write_java_pushed_methods_vendor(FILE * out,const SignatureInfoMap & signatureInfoMap)311 static int write_java_pushed_methods_vendor(FILE* out, const SignatureInfoMap& signatureInfoMap) {
312     for (auto signatureInfoMapIt = signatureInfoMap.begin();
313          signatureInfoMapIt != signatureInfoMap.end(); signatureInfoMapIt++) {
314         // Print method signature.
315         fprintf(out, "    public static VendorAtom createVendorAtom(int atomId");
316         const vector<java_type_t>& signature = signatureInfoMapIt->first;
317         const AtomDecl emptyAttributionDecl;
318         int ret = write_java_method_signature(out, signature, emptyAttributionDecl);
319         if (ret != 0) {
320             return ret;
321         }
322         fprintf(out, ") {\n");
323 
324         // Print method body.
325         const FieldNumberToAtomDeclSet& fieldNumberToAtomDeclSet = signatureInfoMapIt->second;
326         ret = write_method_body_vendor(out, signature, fieldNumberToAtomDeclSet);
327         if (ret != 0) {
328             return ret;
329         }
330 
331         fprintf(out, "    }\n");  // method
332         fprintf(out, "\n");
333     }
334     return 0;
335 }
336 
write_java_enum_values_vendor(FILE * out,const Atoms & atoms)337 static void write_java_enum_values_vendor(FILE* out, const Atoms& atoms) {
338     set<string> processedEnums;
339 
340     fprintf(out, "    // Constants for enum values.\n\n");
341     for (AtomDeclSet::const_iterator atomIt = atoms.decls.begin(); atomIt != atoms.decls.end();
342          atomIt++) {
343         for (vector<AtomField>::const_iterator field = (*atomIt)->fields.begin();
344              field != (*atomIt)->fields.end(); field++) {
345             if (field->javaType == JAVA_TYPE_ENUM || field->javaType == JAVA_TYPE_ENUM_ARRAY) {
346                 // There might be N fields with the same enum type
347                 // Avoiding duplication definitions
348                 // enum type name == [atom_message_type_name]__[enum_type_name]
349                 const string full_enum_type_name = (*atomIt)->message + "__" + field->enumTypeName;
350 
351                 if (processedEnums.find(full_enum_type_name) != processedEnums.end()) {
352                     continue;
353                 }
354                 processedEnums.insert(full_enum_type_name);
355 
356                 fprintf(out, "    // Values for %s.%s\n", (*atomIt)->message.c_str(),
357                         field->name.c_str());
358                 for (map<int, string>::const_iterator value = field->enumValues.begin();
359                      value != field->enumValues.end(); value++) {
360                     fprintf(out, "    public static final int %s__%s = %d;\n",
361                             make_constant_name(full_enum_type_name).c_str(),
362                             make_constant_name(value->second).c_str(), value->first);
363                 }
364                 fprintf(out, "\n");
365             }
366         }
367     }
368 }
369 
write_stats_log_java_vendor(FILE * out,const Atoms & atoms,const string & javaClass,const string & javaPackage)370 int write_stats_log_java_vendor(FILE* out, const Atoms& atoms, const string& javaClass,
371                                 const string& javaPackage) {
372     // Print prelude
373     fprintf(out, "// This file is autogenerated\n");
374     fprintf(out, "\n");
375     fprintf(out, "package %s;\n", javaPackage.c_str());
376     fprintf(out, "\n");
377 
378     fprintf(out, "import android.frameworks.stats.VendorAtom;\n");
379     fprintf(out, "import android.frameworks.stats.VendorAtomValue;\n");
380     fprintf(out, "import android.frameworks.stats.AnnotationValue;\n");
381     fprintf(out, "import android.frameworks.stats.Annotation;\n");
382     fprintf(out, "import android.frameworks.stats.AnnotationId;\n");
383     fprintf(out, "import android.frameworks.stats.AnnotationSet;\n");
384 
385     fprintf(out, "import java.util.ArrayList;\n");
386 
387     fprintf(out, "\n");
388     fprintf(out, "/**\n");
389     fprintf(out, " * Utility class for logging statistics events.\n");
390     fprintf(out, " */\n");
391     fprintf(out, "public final class %s {\n", javaClass.c_str());
392 
393     write_java_atom_codes(out, atoms);
394     write_java_enum_values_vendor(out, atoms);
395 
396     // Print write methods.
397     fprintf(out, "    // Write methods\n");
398     const int errors = write_java_pushed_methods_vendor(out, atoms.signatureInfoMap);
399 
400     fprintf(out, "}\n");
401 
402     return errors;
403 }
404 
405 }  // namespace stats_log_api_gen
406 }  // namespace android
407