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 if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
127 fprintf(out, ", android.util.SparseArray<Object> valueMap");
128 } else {
129 fprintf(out, ", %s arg%d", java_type_name(*arg), argIndex);
130 }
131 argIndex++;
132 }
133 }
134
write_method_body(FILE * out,const vector<java_type_t> & signature,const FieldNumberToAtomDeclSet & fieldNumberToAtomDeclSet,const AtomDecl & attributionDecl,const string & indent)135 static int write_method_body(FILE* out, const vector<java_type_t>& signature,
136 const FieldNumberToAtomDeclSet& fieldNumberToAtomDeclSet,
137 const AtomDecl& attributionDecl, const string& indent) {
138 // Start StatsEvent.Builder.
139 fprintf(out,
140 "%s final StatsEvent.Builder builder = "
141 "StatsEvent.newBuilder();\n",
142 indent.c_str());
143
144 // Write atom code.
145 fprintf(out, "%s builder.setAtomId(code);\n", indent.c_str());
146 write_annotations(out, ATOM_ID_FIELD_NUMBER, fieldNumberToAtomDeclSet);
147
148 // Write the args.
149 int argIndex = 1;
150 for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
151 arg++) {
152 switch (*arg) {
153 case JAVA_TYPE_BOOLEAN:
154 fprintf(out, "%s builder.writeBoolean(arg%d);\n", indent.c_str(),
155 argIndex);
156 break;
157 case JAVA_TYPE_INT:
158 case JAVA_TYPE_ENUM:
159 fprintf(out, "%s builder.writeInt(arg%d);\n", indent.c_str(), argIndex);
160 break;
161 case JAVA_TYPE_FLOAT:
162 fprintf(out, "%s builder.writeFloat(arg%d);\n", indent.c_str(),
163 argIndex);
164 break;
165 case JAVA_TYPE_LONG:
166 fprintf(out, "%s builder.writeLong(arg%d);\n", indent.c_str(), argIndex);
167 break;
168 case JAVA_TYPE_STRING:
169 fprintf(out, "%s builder.writeString(arg%d);\n", indent.c_str(),
170 argIndex);
171 break;
172 case JAVA_TYPE_BYTE_ARRAY:
173 fprintf(out,
174 "%s builder.writeByteArray(null == arg%d ? new byte[0] : "
175 "arg%d);\n",
176 indent.c_str(), argIndex, argIndex);
177 break;
178 case JAVA_TYPE_ATTRIBUTION_CHAIN: {
179 const char* uidName = attributionDecl.fields.front().name.c_str();
180 const char* tagName = attributionDecl.fields.back().name.c_str();
181
182 fprintf(out, "%s builder.writeAttributionChain(\n", indent.c_str());
183 fprintf(out, "%s null == %s ? new int[0] : %s,\n",
184 indent.c_str(), uidName, uidName);
185 fprintf(out, "%s null == %s ? new String[0] : %s);\n",
186 indent.c_str(), tagName, tagName);
187 break;
188 }
189 case JAVA_TYPE_KEY_VALUE_PAIR:
190 fprintf(out, "\n");
191 fprintf(out, "%s // Write KeyValuePairs.\n", indent.c_str());
192 fprintf(out, "%s final int count = valueMap.size();\n", indent.c_str());
193 fprintf(out, "%s android.util.SparseIntArray intMap = null;\n",
194 indent.c_str());
195 fprintf(out, "%s android.util.SparseLongArray longMap = null;\n",
196 indent.c_str());
197 fprintf(out, "%s android.util.SparseArray<String> stringMap = null;\n",
198 indent.c_str());
199 fprintf(out, "%s android.util.SparseArray<Float> floatMap = null;\n",
200 indent.c_str());
201 fprintf(out, "%s for (int i = 0; i < count; i++) {\n", indent.c_str());
202 fprintf(out, "%s final int key = valueMap.keyAt(i);\n",
203 indent.c_str());
204 fprintf(out, "%s final Object value = valueMap.valueAt(i);\n",
205 indent.c_str());
206 fprintf(out, "%s if (value instanceof Integer) {\n", indent.c_str());
207 fprintf(out, "%s if (null == intMap) {\n", indent.c_str());
208 fprintf(out,
209 "%s intMap = new "
210 "android.util.SparseIntArray();\n",
211 indent.c_str());
212 fprintf(out, "%s }\n", indent.c_str());
213 fprintf(out, "%s intMap.put(key, (Integer) value);\n",
214 indent.c_str());
215 fprintf(out, "%s } else if (value instanceof Long) {\n",
216 indent.c_str());
217 fprintf(out, "%s if (null == longMap) {\n", indent.c_str());
218 fprintf(out,
219 "%s longMap = new "
220 "android.util.SparseLongArray();\n",
221 indent.c_str());
222 fprintf(out, "%s }\n", indent.c_str());
223 fprintf(out, "%s longMap.put(key, (Long) value);\n",
224 indent.c_str());
225 fprintf(out, "%s } else if (value instanceof String) {\n",
226 indent.c_str());
227 fprintf(out, "%s if (null == stringMap) {\n", indent.c_str());
228 fprintf(out,
229 "%s stringMap = new "
230 "android.util.SparseArray<>();\n",
231 indent.c_str());
232 fprintf(out, "%s }\n", indent.c_str());
233 fprintf(out, "%s stringMap.put(key, (String) value);\n",
234 indent.c_str());
235 fprintf(out, "%s } else if (value instanceof Float) {\n",
236 indent.c_str());
237 fprintf(out, "%s if (null == floatMap) {\n", indent.c_str());
238 fprintf(out,
239 "%s floatMap = new "
240 "android.util.SparseArray<>();\n",
241 indent.c_str());
242 fprintf(out, "%s }\n", indent.c_str());
243 fprintf(out, "%s floatMap.put(key, (Float) value);\n",
244 indent.c_str());
245 fprintf(out, "%s }\n", indent.c_str());
246 fprintf(out, "%s }\n", indent.c_str());
247 fprintf(out,
248 "%s builder.writeKeyValuePairs("
249 "intMap, longMap, stringMap, floatMap);\n",
250 indent.c_str());
251 break;
252 default:
253 // Unsupported types: OBJECT, DOUBLE.
254 fprintf(stderr, "Encountered unsupported type.");
255 return 1;
256 }
257 write_annotations(out, argIndex, fieldNumberToAtomDeclSet);
258 argIndex++;
259 }
260 return 0;
261 }
262
write_java_pushed_methods(FILE * out,const SignatureInfoMap & signatureInfoMap,const AtomDecl & attributionDecl,const int minApiLevel)263 static int write_java_pushed_methods(FILE* out, const SignatureInfoMap& signatureInfoMap,
264 const AtomDecl& attributionDecl, const int minApiLevel) {
265 for (auto signatureInfoMapIt = signatureInfoMap.begin();
266 signatureInfoMapIt != signatureInfoMap.end(); signatureInfoMapIt++) {
267 // Print method signature.
268 fprintf(out, " public static void write(int code");
269 const vector<java_type_t>& signature = signatureInfoMapIt->first;
270 const FieldNumberToAtomDeclSet& fieldNumberToAtomDeclSet = signatureInfoMapIt->second;
271 write_method_signature(out, signature, attributionDecl);
272 fprintf(out, ") {\n");
273
274 // Print method body.
275 string indent("");
276 if (minApiLevel == API_Q) {
277 fprintf(out, " if (Build.VERSION.SDK_INT > Build.VERSION_CODES.Q) {\n");
278 indent = " ";
279 }
280
281 int ret = write_method_body(out, signature, fieldNumberToAtomDeclSet,
282 attributionDecl, indent);
283 if (ret != 0) {
284 return ret;
285 }
286 fprintf(out, "\n");
287
288 fprintf(out, "%s builder.usePooledBuffer();\n", indent.c_str());
289 fprintf(out, "%s StatsLog.write(builder.build());\n", indent.c_str());
290
291 // Add support for writing using Q schema if this is not the default module.
292 if (minApiLevel == API_Q) {
293 fprintf(out, " } else {\n");
294 fprintf(out, " QLogger.write(code");
295 int argIndex = 1;
296 for (vector<java_type_t>::const_iterator arg = signature.begin();
297 arg != signature.end(); arg++) {
298 if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
299 const char* uidName = attributionDecl.fields.front().name.c_str();
300 const char* tagName = attributionDecl.fields.back().name.c_str();
301 fprintf(out, ", %s, %s", uidName, tagName);
302 } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
303 // Module logging does not yet support key value pair.
304 fprintf(stderr, "Module logging does not yet support key value pair.\n");
305 return 1;
306 } else {
307 fprintf(out, ", arg%d", argIndex);
308 }
309 argIndex++;
310 }
311 fprintf(out, ");\n");
312 fprintf(out, " }\n"); // if
313 }
314
315 fprintf(out, " }\n"); // method
316 fprintf(out, "\n");
317 }
318 return 0;
319 }
320
write_java_pulled_methods(FILE * out,const SignatureInfoMap & signatureInfoMap,const AtomDecl & attributionDecl)321 static int write_java_pulled_methods(FILE* out, const SignatureInfoMap& signatureInfoMap,
322 const AtomDecl& attributionDecl) {
323 for (auto signatureInfoMapIt = signatureInfoMap.begin();
324 signatureInfoMapIt != signatureInfoMap.end(); signatureInfoMapIt++) {
325 // Print method signature.
326 fprintf(out, " public static StatsEvent buildStatsEvent(int code");
327 const vector<java_type_t>& signature = signatureInfoMapIt->first;
328 const FieldNumberToAtomDeclSet& fieldNumberToAtomDeclSet = signatureInfoMapIt->second;
329 write_method_signature(out, signature, attributionDecl);
330 fprintf(out, ") {\n");
331
332 // Print method body.
333 string indent("");
334 int ret = write_method_body(out, signature, fieldNumberToAtomDeclSet,
335 attributionDecl, indent);
336 if (ret != 0) {
337 return ret;
338 }
339 fprintf(out, "\n");
340
341 fprintf(out, "%s return builder.build();\n", indent.c_str());
342
343 fprintf(out, " }\n"); // method
344 fprintf(out, "\n");
345 }
346 return 0;
347 }
348
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)349 int write_stats_log_java(FILE* out, const Atoms& atoms, const AtomDecl& attributionDecl,
350 const string& javaClass, const string& javaPackage, const int minApiLevel,
351 const int compileApiLevel, const bool supportWorkSource) {
352 // Print prelude
353 fprintf(out, "// This file is autogenerated\n");
354 fprintf(out, "\n");
355 fprintf(out, "package %s;\n", javaPackage.c_str());
356 fprintf(out, "\n");
357 fprintf(out, "\n");
358 if (minApiLevel <= API_R) {
359 fprintf(out, "import android.os.Build;\n");
360 }
361 if (minApiLevel <= API_Q) {
362 fprintf(out, "import android.os.SystemClock;\n");
363 }
364
365 fprintf(out, "import android.util.StatsEvent;\n");
366 fprintf(out, "import android.util.StatsLog;\n");
367
368 fprintf(out, "\n");
369 fprintf(out, "\n");
370 fprintf(out, "/**\n");
371 fprintf(out, " * Utility class for logging statistics events.\n");
372 fprintf(out, " */\n");
373 fprintf(out, "public class %s {\n", javaClass.c_str());
374
375 write_java_atom_codes(out, atoms);
376 write_java_enum_values(out, atoms);
377 write_java_annotation_constants(out, minApiLevel, compileApiLevel);
378
379 int errors = 0;
380
381 // Print write methods.
382 fprintf(out, " // Write methods\n");
383 errors += write_java_pushed_methods(out, atoms.signatureInfoMap, attributionDecl, minApiLevel);
384 errors += write_java_non_chained_methods(out, atoms.nonChainedSignatureInfoMap);
385 errors += write_java_pulled_methods(out, atoms.pulledAtomsSignatureInfoMap,
386 attributionDecl);
387 if (supportWorkSource) {
388 errors += write_java_work_source_methods(out, atoms.signatureInfoMap);
389 }
390
391 if (minApiLevel == API_Q) {
392 errors += write_java_q_logger_class(out, atoms.signatureInfoMap, attributionDecl);
393 }
394
395 fprintf(out, "}\n");
396
397 return errors;
398 }
399
400 } // namespace stats_log_api_gen
401 } // namespace android
402