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