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