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 "utils.h"
18 
19 #include "android-base/strings.h"
20 
21 namespace android {
22 namespace stats_log_api_gen {
23 
build_non_chained_decl_map(const Atoms & atoms,std::map<int,AtomDeclSet::const_iterator> * decl_map)24 static void build_non_chained_decl_map(const Atoms& atoms,
25                                        std::map<int, AtomDeclSet::const_iterator>* decl_map) {
26     for (AtomDeclSet::const_iterator atomIt = atoms.non_chained_decls.begin();
27          atomIt != atoms.non_chained_decls.end(); atomIt++) {
28         decl_map->insert(std::make_pair((*atomIt)->code, atomIt));
29     }
30 }
31 
32 /**
33  * Turn lower and camel case into upper case with underscores.
34  */
make_constant_name(const string & str)35 string make_constant_name(const string& str) {
36     string result;
37     const int N = str.size();
38     bool underscore_next = false;
39     for (int i = 0; i < N; i++) {
40         char c = str[i];
41         if (c >= 'A' && c <= 'Z') {
42             if (underscore_next) {
43                 result += '_';
44                 underscore_next = false;
45             }
46         } else if (c >= 'a' && c <= 'z') {
47             c = 'A' + c - 'a';
48             underscore_next = true;
49         } else if (c == '_') {
50             underscore_next = false;
51         }
52         result += c;
53     }
54     return result;
55 }
56 
cpp_type_name(java_type_t type)57 const char* cpp_type_name(java_type_t type) {
58     switch (type) {
59         case JAVA_TYPE_BOOLEAN:
60             return "bool";
61         case JAVA_TYPE_INT:
62         case JAVA_TYPE_ENUM:
63             return "int32_t";
64         case JAVA_TYPE_LONG:
65             return "int64_t";
66         case JAVA_TYPE_FLOAT:
67             return "float";
68         case JAVA_TYPE_DOUBLE:
69             return "double";
70         case JAVA_TYPE_STRING:
71             return "char const*";
72         case JAVA_TYPE_BYTE_ARRAY:
73             return "const BytesField&";
74         default:
75             return "UNKNOWN";
76     }
77 }
78 
java_type_name(java_type_t type)79 const char* java_type_name(java_type_t type) {
80     switch (type) {
81         case JAVA_TYPE_BOOLEAN:
82             return "boolean";
83         case JAVA_TYPE_INT:
84         case JAVA_TYPE_ENUM:
85             return "int";
86         case JAVA_TYPE_LONG:
87             return "long";
88         case JAVA_TYPE_FLOAT:
89             return "float";
90         case JAVA_TYPE_DOUBLE:
91             return "double";
92         case JAVA_TYPE_STRING:
93             return "java.lang.String";
94         case JAVA_TYPE_BYTE_ARRAY:
95             return "byte[]";
96         default:
97             return "UNKNOWN";
98     }
99 }
100 
101 // Native
102 // Writes namespaces for the cpp and header files, returning the number of
103 // namespaces written.
write_namespace(FILE * out,const string & cppNamespaces)104 void write_namespace(FILE* out, const string& cppNamespaces) {
105     vector<string> cppNamespaceVec = android::base::Split(cppNamespaces, ",");
106     for (string cppNamespace : cppNamespaceVec) {
107         fprintf(out, "namespace %s {\n", cppNamespace.c_str());
108     }
109 }
110 
111 // Writes namespace closing brackets for cpp and header files.
write_closing_namespace(FILE * out,const string & cppNamespaces)112 void write_closing_namespace(FILE* out, const string& cppNamespaces) {
113     vector<string> cppNamespaceVec = android::base::Split(cppNamespaces, ",");
114     for (auto it = cppNamespaceVec.rbegin(); it != cppNamespaceVec.rend(); ++it) {
115         fprintf(out, "} // namespace %s\n", it->c_str());
116     }
117 }
118 
write_cpp_usage(FILE * out,const string & method_name,const string & atom_code_name,const shared_ptr<AtomDecl> atom,const AtomDecl & attributionDecl)119 static void write_cpp_usage(FILE* out, const string& method_name, const string& atom_code_name,
120                             const shared_ptr<AtomDecl> atom, const AtomDecl& attributionDecl) {
121     fprintf(out, "     * Usage: %s(StatsLog.%s", method_name.c_str(), atom_code_name.c_str());
122 
123     for (vector<AtomField>::const_iterator field = atom->fields.begin();
124          field != atom->fields.end(); field++) {
125         if (field->javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
126             for (auto chainField : attributionDecl.fields) {
127                 if (chainField.javaType == JAVA_TYPE_STRING) {
128                     fprintf(out, ", const std::vector<%s>& %s", cpp_type_name(chainField.javaType),
129                             chainField.name.c_str());
130                 } else {
131                     fprintf(out, ", const %s* %s, size_t %s_length",
132                             cpp_type_name(chainField.javaType), chainField.name.c_str(),
133                             chainField.name.c_str());
134                 }
135             }
136         } else if (field->javaType == JAVA_TYPE_KEY_VALUE_PAIR) {
137             fprintf(out,
138                     ", const std::map<int, int32_t>& %s_int"
139                     ", const std::map<int, int64_t>& %s_long"
140                     ", const std::map<int, char const*>& %s_str"
141                     ", const std::map<int, float>& %s_float",
142                     field->name.c_str(), field->name.c_str(), field->name.c_str(),
143                     field->name.c_str());
144         } else {
145             fprintf(out, ", %s %s", cpp_type_name(field->javaType), field->name.c_str());
146         }
147     }
148     fprintf(out, ");\n");
149 }
150 
write_native_atom_constants(FILE * out,const Atoms & atoms,const AtomDecl & attributionDecl)151 void write_native_atom_constants(FILE* out, const Atoms& atoms, const AtomDecl& attributionDecl) {
152     fprintf(out, "/**\n");
153     fprintf(out, " * Constants for atom codes.\n");
154     fprintf(out, " */\n");
155     fprintf(out, "enum {\n");
156 
157     std::map<int, AtomDeclSet::const_iterator> atom_code_to_non_chained_decl_map;
158     build_non_chained_decl_map(atoms, &atom_code_to_non_chained_decl_map);
159 
160     size_t i = 0;
161     // Print atom constants
162     for (AtomDeclSet::const_iterator atomIt = atoms.decls.begin(); atomIt != atoms.decls.end();
163          atomIt++) {
164         string constant = make_constant_name((*atomIt)->name);
165         fprintf(out, "\n");
166         fprintf(out, "    /**\n");
167         fprintf(out, "     * %s %s\n", (*atomIt)->message.c_str(), (*atomIt)->name.c_str());
168         write_cpp_usage(out, "stats_write", constant, *atomIt, attributionDecl);
169 
170         auto non_chained_decl = atom_code_to_non_chained_decl_map.find((*atomIt)->code);
171         if (non_chained_decl != atom_code_to_non_chained_decl_map.end()) {
172             write_cpp_usage(out, "stats_write_non_chained", constant, *non_chained_decl->second,
173                             attributionDecl);
174         }
175         fprintf(out, "     */\n");
176         char const* const comma = (i == atoms.decls.size() - 1) ? "" : ",";
177         fprintf(out, "    %s = %d%s\n", constant.c_str(), (*atomIt)->code, comma);
178         i++;
179     }
180     fprintf(out, "\n");
181     fprintf(out, "};\n");
182     fprintf(out, "\n");
183 }
184 
write_native_method_signature(FILE * out,const string & methodName,const vector<java_type_t> & signature,const AtomDecl & attributionDecl,const string & closer)185 void write_native_method_signature(FILE* out, const string& methodName,
186                                    const vector<java_type_t>& signature,
187                                    const AtomDecl& attributionDecl, const string& closer) {
188     fprintf(out, "%s(int32_t code", methodName.c_str());
189     int argIndex = 1;
190     for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
191          arg++) {
192         if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
193             for (auto chainField : attributionDecl.fields) {
194                 if (chainField.javaType == JAVA_TYPE_STRING) {
195                     fprintf(out, ", const std::vector<%s>& %s", cpp_type_name(chainField.javaType),
196                             chainField.name.c_str());
197                 } else {
198                     fprintf(out, ", const %s* %s, size_t %s_length",
199                             cpp_type_name(chainField.javaType), chainField.name.c_str(),
200                             chainField.name.c_str());
201                 }
202             }
203         } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
204             fprintf(out,
205                     ", const std::map<int, int32_t>& arg%d_1, "
206                     "const std::map<int, int64_t>& arg%d_2, "
207                     "const std::map<int, char const*>& arg%d_3, "
208                     "const std::map<int, float>& arg%d_4",
209                     argIndex, argIndex, argIndex, argIndex);
210         } else {
211             fprintf(out, ", %s arg%d", cpp_type_name(*arg), argIndex);
212         }
213         argIndex++;
214     }
215     fprintf(out, ")%s\n", closer.c_str());
216 }
217 
write_native_method_call(FILE * out,const string & methodName,const vector<java_type_t> & signature,const AtomDecl & attributionDecl,int argIndex)218 void write_native_method_call(FILE* out, const string& methodName,
219                               const vector<java_type_t>& signature, const AtomDecl& attributionDecl,
220                               int argIndex) {
221     fprintf(out, "%s(code", methodName.c_str());
222     for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
223          arg++) {
224         if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
225             for (auto chainField : attributionDecl.fields) {
226                 if (chainField.javaType == JAVA_TYPE_STRING) {
227                     fprintf(out, ", %s", chainField.name.c_str());
228                 } else {
229                     fprintf(out, ",  %s,  %s_length", chainField.name.c_str(),
230                             chainField.name.c_str());
231                 }
232             }
233         } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
234             fprintf(out, ", arg%d_1, arg%d_2, arg%d_3, arg%d_4", argIndex, argIndex, argIndex,
235                     argIndex);
236         } else {
237             fprintf(out, ", arg%d", argIndex);
238         }
239         argIndex++;
240     }
241     fprintf(out, ");\n");
242 }
243 
244 // Java
write_java_atom_codes(FILE * out,const Atoms & atoms)245 void write_java_atom_codes(FILE* out, const Atoms& atoms) {
246     fprintf(out, "    // Constants for atom codes.\n");
247 
248     std::map<int, AtomDeclSet::const_iterator> atom_code_to_non_chained_decl_map;
249     build_non_chained_decl_map(atoms, &atom_code_to_non_chained_decl_map);
250 
251     // Print constants for the atom codes.
252     for (AtomDeclSet::const_iterator atomIt = atoms.decls.begin(); atomIt != atoms.decls.end();
253          atomIt++) {
254         string constant = make_constant_name((*atomIt)->name);
255         fprintf(out, "\n");
256         fprintf(out, "    /**\n");
257         fprintf(out, "     * %s %s<br>\n", (*atomIt)->message.c_str(), (*atomIt)->name.c_str());
258         write_java_usage(out, "write", constant, **atomIt);
259         auto non_chained_decl = atom_code_to_non_chained_decl_map.find((*atomIt)->code);
260         if (non_chained_decl != atom_code_to_non_chained_decl_map.end()) {
261             write_java_usage(out, "write_non_chained", constant, **(non_chained_decl->second));
262         }
263         fprintf(out, "     */\n");
264         fprintf(out, "    public static final int %s = %d;\n", constant.c_str(), (*atomIt)->code);
265     }
266     fprintf(out, "\n");
267 }
268 
write_java_enum_values(FILE * out,const Atoms & atoms)269 void write_java_enum_values(FILE* out, const Atoms& atoms) {
270     fprintf(out, "    // Constants for enum values.\n\n");
271     for (AtomDeclSet::const_iterator atomIt = atoms.decls.begin(); atomIt != atoms.decls.end();
272          atomIt++) {
273         for (vector<AtomField>::const_iterator field = (*atomIt)->fields.begin();
274              field != (*atomIt)->fields.end(); field++) {
275             if (field->javaType == JAVA_TYPE_ENUM) {
276                 fprintf(out, "    // Values for %s.%s\n", (*atomIt)->message.c_str(),
277                         field->name.c_str());
278                 for (map<int, string>::const_iterator value = field->enumValues.begin();
279                      value != field->enumValues.end(); value++) {
280                     fprintf(out, "    public static final int %s__%s__%s = %d;\n",
281                             make_constant_name((*atomIt)->message).c_str(),
282                             make_constant_name(field->name).c_str(),
283                             make_constant_name(value->second).c_str(), value->first);
284                 }
285                 fprintf(out, "\n");
286             }
287         }
288     }
289 }
290 
write_java_usage(FILE * out,const string & method_name,const string & atom_code_name,const AtomDecl & atom)291 void write_java_usage(FILE* out, const string& method_name, const string& atom_code_name,
292                       const AtomDecl& atom) {
293     fprintf(out, "     * Usage: StatsLog.%s(StatsLog.%s", method_name.c_str(),
294             atom_code_name.c_str());
295     for (vector<AtomField>::const_iterator field = atom.fields.begin(); field != atom.fields.end();
296          field++) {
297         if (field->javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
298             fprintf(out, ", android.os.WorkSource workSource");
299         } else if (field->javaType == JAVA_TYPE_KEY_VALUE_PAIR) {
300             fprintf(out, ", android.util.SparseArray<Object> value_map");
301         } else if (field->javaType == JAVA_TYPE_BYTE_ARRAY) {
302             fprintf(out, ", byte[] %s", field->name.c_str());
303         } else {
304             fprintf(out, ", %s %s", java_type_name(field->javaType), field->name.c_str());
305         }
306     }
307     fprintf(out, ");<br>\n");
308 }
309 
write_java_non_chained_methods(FILE * out,const SignatureInfoMap & signatureInfoMap)310 int write_java_non_chained_methods(FILE* out, const SignatureInfoMap& signatureInfoMap) {
311     for (auto signatureInfoMapIt = signatureInfoMap.begin();
312          signatureInfoMapIt != signatureInfoMap.end(); signatureInfoMapIt++) {
313         // Print method signature.
314         fprintf(out, "    public static void write_non_chained(int code");
315         vector<java_type_t> signature = signatureInfoMapIt->first;
316         int argIndex = 1;
317         for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
318              arg++) {
319             if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
320                 fprintf(stderr, "Non chained signatures should not have attribution chains.\n");
321                 return 1;
322             } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
323                 fprintf(stderr, "Module logging does not yet support key value pair.\n");
324                 return 1;
325             } else {
326                 fprintf(out, ", %s arg%d", java_type_name(*arg), argIndex);
327             }
328             argIndex++;
329         }
330         fprintf(out, ") {\n");
331 
332         fprintf(out, "        write(code");
333         argIndex = 1;
334         for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
335              arg++) {
336             // First two args are uid and tag of attribution chain.
337             if (argIndex == 1) {
338                 fprintf(out, ", new int[] {arg%d}", argIndex);
339             } else if (argIndex == 2) {
340                 fprintf(out, ", new java.lang.String[] {arg%d}", argIndex);
341             } else {
342                 fprintf(out, ", arg%d", argIndex);
343             }
344             argIndex++;
345         }
346         fprintf(out, ");\n");
347         fprintf(out, "    }\n");
348         fprintf(out, "\n");
349     }
350     return 0;
351 }
352 
write_java_work_source_methods(FILE * out,const SignatureInfoMap & signatureInfoMap)353 int write_java_work_source_methods(FILE* out, const SignatureInfoMap& signatureInfoMap) {
354     fprintf(out, "    // WorkSource methods.\n");
355     for (auto signatureInfoMapIt = signatureInfoMap.begin();
356          signatureInfoMapIt != signatureInfoMap.end(); signatureInfoMapIt++) {
357         vector<java_type_t> signature = signatureInfoMapIt->first;
358         // Determine if there is Attribution in this signature.
359         int attributionArg = -1;
360         int argIndexMax = 0;
361         for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
362              arg++) {
363             argIndexMax++;
364             if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
365                 if (attributionArg > -1) {
366                     fprintf(stderr, "An atom contains multiple AttributionNode fields.\n");
367                     fprintf(stderr, "This is not supported. Aborting WorkSource method writing.\n");
368                     fprintf(out,
369                             "\n// Invalid for WorkSource: more than one attribution "
370                             "chain.\n");
371                     return 1;
372                 }
373                 attributionArg = argIndexMax;
374             }
375         }
376         if (attributionArg < 0) {
377             continue;
378         }
379 
380         fprintf(out, "\n");
381         // Method header (signature)
382         fprintf(out, "    public static void write(int code");
383         int argIndex = 1;
384         for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
385              arg++) {
386             if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
387                 fprintf(out, ", android.os.WorkSource ws");
388             } else {
389                 fprintf(out, ", %s arg%d", java_type_name(*arg), argIndex);
390             }
391             argIndex++;
392         }
393         fprintf(out, ") {\n");
394 
395         // write_non_chained() component. TODO: Remove when flat uids are no longer
396         // needed.
397         fprintf(out, "        for (int i = 0; i < ws.size(); ++i) {\n");
398         fprintf(out, "            write_non_chained(code");
399         for (int argIndex = 1; argIndex <= argIndexMax; argIndex++) {
400             if (argIndex == attributionArg) {
401                 fprintf(out, ", ws.getUid(i), ws.getPackageName(i)");
402             } else {
403                 fprintf(out, ", arg%d", argIndex);
404             }
405         }
406         fprintf(out, ");\n");
407         fprintf(out, "        }\n");  // close for-loop
408 
409         // write() component.
410         fprintf(out,
411                 "        java.util.List<android.os.WorkSource.WorkChain> workChains = "
412                 "ws.getWorkChains();\n");
413         fprintf(out, "        if (workChains != null) {\n");
414         fprintf(out,
415                 "            for (android.os.WorkSource.WorkChain wc : workChains) "
416                 "{\n");
417         fprintf(out, "                write(code");
418         for (int argIndex = 1; argIndex <= argIndexMax; argIndex++) {
419             if (argIndex == attributionArg) {
420                 fprintf(out, ", wc.getUids(), wc.getTags()");
421             } else {
422                 fprintf(out, ", arg%d", argIndex);
423             }
424         }
425         fprintf(out, ");\n");
426         fprintf(out, "            }\n");  // close for-loop
427         fprintf(out, "        }\n");      // close if
428         fprintf(out, "    }\n");          // close method
429     }
430     return 0;
431 }
432 
433 }  // namespace stats_log_api_gen
434 }  // namespace android
435