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