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