• 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 "java_writer.h"
18 
19 #include "java_writer_q.h"
20 #include "utils.h"
21 
22 namespace android {
23 namespace stats_log_api_gen {
24 
write_java_q_logger_class(FILE * out,const SignatureInfoMap & signatureInfoMap,const AtomDecl & attributionDecl)25 static int write_java_q_logger_class(FILE* out, const SignatureInfoMap& signatureInfoMap,
26                                      const AtomDecl& attributionDecl) {
27     fprintf(out, "\n");
28     fprintf(out, "    // Write logging helper methods for statsd in Q and earlier.\n");
29     fprintf(out, "    private static class QLogger {\n");
30 
31     write_java_q_logging_constants(out, "        ");
32 
33     // Print Q write methods.
34     fprintf(out, "\n");
35     fprintf(out, "        // Write methods.\n");
36     write_java_methods_q_schema(out, signatureInfoMap, attributionDecl, "        ");
37 
38     fprintf(out, "    }\n");
39     return 0;
40 }
41 
write_java_annotation_constants(FILE * out,const int minApiLevel,const int compileApiLevel)42 static void write_java_annotation_constants(FILE* out, const int minApiLevel,
43                                             const int compileApiLevel) {
44     fprintf(out, "    // Annotation constants.\n");
45 
46     const map<AnnotationId, AnnotationStruct>& ANNOTATION_ID_CONSTANTS =
47             get_annotation_id_constants();
48     for (const auto& [id, annotation] : ANNOTATION_ID_CONSTANTS) {
49         if (annotation.minApiLevel < API_U) {  // we don't generate annotation constants for U+
50             if (compileApiLevel <= API_R) {
51                 fprintf(out, "    public static final byte %s = %hhu;\n", annotation.name.c_str(),
52                         id);
53             } else if (minApiLevel <= API_R) {  // compileApiLevel = S+
54                 fprintf(out, "    public static final byte %s =\n", annotation.name.c_str());
55                 fprintf(out, "            Build.VERSION.SDK_INT <= %s ?\n",
56                         get_java_build_version_code(API_R).c_str());
57                 fprintf(out, "            %hhu : StatsLog.%s;\n", id, annotation.name.c_str());
58                 fprintf(out, "\n");
59             } else {
60                 fprintf(out, "    public static final byte %s = StatsLog.%s;\n",
61                         annotation.name.c_str(), annotation.name.c_str());
62             }
63         }
64     }
65 
66     fprintf(out, "\n");
67 }
68 
write_annotations(FILE * out,int argIndex,const FieldNumberToAtomDeclSet & fieldNumberToAtomDeclSet)69 static void write_annotations(FILE* out, int argIndex,
70                               const FieldNumberToAtomDeclSet& fieldNumberToAtomDeclSet) {
71     FieldNumberToAtomDeclSet::const_iterator fieldNumberToAtomDeclSetIt =
72             fieldNumberToAtomDeclSet.find(argIndex);
73     if (fieldNumberToAtomDeclSet.end() == fieldNumberToAtomDeclSetIt) {
74         return;
75     }
76     const AtomDeclSet& atomDeclSet = fieldNumberToAtomDeclSetIt->second;
77     const map<AnnotationId, AnnotationStruct>& ANNOTATION_ID_CONSTANTS =
78             get_annotation_id_constants();
79     for (const shared_ptr<AtomDecl>& atomDecl : atomDeclSet) {
80         const string atomConstant = make_constant_name(atomDecl->name);
81         fprintf(out, "        if (%s == code) {\n", atomConstant.c_str());
82         const AnnotationSet& annotations = atomDecl->fieldNumberToAnnotations.at(argIndex);
83         int resetState = -1;
84         int defaultState = -1;
85         for (const shared_ptr<Annotation>& annotation : annotations) {
86             const AnnotationStruct& annotationConstant =
87                     ANNOTATION_ID_CONSTANTS.at(annotation->annotationId);
88             const char *prefix = annotationConstant.minApiLevel >= API_U ? "StatsLog." : "";
89             switch (annotation->type) {
90                 case ANNOTATION_TYPE_INT:
91                     if (ANNOTATION_ID_TRIGGER_STATE_RESET == annotation->annotationId) {
92                         resetState = annotation->value.intValue;
93                     } else if (ANNOTATION_ID_DEFAULT_STATE == annotation->annotationId) {
94                         defaultState = annotation->value.intValue;
95                     } else if (ANNOTATION_ID_RESTRICTION_CATEGORY == annotation->annotationId) {
96                         fprintf(out, "            builder.addIntAnnotation(%s%s,\n",
97                                 prefix, annotationConstant.name.c_str());
98                         fprintf(out, "                                     %s%s);\n",
99                                 prefix, get_restriction_category_str(annotation->value.intValue)
100                                 .c_str());
101                     } else {
102                         fprintf(out, "            builder.addIntAnnotation(%s%s, %d);\n",
103                                 prefix, annotationConstant.name.c_str(),
104                                 annotation->value.intValue);
105                     }
106                     break;
107                 case ANNOTATION_TYPE_BOOL:
108                     fprintf(out, "            builder.addBooleanAnnotation(%s%s, %s);\n",
109                             prefix, annotationConstant.name.c_str(),
110                             annotation->value.boolValue ? "true" : "false");
111                     break;
112                 default:
113                     break;
114             }
115         }
116         if (defaultState != -1 && resetState != -1) {
117             const AnnotationStruct& annotationConstant =
118                     ANNOTATION_ID_CONSTANTS.at(ANNOTATION_ID_TRIGGER_STATE_RESET);
119             fprintf(out, "            if (arg%d == %d) {\n", argIndex, resetState);
120             fprintf(out, "                builder.addIntAnnotation(%s, %d);\n",
121                     annotationConstant.name.c_str(), defaultState);
122             fprintf(out, "            }\n");
123         }
124         fprintf(out, "        }\n");
125     }
126 }
127 
write_method_signature(FILE * out,const vector<java_type_t> & signature,const AtomDecl & attributionDecl)128 static void write_method_signature(FILE* out, const vector<java_type_t>& signature,
129                                    const AtomDecl& attributionDecl) {
130     int argIndex = 1;
131     for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
132          arg++) {
133         if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
134             for (const auto& chainField : attributionDecl.fields) {
135                 fprintf(out, ", %s[] %s", java_type_name(chainField.javaType),
136                         chainField.name.c_str());
137             }
138         } else {
139             fprintf(out, ", %s arg%d", java_type_name(*arg), argIndex);
140         }
141         argIndex++;
142     }
143 }
144 
write_method_body(FILE * out,const vector<java_type_t> & signature,const FieldNumberToAtomDeclSet & fieldNumberToAtomDeclSet,const AtomDecl & attributionDecl,const string & indent,const int minApiLevel)145 static int write_method_body(FILE* out, const vector<java_type_t>& signature,
146                              const FieldNumberToAtomDeclSet& fieldNumberToAtomDeclSet,
147                              const AtomDecl& attributionDecl, const string& indent,
148                              const int minApiLevel) {
149     // Start StatsEvent.Builder.
150     fprintf(out,
151             "%s        final StatsEvent.Builder builder = "
152             "StatsEvent.newBuilder();\n",
153             indent.c_str());
154 
155     // Write atom code.
156     fprintf(out, "%s        builder.setAtomId(code);\n", indent.c_str());
157     write_annotations(out, ATOM_ID_FIELD_NUMBER, fieldNumberToAtomDeclSet);
158 
159     // Write the args.
160     int argIndex = 1;
161     for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
162          arg++) {
163         if (minApiLevel < API_T && is_repeated_field(*arg)) {
164             fprintf(stderr, "Found repeated field type with min api level < T.");
165             return 1;
166         }
167         switch (*arg) {
168             case JAVA_TYPE_BOOLEAN:
169                 fprintf(out, "%s        builder.writeBoolean(arg%d);\n", indent.c_str(), argIndex);
170                 break;
171             case JAVA_TYPE_INT:
172             case JAVA_TYPE_ENUM:
173                 fprintf(out, "%s        builder.writeInt(arg%d);\n", indent.c_str(), argIndex);
174                 break;
175             case JAVA_TYPE_FLOAT:
176                 fprintf(out, "%s        builder.writeFloat(arg%d);\n", indent.c_str(), argIndex);
177                 break;
178             case JAVA_TYPE_LONG:
179                 fprintf(out, "%s        builder.writeLong(arg%d);\n", indent.c_str(), argIndex);
180                 break;
181             case JAVA_TYPE_STRING:
182                 fprintf(out, "%s        builder.writeString(arg%d);\n", indent.c_str(), argIndex);
183                 break;
184             case JAVA_TYPE_BYTE_ARRAY:
185                 fprintf(out,
186                         "%s        builder.writeByteArray(null == arg%d ? new byte[0] : "
187                         "arg%d);\n",
188                         indent.c_str(), argIndex, argIndex);
189                 break;
190             case JAVA_TYPE_BOOLEAN_ARRAY:
191                 fprintf(out,
192                         "%s        builder.writeBooleanArray(null == arg%d ? new boolean[0] : "
193                         "arg%d);\n",
194                         indent.c_str(), argIndex, argIndex);
195                 break;
196             case JAVA_TYPE_INT_ARRAY:
197                 fprintf(out,
198                         "%s        builder.writeIntArray(null == arg%d ? new int[0] : arg%d);\n",
199                         indent.c_str(), argIndex, argIndex);
200                 break;
201             case JAVA_TYPE_FLOAT_ARRAY:
202                 fprintf(out,
203                         "%s        builder.writeFloatArray(null == arg%d ? new float[0] : "
204                         "arg%d);\n",
205                         indent.c_str(), argIndex, argIndex);
206                 break;
207             case JAVA_TYPE_LONG_ARRAY:
208                 fprintf(out,
209                         "%s        builder.writeLongArray(null == arg%d ? new long[0] : arg%d);\n",
210                         indent.c_str(), argIndex, argIndex);
211                 break;
212             case JAVA_TYPE_STRING_ARRAY:
213                 fprintf(out,
214                         "%s        builder.writeStringArray(null == arg%d ? new String[0] : "
215                         "arg%d);\n",
216                         indent.c_str(), argIndex, argIndex);
217                 break;
218             case JAVA_TYPE_ATTRIBUTION_CHAIN: {
219                 const char* uidName = attributionDecl.fields.front().name.c_str();
220                 const char* tagName = attributionDecl.fields.back().name.c_str();
221 
222                 fprintf(out, "%s        builder.writeAttributionChain(\n", indent.c_str());
223                 fprintf(out, "%s                null == %s ? new int[0] : %s,\n", indent.c_str(),
224                         uidName, uidName);
225                 fprintf(out, "%s                null == %s ? new String[0] : %s);\n",
226                         indent.c_str(), tagName, tagName);
227                 break;
228             }
229             default:
230                 // Unsupported types: OBJECT, DOUBLE.
231                 fprintf(stderr, "Encountered unsupported type.");
232                 return 1;
233         }
234         write_annotations(out, argIndex, fieldNumberToAtomDeclSet);
235         argIndex++;
236     }
237     return 0;
238 }
239 
write_java_pushed_methods(FILE * out,const SignatureInfoMap & signatureInfoMap,const AtomDecl & attributionDecl,const int minApiLevel)240 static int write_java_pushed_methods(FILE* out, const SignatureInfoMap& signatureInfoMap,
241                                      const AtomDecl& attributionDecl, const int minApiLevel) {
242     for (auto signatureInfoMapIt = signatureInfoMap.begin();
243          signatureInfoMapIt != signatureInfoMap.end(); signatureInfoMapIt++) {
244         const FieldNumberToAtomDeclSet& fieldNumberToAtomDeclSet = signatureInfoMapIt->second;
245         FieldNumberToAtomDeclSet::const_iterator fieldNumberToAtomDeclSetIt =
246             fieldNumberToAtomDeclSet.find(ATOM_ID_FIELD_NUMBER);
247         if (fieldNumberToAtomDeclSetIt != fieldNumberToAtomDeclSet.end()
248             && requires_api_needed(fieldNumberToAtomDeclSetIt->second)) {
249             fprintf(out, "    @RequiresApi(%s)\n",
250                     get_java_build_version_code(
251                         get_min_api_level(fieldNumberToAtomDeclSetIt->second)).c_str());
252         }
253         // Print method signature.
254         fprintf(out, "    public static void write(int code");
255         const vector<java_type_t>& signature = signatureInfoMapIt->first;
256         write_method_signature(out, signature, attributionDecl);
257         fprintf(out, ") {\n");
258 
259         // Print method body.
260         string indent("");
261         if (minApiLevel == API_Q) {
262             fprintf(out, "        if (Build.VERSION.SDK_INT > %s) {\n",
263                     get_java_build_version_code(API_Q).c_str());
264             indent = "    ";
265         }
266 
267         int ret = write_method_body(out, signature, fieldNumberToAtomDeclSet, attributionDecl,
268                                     indent, minApiLevel);
269         if (ret != 0) {
270             return ret;
271         }
272         fprintf(out, "\n");
273 
274         fprintf(out, "%s        builder.usePooledBuffer();\n", indent.c_str());
275         fprintf(out, "%s        StatsLog.write(builder.build());\n", indent.c_str());
276 
277         // Add support for writing using Q schema if this is not the default module.
278         if (minApiLevel == API_Q) {
279             fprintf(out, "        } else {\n");
280             fprintf(out, "            QLogger.write(code");
281             int argIndex = 1;
282             for (vector<java_type_t>::const_iterator arg = signature.begin();
283                  arg != signature.end(); arg++) {
284                 if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
285                     const char* uidName = attributionDecl.fields.front().name.c_str();
286                     const char* tagName = attributionDecl.fields.back().name.c_str();
287                     fprintf(out, ", %s, %s", uidName, tagName);
288                 } else if (is_repeated_field(*arg)) {
289                     // Module logging does not support repeated fields.
290                     fprintf(stderr, "Module logging does not support repeated fields.\n");
291                     return 1;
292                 } else {
293                     fprintf(out, ", arg%d", argIndex);
294                 }
295                 argIndex++;
296             }
297             fprintf(out, ");\n");
298             fprintf(out, "        }\n");  // if
299         }
300 
301         fprintf(out, "    }\n");  // method
302         fprintf(out, "\n");
303     }
304     return 0;
305 }
306 
write_java_pulled_methods(FILE * out,const SignatureInfoMap & signatureInfoMap,const AtomDecl & attributionDecl,const int minApiLevel)307 static int write_java_pulled_methods(FILE* out, const SignatureInfoMap& signatureInfoMap,
308                                      const AtomDecl& attributionDecl, const int minApiLevel) {
309     for (auto signatureInfoMapIt = signatureInfoMap.begin();
310          signatureInfoMapIt != signatureInfoMap.end(); signatureInfoMapIt++) {
311         // Print method signature.
312         fprintf(out, "    public static StatsEvent buildStatsEvent(int code");
313         const vector<java_type_t>& signature = signatureInfoMapIt->first;
314         const FieldNumberToAtomDeclSet& fieldNumberToAtomDeclSet = signatureInfoMapIt->second;
315         write_method_signature(out, signature, attributionDecl);
316         fprintf(out, ") {\n");
317 
318         // Print method body.
319         string indent("");
320         int ret = write_method_body(out, signature, fieldNumberToAtomDeclSet, attributionDecl,
321                                     indent, minApiLevel);
322         if (ret != 0) {
323             return ret;
324         }
325         fprintf(out, "\n");
326 
327         fprintf(out, "%s        return builder.build();\n", indent.c_str());
328 
329         fprintf(out, "    }\n");  // method
330         fprintf(out, "\n");
331     }
332     return 0;
333 }
334 
write_stats_log_java(FILE * out,const Atoms & atoms,const AtomDecl & attributionDecl,const string & javaClass,const string & javaPackage,const int minApiLevel,const int compileApiLevel,const bool supportWorkSource)335 int write_stats_log_java(FILE* out, const Atoms& atoms, const AtomDecl& attributionDecl,
336                          const string& javaClass, const string& javaPackage, const int minApiLevel,
337                          const int compileApiLevel, const bool supportWorkSource) {
338     // Print prelude
339     fprintf(out, "// This file is autogenerated\n");
340     fprintf(out, "\n");
341     fprintf(out, "package %s;\n", javaPackage.c_str());
342     fprintf(out, "\n");
343     fprintf(out, "\n");
344     fprintf(out, "import android.os.Build;\n");
345     if (minApiLevel <= API_Q) {
346         fprintf(out, "import android.os.SystemClock;\n");
347     }
348 
349     fprintf(out, "import android.util.StatsEvent;\n");
350     fprintf(out, "import android.util.StatsLog;\n");
351     if (requires_api_needed(atoms.decls)) {
352         fprintf(out, "import androidx.annotation.RequiresApi;\n");
353     }
354 
355     fprintf(out, "\n");
356     fprintf(out, "\n");
357     fprintf(out, "/**\n");
358     fprintf(out, " * Utility class for logging statistics events.\n");
359     fprintf(out, " */\n");
360     fprintf(out, "public final class %s {\n", javaClass.c_str());
361 
362     write_java_atom_codes(out, atoms);
363     write_java_enum_values(out, atoms);
364     write_java_annotation_constants(out, minApiLevel, compileApiLevel);
365 
366     int errors = 0;
367 
368     // Print write methods.
369     fprintf(out, "    // Write methods\n");
370     errors += write_java_pushed_methods(out, atoms.signatureInfoMap, attributionDecl, minApiLevel);
371     errors += write_java_non_chained_methods(out, atoms.nonChainedSignatureInfoMap);
372     errors += write_java_pulled_methods(out, atoms.pulledAtomsSignatureInfoMap, attributionDecl,
373                                         minApiLevel);
374     if (supportWorkSource) {
375         errors += write_java_work_source_methods(out, atoms.signatureInfoMap);
376     }
377 
378     if (minApiLevel == API_Q) {
379         errors += write_java_q_logger_class(out, atoms.signatureInfoMap, attributionDecl);
380     }
381 
382     fprintf(out, "}\n");
383 
384     return errors;
385 }
386 
write_stats_log_java_vendor(FILE * out,const Atoms & atoms,const string & javaClass,const string & javaPackage)387 int write_stats_log_java_vendor(FILE* out, const Atoms& atoms, const string& javaClass,
388                                 const string& javaPackage) {
389     // Print prelude
390     fprintf(out, "// This file is autogenerated\n");
391     fprintf(out, "\n");
392     fprintf(out, "package %s;\n", javaPackage.c_str());
393     fprintf(out, "\n");
394     fprintf(out, "/**\n");
395     fprintf(out, " * Utility class for logging statistics events.\n");
396     fprintf(out, " */\n");
397     fprintf(out, "public final class %s {\n", javaClass.c_str());
398 
399     write_java_atom_codes(out, atoms);
400     write_java_enum_values_vendor(out, atoms);
401 
402     fprintf(out, "}\n");
403 
404     return 0;
405 }
406 
407 
408 }  // namespace stats_log_api_gen
409 }  // namespace android
410