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)42 static void write_java_annotation_constants(FILE* out) {
43 fprintf(out, " // Annotation constants.\n");
44
45 for (const auto& [id, name] : ANNOTATION_ID_CONSTANTS) {
46 fprintf(out, " public static final byte %s = %hhu;\n", name.c_str(), id);
47 }
48 fprintf(out, "\n");
49 }
50
write_annotations(FILE * out,int argIndex,const FieldNumberToAtomDeclSet & fieldNumberToAtomDeclSet)51 static void write_annotations(FILE* out, int argIndex,
52 const FieldNumberToAtomDeclSet& fieldNumberToAtomDeclSet) {
53 FieldNumberToAtomDeclSet::const_iterator fieldNumberToAtomDeclSetIt =
54 fieldNumberToAtomDeclSet.find(argIndex);
55 if (fieldNumberToAtomDeclSet.end() == fieldNumberToAtomDeclSetIt) {
56 return;
57 }
58 const AtomDeclSet& atomDeclSet = fieldNumberToAtomDeclSetIt->second;
59 for (const shared_ptr<AtomDecl>& atomDecl : atomDeclSet) {
60 const string atomConstant = make_constant_name(atomDecl->name);
61 fprintf(out, " if (%s == code) {\n", atomConstant.c_str());
62 const AnnotationSet& annotations = atomDecl->fieldNumberToAnnotations.at(argIndex);
63 int resetState = -1;
64 int defaultState = -1;
65 for (const shared_ptr<Annotation>& annotation : annotations) {
66 const string& annotationConstant = ANNOTATION_ID_CONSTANTS.at(annotation->annotationId);
67 switch (annotation->type) {
68 case ANNOTATION_TYPE_INT:
69 if (ANNOTATION_ID_TRIGGER_STATE_RESET == annotation->annotationId) {
70 resetState = annotation->value.intValue;
71 } else if (ANNOTATION_ID_DEFAULT_STATE == annotation->annotationId) {
72 defaultState = annotation->value.intValue;
73 } else {
74 fprintf(out, " builder.addIntAnnotation(%s, %d);\n",
75 annotationConstant.c_str(), annotation->value.intValue);
76 }
77 break;
78 case ANNOTATION_TYPE_BOOL:
79 fprintf(out, " builder.addBooleanAnnotation(%s, %s);\n",
80 annotationConstant.c_str(),
81 annotation->value.boolValue ? "true" : "false");
82 break;
83 default:
84 break;
85 }
86 }
87 if (defaultState != -1 && resetState != -1) {
88 const string& annotationConstant =
89 ANNOTATION_ID_CONSTANTS.at(ANNOTATION_ID_TRIGGER_STATE_RESET);
90 fprintf(out, " if (arg%d == %d) {\n", argIndex, resetState);
91 fprintf(out, " builder.addIntAnnotation(%s, %d);\n",
92 annotationConstant.c_str(), defaultState);
93 fprintf(out, " }\n");
94 }
95 fprintf(out, " }\n");
96 }
97 }
98
write_java_methods(FILE * out,const SignatureInfoMap & signatureInfoMap,const AtomDecl & attributionDecl,const bool supportQ)99 static int write_java_methods(FILE* out, const SignatureInfoMap& signatureInfoMap,
100 const AtomDecl& attributionDecl, const bool supportQ) {
101 for (auto signatureInfoMapIt = signatureInfoMap.begin();
102 signatureInfoMapIt != signatureInfoMap.end(); signatureInfoMapIt++) {
103 // Print method signature.
104 fprintf(out, " public static void write(int code");
105 const vector<java_type_t>& signature = signatureInfoMapIt->first;
106 const FieldNumberToAtomDeclSet& fieldNumberToAtomDeclSet = signatureInfoMapIt->second;
107 int argIndex = 1;
108 for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
109 arg++) {
110 if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
111 for (auto chainField : attributionDecl.fields) {
112 fprintf(out, ", %s[] %s", java_type_name(chainField.javaType),
113 chainField.name.c_str());
114 }
115 } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
116 fprintf(out, ", android.util.SparseArray<Object> valueMap");
117 } else {
118 fprintf(out, ", %s arg%d", java_type_name(*arg), argIndex);
119 }
120 argIndex++;
121 }
122 fprintf(out, ") {\n");
123
124 // Print method body.
125 string indent("");
126 if (supportQ) {
127 fprintf(out, " if (Build.VERSION.SDK_INT > Build.VERSION_CODES.Q) {\n");
128 indent = " ";
129 }
130
131 // Start StatsEvent.Builder.
132 fprintf(out,
133 "%s final StatsEvent.Builder builder = "
134 "StatsEvent.newBuilder();\n",
135 indent.c_str());
136
137 // Write atom code.
138 fprintf(out, "%s builder.setAtomId(code);\n", indent.c_str());
139 write_annotations(out, ATOM_ID_FIELD_NUMBER, fieldNumberToAtomDeclSet);
140
141 // Write the args.
142 argIndex = 1;
143 for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
144 arg++) {
145 switch (*arg) {
146 case JAVA_TYPE_BOOLEAN:
147 fprintf(out, "%s builder.writeBoolean(arg%d);\n", indent.c_str(),
148 argIndex);
149 break;
150 case JAVA_TYPE_INT:
151 case JAVA_TYPE_ENUM:
152 fprintf(out, "%s builder.writeInt(arg%d);\n", indent.c_str(), argIndex);
153 break;
154 case JAVA_TYPE_FLOAT:
155 fprintf(out, "%s builder.writeFloat(arg%d);\n", indent.c_str(),
156 argIndex);
157 break;
158 case JAVA_TYPE_LONG:
159 fprintf(out, "%s builder.writeLong(arg%d);\n", indent.c_str(), argIndex);
160 break;
161 case JAVA_TYPE_STRING:
162 fprintf(out, "%s builder.writeString(arg%d);\n", indent.c_str(),
163 argIndex);
164 break;
165 case JAVA_TYPE_BYTE_ARRAY:
166 fprintf(out,
167 "%s builder.writeByteArray(null == arg%d ? new byte[0] : "
168 "arg%d);\n",
169 indent.c_str(), argIndex, argIndex);
170 break;
171 case JAVA_TYPE_ATTRIBUTION_CHAIN: {
172 const char* uidName = attributionDecl.fields.front().name.c_str();
173 const char* tagName = attributionDecl.fields.back().name.c_str();
174
175 fprintf(out, "%s builder.writeAttributionChain(\n", indent.c_str());
176 fprintf(out, "%s null == %s ? new int[0] : %s,\n",
177 indent.c_str(), uidName, uidName);
178 fprintf(out, "%s null == %s ? new String[0] : %s);\n",
179 indent.c_str(), tagName, tagName);
180 break;
181 }
182 case JAVA_TYPE_KEY_VALUE_PAIR:
183 fprintf(out, "\n");
184 fprintf(out, "%s // Write KeyValuePairs.\n", indent.c_str());
185 fprintf(out, "%s final int count = valueMap.size();\n", indent.c_str());
186 fprintf(out, "%s android.util.SparseIntArray intMap = null;\n",
187 indent.c_str());
188 fprintf(out, "%s android.util.SparseLongArray longMap = null;\n",
189 indent.c_str());
190 fprintf(out, "%s android.util.SparseArray<String> stringMap = null;\n",
191 indent.c_str());
192 fprintf(out, "%s android.util.SparseArray<Float> floatMap = null;\n",
193 indent.c_str());
194 fprintf(out, "%s for (int i = 0; i < count; i++) {\n", indent.c_str());
195 fprintf(out, "%s final int key = valueMap.keyAt(i);\n",
196 indent.c_str());
197 fprintf(out, "%s final Object value = valueMap.valueAt(i);\n",
198 indent.c_str());
199 fprintf(out, "%s if (value instanceof Integer) {\n", indent.c_str());
200 fprintf(out, "%s if (null == intMap) {\n", indent.c_str());
201 fprintf(out,
202 "%s intMap = new "
203 "android.util.SparseIntArray();\n",
204 indent.c_str());
205 fprintf(out, "%s }\n", indent.c_str());
206 fprintf(out, "%s intMap.put(key, (Integer) value);\n",
207 indent.c_str());
208 fprintf(out, "%s } else if (value instanceof Long) {\n",
209 indent.c_str());
210 fprintf(out, "%s if (null == longMap) {\n", indent.c_str());
211 fprintf(out,
212 "%s longMap = new "
213 "android.util.SparseLongArray();\n",
214 indent.c_str());
215 fprintf(out, "%s }\n", indent.c_str());
216 fprintf(out, "%s longMap.put(key, (Long) value);\n",
217 indent.c_str());
218 fprintf(out, "%s } else if (value instanceof String) {\n",
219 indent.c_str());
220 fprintf(out, "%s if (null == stringMap) {\n", indent.c_str());
221 fprintf(out,
222 "%s stringMap = new "
223 "android.util.SparseArray<>();\n",
224 indent.c_str());
225 fprintf(out, "%s }\n", indent.c_str());
226 fprintf(out, "%s stringMap.put(key, (String) value);\n",
227 indent.c_str());
228 fprintf(out, "%s } else if (value instanceof Float) {\n",
229 indent.c_str());
230 fprintf(out, "%s if (null == floatMap) {\n", indent.c_str());
231 fprintf(out,
232 "%s floatMap = new "
233 "android.util.SparseArray<>();\n",
234 indent.c_str());
235 fprintf(out, "%s }\n", indent.c_str());
236 fprintf(out, "%s floatMap.put(key, (Float) value);\n",
237 indent.c_str());
238 fprintf(out, "%s }\n", indent.c_str());
239 fprintf(out, "%s }\n", indent.c_str());
240 fprintf(out,
241 "%s builder.writeKeyValuePairs("
242 "intMap, longMap, stringMap, floatMap);\n",
243 indent.c_str());
244 break;
245 default:
246 // Unsupported types: OBJECT, DOUBLE.
247 fprintf(stderr, "Encountered unsupported type.");
248 return 1;
249 }
250 write_annotations(out, argIndex, fieldNumberToAtomDeclSet);
251 argIndex++;
252 }
253
254 fprintf(out, "\n");
255 fprintf(out, "%s builder.usePooledBuffer();\n", indent.c_str());
256 fprintf(out, "%s StatsLog.write(builder.build());\n", indent.c_str());
257
258 // Add support for writing using Q schema if this is not the default module.
259 if (supportQ) {
260 fprintf(out, " } else {\n");
261 fprintf(out, " QLogger.write(code");
262 argIndex = 1;
263 for (vector<java_type_t>::const_iterator arg = signature.begin();
264 arg != signature.end(); arg++) {
265 if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
266 const char* uidName = attributionDecl.fields.front().name.c_str();
267 const char* tagName = attributionDecl.fields.back().name.c_str();
268 fprintf(out, ", %s, %s", uidName, tagName);
269 } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
270 // Module logging does not yet support key value pair.
271 fprintf(stderr, "Module logging does not yet support key value pair.\n");
272 return 1;
273 } else {
274 fprintf(out, ", arg%d", argIndex);
275 }
276 argIndex++;
277 }
278 fprintf(out, ");\n");
279 fprintf(out, " }\n"); // if
280 }
281
282 fprintf(out, " }\n"); // method
283 fprintf(out, "\n");
284 }
285 return 0;
286 }
287
write_stats_log_java(FILE * out,const Atoms & atoms,const AtomDecl & attributionDecl,const string & javaClass,const string & javaPackage,const bool supportQ,const bool supportWorkSource)288 int write_stats_log_java(FILE* out, const Atoms& atoms, const AtomDecl& attributionDecl,
289 const string& javaClass, const string& javaPackage, const bool supportQ,
290 const bool supportWorkSource) {
291 // Print prelude
292 fprintf(out, "// This file is autogenerated\n");
293 fprintf(out, "\n");
294 fprintf(out, "package %s;\n", javaPackage.c_str());
295 fprintf(out, "\n");
296 fprintf(out, "\n");
297 if (supportQ) {
298 fprintf(out, "import android.os.Build;\n");
299 fprintf(out, "import android.os.SystemClock;\n");
300 }
301
302 fprintf(out, "import android.util.StatsEvent;\n");
303 fprintf(out, "import android.util.StatsLog;\n");
304
305 fprintf(out, "\n");
306 fprintf(out, "\n");
307 fprintf(out, "/**\n");
308 fprintf(out, " * Utility class for logging statistics events.\n");
309 fprintf(out, " */\n");
310 fprintf(out, "public class %s {\n", javaClass.c_str());
311
312 write_java_atom_codes(out, atoms);
313 write_java_enum_values(out, atoms);
314 write_java_annotation_constants(out);
315
316 int errors = 0;
317
318 // Print write methods.
319 fprintf(out, " // Write methods\n");
320 errors += write_java_methods(out, atoms.signatureInfoMap, attributionDecl, supportQ);
321 errors += write_java_non_chained_methods(out, atoms.nonChainedSignatureInfoMap);
322 if (supportWorkSource) {
323 errors += write_java_work_source_methods(out, atoms.signatureInfoMap);
324 }
325
326 if (supportQ) {
327 errors += write_java_q_logger_class(out, atoms.signatureInfoMap, attributionDecl);
328 }
329
330 fprintf(out, "}\n");
331
332 return errors;
333 }
334
335 } // namespace stats_log_api_gen
336 } // namespace android
337