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