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,set<AtomDecl>::const_iterator> * decl_map)24 static void build_non_chained_decl_map(const Atoms& atoms,
25                                 std::map<int, set<AtomDecl>::const_iterator>* decl_map) {
26     for (set<AtomDecl>::const_iterator atom = atoms.non_chained_decls.begin();
27         atom != atoms.non_chained_decls.end(); atom++) {
28         decl_map->insert(std::make_pair(atom->code, atom));
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 
atom_needed_for_module(const AtomDecl & atomDecl,const string & moduleName)101 bool atom_needed_for_module(const AtomDecl& atomDecl, const string& moduleName) {
102     if (moduleName == DEFAULT_MODULE_NAME) {
103         return true;
104     }
105     return atomDecl.hasModule && (moduleName == atomDecl.moduleName);
106 }
107 
signature_needed_for_module(const set<string> & modules,const string & moduleName)108 bool signature_needed_for_module(const set<string>& modules, const string& moduleName) {
109     if (moduleName == DEFAULT_MODULE_NAME) {
110         return true;
111     }
112     return modules.find(moduleName) != modules.end();
113 }
114 
115 // Native
116 // Writes namespaces for the cpp and header files, returning the number of namespaces written.
write_namespace(FILE * out,const string & cppNamespaces)117 void write_namespace(FILE* out, const string& cppNamespaces) {
118     vector<string> cppNamespaceVec = android::base::Split(cppNamespaces, ",");
119     for (string cppNamespace : cppNamespaceVec) {
120         fprintf(out, "namespace %s {\n", cppNamespace.c_str());
121     }
122 }
123 
124 // Writes namespace closing brackets for cpp and header files.
write_closing_namespace(FILE * out,const string & cppNamespaces)125 void write_closing_namespace(FILE* out, const string& cppNamespaces) {
126     vector<string> cppNamespaceVec = android::base::Split(cppNamespaces, ",");
127     for (auto it = cppNamespaceVec.rbegin(); it != cppNamespaceVec.rend(); ++it) {
128         fprintf(out, "} // namespace %s\n", it->c_str());
129     }
130 }
131 
write_cpp_usage(FILE * out,const string & method_name,const string & atom_code_name,const AtomDecl & atom,const AtomDecl & attributionDecl)132 static void write_cpp_usage(
133     FILE* out, const string& method_name, const string& atom_code_name,
134     const AtomDecl& atom, const AtomDecl &attributionDecl) {
135     fprintf(out, "     * Usage: %s(StatsLog.%s", method_name.c_str(),
136             atom_code_name.c_str());
137 
138     for (vector<AtomField>::const_iterator field = atom.fields.begin();
139             field != atom.fields.end(); field++) {
140         if (field->javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
141             for (auto chainField : attributionDecl.fields) {
142                 if (chainField.javaType == JAVA_TYPE_STRING) {
143                     fprintf(out, ", const std::vector<%s>& %s",
144                          cpp_type_name(chainField.javaType),
145                          chainField.name.c_str());
146                 } else {
147                     fprintf(out, ", const %s* %s, size_t %s_length",
148                          cpp_type_name(chainField.javaType),
149                          chainField.name.c_str(), chainField.name.c_str());
150                 }
151             }
152         } else if (field->javaType == JAVA_TYPE_KEY_VALUE_PAIR) {
153             fprintf(out, ", const std::map<int, int32_t>& %s_int"
154                          ", const std::map<int, int64_t>& %s_long"
155                          ", const std::map<int, char const*>& %s_str"
156                          ", const std::map<int, float>& %s_float",
157                          field->name.c_str(),
158                          field->name.c_str(),
159                          field->name.c_str(),
160                          field->name.c_str());
161         } else {
162             fprintf(out, ", %s %s", cpp_type_name(field->javaType), field->name.c_str());
163         }
164     }
165     fprintf(out, ");\n");
166 }
167 
write_native_atom_constants(FILE * out,const Atoms & atoms,const AtomDecl & attributionDecl,const string & moduleName)168 void write_native_atom_constants(FILE* out, const Atoms& atoms, const AtomDecl& attributionDecl,
169         const string& moduleName) {
170     fprintf(out, "/**\n");
171     fprintf(out, " * Constants for atom codes.\n");
172     fprintf(out, " */\n");
173     fprintf(out, "enum {\n");
174 
175     std::map<int, set<AtomDecl>::const_iterator> atom_code_to_non_chained_decl_map;
176     build_non_chained_decl_map(atoms, &atom_code_to_non_chained_decl_map);
177 
178     size_t i = 0;
179     // Print atom constants
180     for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
181         atom != atoms.decls.end(); atom++) {
182         // Skip if the atom is not needed for the module.
183         if (!atom_needed_for_module(*atom, moduleName)) {
184             continue;
185         }
186         string constant = make_constant_name(atom->name);
187         fprintf(out, "\n");
188         fprintf(out, "    /**\n");
189         fprintf(out, "     * %s %s\n", atom->message.c_str(), atom->name.c_str());
190         write_cpp_usage(out, "stats_write", constant, *atom, attributionDecl);
191 
192         auto non_chained_decl = atom_code_to_non_chained_decl_map.find(atom->code);
193         if (non_chained_decl != atom_code_to_non_chained_decl_map.end()) {
194             write_cpp_usage(out, "stats_write_non_chained", constant, *non_chained_decl->second,
195                 attributionDecl);
196         }
197         fprintf(out, "     */\n");
198         char const* const comma = (i == atoms.decls.size() - 1) ? "" : ",";
199         fprintf(out, "    %s = %d%s\n", constant.c_str(), atom->code, comma);
200         i++;
201     }
202     fprintf(out, "\n");
203     fprintf(out, "};\n");
204     fprintf(out, "\n");
205 }
206 
write_native_method_signature(FILE * out,const string & methodName,const vector<java_type_t> & signature,const AtomDecl & attributionDecl,const string & closer)207 void write_native_method_signature(FILE* out, const string& methodName,
208         const vector<java_type_t>& signature, const AtomDecl& attributionDecl,
209         const string& closer) {
210     fprintf(out, "%s(int32_t code", methodName.c_str());
211     int argIndex = 1;
212     for (vector<java_type_t>::const_iterator arg = signature.begin();
213         arg != signature.end(); arg++) {
214         if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
215             for (auto chainField : attributionDecl.fields) {
216                 if (chainField.javaType == JAVA_TYPE_STRING) {
217                         fprintf(out, ", const std::vector<%s>& %s",
218                              cpp_type_name(chainField.javaType),
219                              chainField.name.c_str());
220                 } else {
221                         fprintf(out, ", const %s* %s, size_t %s_length",
222                              cpp_type_name(chainField.javaType),
223                              chainField.name.c_str(), chainField.name.c_str());
224                 }
225             }
226         } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
227             fprintf(out, ", const std::map<int, int32_t>& arg%d_1, "
228                          "const std::map<int, int64_t>& arg%d_2, "
229                          "const std::map<int, char const*>& arg%d_3, "
230                          "const std::map<int, float>& arg%d_4",
231                          argIndex, argIndex, argIndex, argIndex);
232         } else {
233             fprintf(out, ", %s arg%d", cpp_type_name(*arg), argIndex);
234         }
235         argIndex++;
236     }
237     fprintf(out, ")%s\n", closer.c_str());
238 }
239 
write_native_method_call(FILE * out,const string & methodName,const vector<java_type_t> & signature,const AtomDecl & attributionDecl,int argIndex)240 void write_native_method_call(FILE* out, const string& methodName,
241         const vector<java_type_t>& signature, const AtomDecl& attributionDecl, int argIndex) {
242     fprintf(out, "%s(code", methodName.c_str());
243     for (vector<java_type_t>::const_iterator arg = signature.begin();
244        arg != signature.end(); arg++) {
245        if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
246            for (auto chainField : attributionDecl.fields) {
247                if (chainField.javaType == JAVA_TYPE_STRING) {
248                        fprintf(out, ", %s",
249                             chainField.name.c_str());
250                } else {
251                        fprintf(out, ",  %s,  %s_length",
252                             chainField.name.c_str(), chainField.name.c_str());
253                }
254            }
255        } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
256            fprintf(out, ", arg%d_1, arg%d_2, arg%d_3, arg%d_4", argIndex,
257                    argIndex, argIndex, argIndex);
258        } else {
259            fprintf(out, ", arg%d", argIndex);
260        }
261        argIndex++;
262     }
263     fprintf(out, ");\n");
264 }
265 
266 // Java
write_java_atom_codes(FILE * out,const Atoms & atoms,const string & moduleName)267 void write_java_atom_codes(FILE* out, const Atoms& atoms, const string& moduleName) {
268     fprintf(out, "    // Constants for atom codes.\n");
269 
270     std::map<int, set<AtomDecl>::const_iterator> atom_code_to_non_chained_decl_map;
271     build_non_chained_decl_map(atoms, &atom_code_to_non_chained_decl_map);
272 
273     // Print constants for the atom codes.
274     for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
275             atom != atoms.decls.end(); atom++) {
276         // Skip if the atom is not needed for the module.
277         if (!atom_needed_for_module(*atom, moduleName)) {
278             continue;
279         }
280         string constant = make_constant_name(atom->name);
281         fprintf(out, "\n");
282         fprintf(out, "    /**\n");
283         fprintf(out, "     * %s %s<br>\n", atom->message.c_str(), atom->name.c_str());
284         write_java_usage(out, "write", constant, *atom);
285         auto non_chained_decl = atom_code_to_non_chained_decl_map.find(atom->code);
286         if (non_chained_decl != atom_code_to_non_chained_decl_map.end()) {
287             write_java_usage(out, "write_non_chained", constant, *non_chained_decl->second);
288         }
289         if (moduleName == DEFAULT_MODULE_NAME) {
290             fprintf(out, "     * @hide\n");
291         }
292         fprintf(out, "     */\n");
293         fprintf(out, "    public static final int %s = %d;\n", constant.c_str(), atom->code);
294     }
295     fprintf(out, "\n");
296 }
297 
write_java_enum_values(FILE * out,const Atoms & atoms,const string & moduleName)298 void write_java_enum_values(FILE* out, const Atoms& atoms, const string& moduleName) {
299     fprintf(out, "    // Constants for enum values.\n\n");
300     for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
301         atom != atoms.decls.end(); atom++) {
302         // Skip if the atom is not needed for the module.
303         if (!atom_needed_for_module(*atom, moduleName)) {
304             continue;
305         }
306         for (vector<AtomField>::const_iterator field = atom->fields.begin();
307             field != atom->fields.end(); field++) {
308             if (field->javaType == JAVA_TYPE_ENUM) {
309                 fprintf(out, "    // Values for %s.%s\n", atom->message.c_str(),
310                     field->name.c_str());
311                 for (map<int, string>::const_iterator value = field->enumValues.begin();
312                     value != field->enumValues.end(); value++) {
313                     if (moduleName == DEFAULT_MODULE_NAME) {
314                         fprintf(out, "    /** @hide */\n");
315                     }
316                     fprintf(out, "    public static final int %s__%s__%s = %d;\n",
317                         make_constant_name(atom->message).c_str(),
318                         make_constant_name(field->name).c_str(),
319                         make_constant_name(value->second).c_str(),
320                         value->first);
321                 }
322                 fprintf(out, "\n");
323             }
324         }
325     }
326 }
327 
write_java_usage(FILE * out,const string & method_name,const string & atom_code_name,const AtomDecl & atom)328 void write_java_usage(FILE* out, const string& method_name, const string& atom_code_name,
329         const AtomDecl& atom) {
330     fprintf(out, "     * Usage: StatsLog.%s(StatsLog.%s",
331         method_name.c_str(), atom_code_name.c_str());
332     for (vector<AtomField>::const_iterator field = atom.fields.begin();
333         field != atom.fields.end(); field++) {
334         if (field->javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
335             fprintf(out, ", android.os.WorkSource workSource");
336         } else if (field->javaType == JAVA_TYPE_KEY_VALUE_PAIR) {
337             fprintf(out, ", android.util.SparseArray<Object> value_map");
338         } else if (field->javaType == JAVA_TYPE_BYTE_ARRAY) {
339             fprintf(out, ", byte[] %s", field->name.c_str());
340         } else {
341             fprintf(out, ", %s %s", java_type_name(field->javaType), field->name.c_str());
342         }
343     }
344     fprintf(out, ");<br>\n");
345 }
346 
write_java_non_chained_methods(FILE * out,const map<vector<java_type_t>,set<string>> & signatures_to_modules,const string & moduleName)347 int write_java_non_chained_methods(
348         FILE* out,
349         const map<vector<java_type_t>, set<string>>& signatures_to_modules,
350         const string& moduleName
351         ) {
352     for (auto signature_to_modules_it = signatures_to_modules.begin();
353             signature_to_modules_it != signatures_to_modules.end(); signature_to_modules_it++) {
354         // Skip if this signature is not needed for the module.
355         if (!signature_needed_for_module(signature_to_modules_it->second, moduleName)) {
356             continue;
357         }
358 
359         // Print method signature.
360         if (DEFAULT_MODULE_NAME == moduleName) {
361             fprintf(out, "    /** @hide */\n");
362         }
363         fprintf(out, "    public static void write_non_chained(int code");
364         vector<java_type_t> signature = signature_to_modules_it->first;
365         int argIndex = 1;
366         for (vector<java_type_t>::const_iterator arg = signature.begin();
367                 arg != signature.end(); arg++) {
368             if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
369                 // Non chained signatures should not have attribution chains.
370                 return 1;
371             } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
372                 // Module logging does not yet support key value pair.
373                 return 1;
374             } else {
375                 fprintf(out, ", %s arg%d", java_type_name(*arg), argIndex);
376             }
377             argIndex++;
378         }
379         fprintf(out, ") {\n");
380 
381         fprintf(out, "        write(code");
382         argIndex = 1;
383         for (vector<java_type_t>::const_iterator arg = signature.begin();
384                 arg != signature.end(); arg++) {
385             // First two args are uid and tag of attribution chain.
386             if (argIndex == 1) {
387                 fprintf(out, ", new int[] {arg%d}", argIndex);
388             } else if (argIndex == 2) {
389                 fprintf(out, ", new java.lang.String[] {arg%d}", argIndex);
390             } else {
391                 fprintf(out, ", arg%d", argIndex);
392             }
393             argIndex++;
394         }
395         fprintf(out, ");\n");
396         fprintf(out, "    }\n");
397         fprintf(out, "\n");
398     }
399     return 0;
400 }
401 
write_java_work_source_methods(FILE * out,const map<vector<java_type_t>,set<string>> & signatures_to_modules,const string & moduleName)402 int write_java_work_source_methods(
403         FILE* out,
404         const map<vector<java_type_t>, set<string>>& signatures_to_modules,
405         const string& moduleName
406         ) {
407     fprintf(out, "    // WorkSource methods.\n");
408     for (auto signature_to_modules_it = signatures_to_modules.begin();
409             signature_to_modules_it != signatures_to_modules.end(); signature_to_modules_it++) {
410         // Skip if this signature is not needed for the module.
411         if (!signature_needed_for_module(signature_to_modules_it->second, moduleName)) {
412             continue;
413         }
414         vector<java_type_t> signature = signature_to_modules_it->first;
415         // Determine if there is Attribution in this signature.
416         int attributionArg = -1;
417         int argIndexMax = 0;
418         for (vector<java_type_t>::const_iterator arg = signature.begin();
419                 arg != signature.end(); arg++) {
420             argIndexMax++;
421             if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
422                 if (attributionArg > -1) {
423                     fprintf(stderr, "An atom contains multiple AttributionNode fields.\n");
424                     fprintf(stderr, "This is not supported. Aborting WorkSource method writing.\n");
425                     fprintf(out, "\n// Invalid for WorkSource: more than one attribution chain.\n");
426                     return 1;
427                 }
428                 attributionArg = argIndexMax;
429             }
430         }
431         if (attributionArg < 0) {
432             continue;
433         }
434 
435         fprintf(out, "\n");
436         // Method header (signature)
437         if (DEFAULT_MODULE_NAME == moduleName) {
438             fprintf(out, "    /** @hide */\n");
439         }
440         fprintf(out, "    public static void write(int code");
441         int argIndex = 1;
442         for (vector<java_type_t>::const_iterator arg = signature.begin();
443                 arg != signature.end(); arg++) {
444             if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
445                 fprintf(out, ", android.os.WorkSource ws");
446             } else {
447                 fprintf(out, ", %s arg%d", java_type_name(*arg), argIndex);
448             }
449             argIndex++;
450         }
451         fprintf(out, ") {\n");
452 
453         // write_non_chained() component. TODO: Remove when flat uids are no longer needed.
454         fprintf(out, "        for (int i = 0; i < ws.size(); ++i) {\n");
455         fprintf(out, "            write_non_chained(code");
456         for (int argIndex = 1; argIndex <= argIndexMax; argIndex++) {
457             if (argIndex == attributionArg) {
458                 fprintf(out, ", ws.get(i), ws.getName(i)");
459             } else {
460                fprintf(out, ", arg%d", argIndex);
461             }
462         }
463         fprintf(out, ");\n");
464         fprintf(out, "        }\n"); // close for-loop
465 
466         // write() component.
467         fprintf(out, "        java.util.ArrayList<android.os.WorkSource.WorkChain> workChains = "
468                 "ws.getWorkChains();\n");
469         fprintf(out, "        if (workChains != null) {\n");
470         fprintf(out, "            for (android.os.WorkSource.WorkChain wc : workChains) {\n");
471         fprintf(out, "                write(code");
472         for (int argIndex = 1; argIndex <= argIndexMax; argIndex++) {
473             if (argIndex == attributionArg) {
474                 fprintf(out, ", wc.getUids(), wc.getTags()");
475             } else {
476                fprintf(out, ", arg%d", argIndex);
477             }
478         }
479         fprintf(out, ");\n");
480         fprintf(out, "            }\n"); // close for-loop
481         fprintf(out, "        }\n"); // close if
482         fprintf(out, "    }\n"); // close method
483     }
484     return 0;
485 }
486 
487 }  // namespace stats_log_api_gen
488 }  // namespace android
489