• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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