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