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 #include "java_writer_q.h"
19 #include "utils.h"
20 
21 namespace android {
22 namespace stats_log_api_gen {
23 
write_java_q_logger_class(FILE * out,const map<vector<java_type_t>,set<string>> & signatures_to_modules,const AtomDecl & attributionDecl,const string & moduleName)24 static int write_java_q_logger_class(
25         FILE* out,
26         const map<vector<java_type_t>, set<string>>& signatures_to_modules,
27         const AtomDecl &attributionDecl,
28         const string& moduleName
29         ) {
30     fprintf(out, "\n");
31     fprintf(out, "    // Write logging helper methods for statsd in Q and earlier.\n");
32     fprintf(out, "    private static class QLogger {\n");
33 
34     write_java_q_logging_constants(out, "        ");
35 
36     // Print Q write methods.
37     fprintf(out, "\n");
38     fprintf(out, "        // Write methods.\n");
39     write_java_methods_q_schema(
40             out, signatures_to_modules, attributionDecl, moduleName, "        ");
41 
42     fprintf(out, "    }\n");
43     return 0;
44 }
45 
46 
write_java_methods(FILE * out,const map<vector<java_type_t>,set<string>> & signatures_to_modules,const AtomDecl & attributionDecl,const string & moduleName,const bool supportQ)47 static int write_java_methods(
48         FILE* out,
49         const map<vector<java_type_t>, set<string>>& signatures_to_modules,
50         const AtomDecl &attributionDecl,
51         const string& moduleName,
52         const bool supportQ
53         ) {
54     for (auto signature_to_modules_it = signatures_to_modules.begin();
55             signature_to_modules_it != signatures_to_modules.end(); signature_to_modules_it++) {
56         // Skip if this signature is not needed for the module.
57         if (!signature_needed_for_module(signature_to_modules_it->second, moduleName)) {
58             continue;
59         }
60 
61         // Print method signature.
62         if (DEFAULT_MODULE_NAME == moduleName) {
63             fprintf(out, "    /** @hide */\n");
64         }
65         fprintf(out, "    public static void write(int code");
66         vector<java_type_t> signature = signature_to_modules_it->first;
67         int argIndex = 1;
68         for (vector<java_type_t>::const_iterator arg = signature.begin();
69                 arg != signature.end(); arg++) {
70             if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
71                 for (auto chainField : attributionDecl.fields) {
72                     fprintf(out, ", %s[] %s",
73                         java_type_name(chainField.javaType), chainField.name.c_str());
74                 }
75             } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
76                 fprintf(out, ", android.util.SparseArray<Object> valueMap");
77             } else {
78                 fprintf(out, ", %s arg%d", java_type_name(*arg), argIndex);
79             }
80             argIndex++;
81         }
82         fprintf(out, ") {\n");
83 
84         // Print method body.
85         string indent("");
86         if (supportQ) {
87             // TODO(b/146235828): Use just SDK_INT check once it is incremented from Q.
88             fprintf(out, "        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.Q\n");
89             fprintf(out, "                || (Build.VERSION.SDK_INT == Build.VERSION_CODES.Q\n");
90             fprintf(out, "                    && Build.VERSION.PREVIEW_SDK_INT > 0)) {\n");
91             indent = "    ";
92         }
93 
94         // Start StatsEvent.Builder.
95         fprintf(out, "%s        final StatsEvent.Builder builder = StatsEvent.newBuilder();\n",
96                 indent.c_str());
97 
98         // Write atom code.
99         fprintf(out, "%s        builder.setAtomId(code);\n", indent.c_str());
100 
101         // Write the args.
102         argIndex = 1;
103         for (vector<java_type_t>::const_iterator arg = signature.begin();
104                 arg != signature.end(); arg++) {
105             switch (*arg) {
106             case JAVA_TYPE_BOOLEAN:
107                 fprintf(out, "%s        builder.writeBoolean(arg%d);\n", indent.c_str(), argIndex);
108                 break;
109             case JAVA_TYPE_INT:
110             case JAVA_TYPE_ENUM:
111                 fprintf(out, "%s        builder.writeInt(arg%d);\n", indent.c_str(), argIndex);
112                 break;
113             case JAVA_TYPE_FLOAT:
114                 fprintf(out, "%s        builder.writeFloat(arg%d);\n", indent.c_str(), argIndex);
115                 break;
116             case JAVA_TYPE_LONG:
117                 fprintf(out, "%s        builder.writeLong(arg%d);\n", indent.c_str(), argIndex);
118                 break;
119             case JAVA_TYPE_STRING:
120                 fprintf(out, "%s        builder.writeString(arg%d);\n", indent.c_str(), argIndex);
121                 break;
122             case JAVA_TYPE_BYTE_ARRAY:
123                 fprintf(out, "%s        builder.writeByteArray(null == arg%d ? new byte[0] : arg%d);\n",
124                         indent.c_str(), argIndex, argIndex);
125                 break;
126             case JAVA_TYPE_ATTRIBUTION_CHAIN:
127             {
128                 const char* uidName = attributionDecl.fields.front().name.c_str();
129                 const char* tagName = attributionDecl.fields.back().name.c_str();
130 
131                 fprintf(out, "%s        builder.writeAttributionChain(\n", indent.c_str());
132                 fprintf(out, "%s                null == %s ? new int[0] : %s,\n",
133                         indent.c_str(), uidName, uidName);
134                 fprintf(out, "%s                null == %s ? new String[0] : %s);\n",
135                         indent.c_str(), tagName, tagName);
136                 break;
137             }
138             case JAVA_TYPE_KEY_VALUE_PAIR:
139                 fprintf(out, "\n");
140                 fprintf(out,
141                         "%s        // Write KeyValuePairs.\n", indent.c_str());
142                 fprintf(out,
143                         "%s        final int count = valueMap.size();\n", indent.c_str());
144                 fprintf(out,
145                         "%s        android.util.SparseIntArray intMap = null;\n",
146                         indent.c_str());
147                 fprintf(out,
148                         "%s        android.util.SparseLongArray longMap = null;\n",
149                         indent.c_str());
150                 fprintf(out,
151                         "%s        android.util.SparseArray<String> stringMap = null;\n",
152                         indent.c_str());
153                 fprintf(out,
154                         "%s        android.util.SparseArray<Float> floatMap = null;\n",
155                         indent.c_str());
156                 fprintf(out,
157                         "%s        for (int i = 0; i < count; i++) {\n", indent.c_str());
158                 fprintf(out,
159                         "%s            final int key = valueMap.keyAt(i);\n", indent.c_str());
160                 fprintf(out,
161                         "%s            final Object value = valueMap.valueAt(i);\n",
162                         indent.c_str());
163                 fprintf(out,
164                         "%s            if (value instanceof Integer) {\n", indent.c_str());
165                 fprintf(out,
166                         "%s                if (null == intMap) {\n", indent.c_str());
167                 fprintf(out,
168                         "%s                    intMap = new android.util.SparseIntArray();\n", indent.c_str());
169                 fprintf(out,
170                         "%s                }\n", indent.c_str());
171                 fprintf(out,
172                         "%s                intMap.put(key, (Integer) value);\n", indent.c_str());
173                 fprintf(out,
174                         "%s            } else if (value instanceof Long) {\n", indent.c_str());
175                 fprintf(out,
176                         "%s                if (null == longMap) {\n", indent.c_str());
177                 fprintf(out,
178                         "%s                    longMap = new android.util.SparseLongArray();\n", indent.c_str());
179                 fprintf(out,
180                         "%s                }\n", indent.c_str());
181                 fprintf(out,
182                         "%s                longMap.put(key, (Long) value);\n", indent.c_str());
183                 fprintf(out,
184                         "%s            } else if (value instanceof String) {\n", indent.c_str());
185                 fprintf(out,
186                         "%s                if (null == stringMap) {\n", indent.c_str());
187                 fprintf(out,
188                         "%s                    stringMap = new android.util.SparseArray<>();\n", indent.c_str());
189                 fprintf(out,
190                         "%s                }\n", indent.c_str());
191                 fprintf(out,
192                         "%s                stringMap.put(key, (String) value);\n", indent.c_str());
193                 fprintf(out,
194                         "%s            } else if (value instanceof Float) {\n", indent.c_str());
195                 fprintf(out,
196                         "%s                if (null == floatMap) {\n", indent.c_str());
197                 fprintf(out,
198                         "%s                    floatMap = new android.util.SparseArray<>();\n", indent.c_str());
199                 fprintf(out,
200                         "%s                }\n", indent.c_str());
201                 fprintf(out,
202                         "%s                floatMap.put(key, (Float) value);\n", indent.c_str());
203                 fprintf(out,
204                         "%s            }\n", indent.c_str());
205                 fprintf(out,
206                         "%s        }\n", indent.c_str());
207                 fprintf(out,
208                         "%s        builder.writeKeyValuePairs("
209                         "intMap, longMap, stringMap, floatMap);\n", indent.c_str());
210                 break;
211             default:
212                 // Unsupported types: OBJECT, DOUBLE.
213                 fprintf(stderr, "Encountered unsupported type.");
214                 return 1;
215             }
216             argIndex++;
217         }
218 
219         fprintf(out, "\n");
220         fprintf(out, "%s        builder.usePooledBuffer();\n", indent.c_str());
221         fprintf(out, "%s        StatsLog.write(builder.build());\n", indent.c_str());
222 
223         // Add support for writing using Q schema if this is not the default module.
224         if (supportQ) {
225             fprintf(out, "        } else {\n");
226             fprintf(out, "            QLogger.write(code");
227             argIndex = 1;
228             for (vector<java_type_t>::const_iterator arg = signature.begin();
229                 arg != signature.end(); arg++) {
230                 if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
231                     const char* uidName = attributionDecl.fields.front().name.c_str();
232                     const char* tagName = attributionDecl.fields.back().name.c_str();
233                     fprintf(out, ", %s, %s", uidName, tagName);
234                 } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
235                     // Module logging does not yet support key value pair.
236                     fprintf(stderr, "Module logging does not yet support key value pair.\n");
237                     return 1;
238                 } else {
239                     fprintf(out, ", arg%d", argIndex);
240                 }
241                 argIndex++;
242             }
243             fprintf(out, ");\n");
244             fprintf(out, "        }\n"); // if
245         }
246 
247         fprintf(out, "    }\n"); // method
248         fprintf(out, "\n");
249     }
250     return 0;
251 
252 }
253 
write_stats_log_java(FILE * out,const Atoms & atoms,const AtomDecl & attributionDecl,const string & moduleName,const string & javaClass,const string & javaPackage,const bool supportQ,const bool supportWorkSource)254 int write_stats_log_java(FILE* out, const Atoms& atoms, const AtomDecl &attributionDecl,
255                                     const string& moduleName, const string& javaClass,
256                                     const string& javaPackage, const bool supportQ,
257                                     const bool supportWorkSource) {
258     // Print prelude
259     fprintf(out, "// This file is autogenerated\n");
260     fprintf(out, "\n");
261     fprintf(out, "package %s;\n", javaPackage.c_str());
262     fprintf(out, "\n");
263     fprintf(out, "\n");
264     if (supportQ) {
265         fprintf(out, "import android.os.Build;\n");
266         fprintf(out, "import android.os.SystemClock;\n");
267     }
268 
269     fprintf(out, "import android.util.StatsEvent;\n");
270     fprintf(out, "import android.util.StatsLog;\n");
271 
272     fprintf(out, "\n");
273     fprintf(out, "\n");
274     fprintf(out, "/**\n");
275     fprintf(out, " * Utility class for logging statistics events.\n");
276     if (DEFAULT_MODULE_NAME == moduleName) {
277         fprintf(out, " * @hide\n");
278     }
279     fprintf(out, " */\n");
280     fprintf(out, "public class %s {\n", javaClass.c_str());
281 
282     write_java_atom_codes(out, atoms, moduleName);
283     write_java_enum_values(out, atoms, moduleName);
284 
285     int errors = 0;
286 
287     // Print write methods.
288     fprintf(out, "    // Write methods\n");
289     errors += write_java_methods(
290             out, atoms.signatures_to_modules, attributionDecl, moduleName, supportQ);
291     errors += write_java_non_chained_methods(
292             out, atoms.non_chained_signatures_to_modules, moduleName);
293     if (supportWorkSource) {
294         errors += write_java_work_source_methods(out, atoms.signatures_to_modules, moduleName);
295     }
296 
297     if (supportQ) {
298         errors += write_java_q_logger_class(
299                 out, atoms.signatures_to_modules, attributionDecl, moduleName);
300     }
301 
302     fprintf(out, "}\n");
303 
304     return errors;
305 }
306 
307 }  // namespace stats_log_api_gen
308 }  // namespace android
309