1 
2 
3 #include "Collation.h"
4 #include "atoms_info_writer.h"
5 #if !defined(STATS_SCHEMA_LEGACY)
6 #include "java_writer.h"
7 #endif
8 #include "java_writer_q.h"
9 #include "native_writer.h"
10 #include "utils.h"
11 
12 #include "frameworks/base/cmds/statsd/src/atoms.pb.h"
13 
14 #include <map>
15 #include <set>
16 #include <vector>
17 
18 #include <getopt.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 
23 using namespace google::protobuf;
24 using namespace std;
25 
26 namespace android {
27 namespace stats_log_api_gen {
28 
29 using android::os::statsd::Atom;
30 
31 // Hide the JNI write helpers that are not used in the new schema.
32 // TODO(b/145100015): Remove this and other JNI related functionality once StatsEvent migration is
33 // complete.
34 #if defined(STATS_SCHEMA_LEGACY)
35 // JNI helpers.
36 static const char*
jni_type_name(java_type_t type)37 jni_type_name(java_type_t type)
38 {
39     switch (type) {
40         case JAVA_TYPE_BOOLEAN:
41             return "jboolean";
42         case JAVA_TYPE_INT:
43         case JAVA_TYPE_ENUM:
44             return "jint";
45         case JAVA_TYPE_LONG:
46             return "jlong";
47         case JAVA_TYPE_FLOAT:
48             return "jfloat";
49         case JAVA_TYPE_DOUBLE:
50             return "jdouble";
51         case JAVA_TYPE_STRING:
52             return "jstring";
53         case JAVA_TYPE_BYTE_ARRAY:
54             return "jbyteArray";
55         default:
56             return "UNKNOWN";
57     }
58 }
59 
60 static const char*
jni_array_type_name(java_type_t type)61 jni_array_type_name(java_type_t type)
62 {
63     switch (type) {
64         case JAVA_TYPE_INT:
65             return "jintArray";
66         case JAVA_TYPE_FLOAT:
67             return "jfloatArray";
68         case JAVA_TYPE_STRING:
69             return "jobjectArray";
70         default:
71             return "UNKNOWN";
72     }
73 }
74 
75 static string
jni_function_name(const string & method_name,const vector<java_type_t> & signature)76 jni_function_name(const string& method_name, const vector<java_type_t>& signature)
77 {
78     string result("StatsLog_" + method_name);
79     for (vector<java_type_t>::const_iterator arg = signature.begin();
80         arg != signature.end(); arg++) {
81         switch (*arg) {
82             case JAVA_TYPE_BOOLEAN:
83                 result += "_boolean";
84                 break;
85             case JAVA_TYPE_INT:
86             case JAVA_TYPE_ENUM:
87                 result += "_int";
88                 break;
89             case JAVA_TYPE_LONG:
90                 result += "_long";
91                 break;
92             case JAVA_TYPE_FLOAT:
93                 result += "_float";
94                 break;
95             case JAVA_TYPE_DOUBLE:
96                 result += "_double";
97                 break;
98             case JAVA_TYPE_STRING:
99                 result += "_String";
100                 break;
101             case JAVA_TYPE_ATTRIBUTION_CHAIN:
102               result += "_AttributionChain";
103               break;
104             case JAVA_TYPE_KEY_VALUE_PAIR:
105               result += "_KeyValuePairs";
106               break;
107             case JAVA_TYPE_BYTE_ARRAY:
108                 result += "_bytes";
109                 break;
110             default:
111                 result += "_UNKNOWN";
112                 break;
113         }
114     }
115     return result;
116 }
117 
118 static const char*
java_type_signature(java_type_t type)119 java_type_signature(java_type_t type)
120 {
121     switch (type) {
122         case JAVA_TYPE_BOOLEAN:
123             return "Z";
124         case JAVA_TYPE_INT:
125         case JAVA_TYPE_ENUM:
126             return "I";
127         case JAVA_TYPE_LONG:
128             return "J";
129         case JAVA_TYPE_FLOAT:
130             return "F";
131         case JAVA_TYPE_DOUBLE:
132             return "D";
133         case JAVA_TYPE_STRING:
134             return "Ljava/lang/String;";
135         case JAVA_TYPE_BYTE_ARRAY:
136             return "[B";
137         default:
138             return "UNKNOWN";
139     }
140 }
141 
142 static string
jni_function_signature(const vector<java_type_t> & signature,const AtomDecl & attributionDecl)143 jni_function_signature(const vector<java_type_t>& signature, const AtomDecl &attributionDecl)
144 {
145     string result("(I");
146     for (vector<java_type_t>::const_iterator arg = signature.begin();
147         arg != signature.end(); arg++) {
148         if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
149             for (auto chainField : attributionDecl.fields) {
150                 result += "[";
151                 result += java_type_signature(chainField.javaType);
152             }
153         } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
154             result += "Landroid/util/SparseArray;";
155         } else {
156             result += java_type_signature(*arg);
157         }
158     }
159     result += ")I";
160     return result;
161 }
162 
write_key_value_map_jni(FILE * out)163 static void write_key_value_map_jni(FILE* out) {
164    fprintf(out, "    std::map<int, int32_t> int32_t_map;\n");
165    fprintf(out, "    std::map<int, int64_t> int64_t_map;\n");
166    fprintf(out, "    std::map<int, float> float_map;\n");
167    fprintf(out, "    std::map<int, char const*> string_map;\n\n");
168 
169    fprintf(out, "    jclass jmap_class = env->FindClass(\"android/util/SparseArray\");\n");
170 
171    fprintf(out, "    jmethodID jget_size_method = env->GetMethodID(jmap_class, \"size\", \"()I\");\n");
172    fprintf(out, "    jmethodID jget_key_method = env->GetMethodID(jmap_class, \"keyAt\", \"(I)I\");\n");
173    fprintf(out, "    jmethodID jget_value_method = env->GetMethodID(jmap_class, \"valueAt\", \"(I)Ljava/lang/Object;\");\n\n");
174 
175 
176    fprintf(out, "    std::vector<std::unique_ptr<ScopedUtfChars>> scoped_ufs;\n\n");
177 
178    fprintf(out, "    jclass jint_class = env->FindClass(\"java/lang/Integer\");\n");
179    fprintf(out, "    jclass jlong_class = env->FindClass(\"java/lang/Long\");\n");
180    fprintf(out, "    jclass jfloat_class = env->FindClass(\"java/lang/Float\");\n");
181    fprintf(out, "    jclass jstring_class = env->FindClass(\"java/lang/String\");\n");
182    fprintf(out, "    jmethodID jget_int_method = env->GetMethodID(jint_class, \"intValue\", \"()I\");\n");
183    fprintf(out, "    jmethodID jget_long_method = env->GetMethodID(jlong_class, \"longValue\", \"()J\");\n");
184    fprintf(out, "    jmethodID jget_float_method = env->GetMethodID(jfloat_class, \"floatValue\", \"()F\");\n\n");
185 
186    fprintf(out, "    jint jsize = env->CallIntMethod(value_map, jget_size_method);\n");
187    fprintf(out, "    for(int i = 0; i < jsize; i++) {\n");
188    fprintf(out, "        jint key = env->CallIntMethod(value_map, jget_key_method, i);\n");
189    fprintf(out, "        jobject jvalue_obj = env->CallObjectMethod(value_map, jget_value_method, i);\n");
190    fprintf(out, "        if (jvalue_obj == NULL) { continue; }\n");
191    fprintf(out, "        if (env->IsInstanceOf(jvalue_obj, jint_class)) {\n");
192    fprintf(out, "            int32_t_map[key] = env->CallIntMethod(jvalue_obj, jget_int_method);\n");
193    fprintf(out, "        } else if (env->IsInstanceOf(jvalue_obj, jlong_class)) {\n");
194    fprintf(out, "            int64_t_map[key] = env->CallLongMethod(jvalue_obj, jget_long_method);\n");
195    fprintf(out, "        } else if (env->IsInstanceOf(jvalue_obj, jfloat_class)) {\n");
196    fprintf(out, "            float_map[key] = env->CallFloatMethod(jvalue_obj, jget_float_method);\n");
197    fprintf(out, "        } else if (env->IsInstanceOf(jvalue_obj, jstring_class)) {\n");
198    fprintf(out, "            std::unique_ptr<ScopedUtfChars> utf(new ScopedUtfChars(env, (jstring)jvalue_obj));\n");
199    fprintf(out, "            if (utf->c_str() != NULL) { string_map[key] = utf->c_str(); }\n");
200    fprintf(out, "            scoped_ufs.push_back(std::move(utf));\n");
201    fprintf(out, "        }\n");
202    fprintf(out, "    }\n");
203 }
204 
205 static int
write_stats_log_jni_method(FILE * out,const string & java_method_name,const string & cpp_method_name,const map<vector<java_type_t>,set<string>> & signatures_to_modules,const AtomDecl & attributionDecl)206 write_stats_log_jni_method(FILE* out, const string& java_method_name, const string& cpp_method_name,
207         const map<vector<java_type_t>, set<string>>& signatures_to_modules,
208         const AtomDecl &attributionDecl) {
209     // Print write methods
210     for (auto signature_to_modules_it = signatures_to_modules.begin();
211             signature_to_modules_it != signatures_to_modules.end(); signature_to_modules_it++) {
212         vector<java_type_t> signature = signature_to_modules_it->first;
213         int argIndex;
214 
215         fprintf(out, "static int\n");
216         fprintf(out, "%s(JNIEnv* env, jobject clazz UNUSED, jint code",
217                 jni_function_name(java_method_name, signature).c_str());
218         argIndex = 1;
219         for (vector<java_type_t>::const_iterator arg = signature.begin();
220                 arg != signature.end(); arg++) {
221             if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
222                 for (auto chainField : attributionDecl.fields) {
223                     fprintf(out, ", %s %s", jni_array_type_name(chainField.javaType),
224                         chainField.name.c_str());
225                 }
226             } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
227                 fprintf(out, ", jobject value_map");
228             } else {
229                 fprintf(out, ", %s arg%d", jni_type_name(*arg), argIndex);
230             }
231             argIndex++;
232         }
233         fprintf(out, ")\n");
234 
235         fprintf(out, "{\n");
236 
237         // Prepare strings
238         argIndex = 1;
239         bool hadStringOrChain = false;
240         bool isKeyValuePairAtom = false;
241         for (vector<java_type_t>::const_iterator arg = signature.begin();
242                 arg != signature.end(); arg++) {
243             if (*arg == JAVA_TYPE_STRING) {
244                 hadStringOrChain = true;
245                 fprintf(out, "    const char* str%d;\n", argIndex);
246                 fprintf(out, "    if (arg%d != NULL) {\n", argIndex);
247                 fprintf(out, "        str%d = env->GetStringUTFChars(arg%d, NULL);\n",
248                         argIndex, argIndex);
249                 fprintf(out, "    } else {\n");
250                 fprintf(out, "        str%d = NULL;\n", argIndex);
251                 fprintf(out, "    }\n");
252             } else if (*arg == JAVA_TYPE_BYTE_ARRAY) {
253                 hadStringOrChain = true;
254                 fprintf(out, "    jbyte* jbyte_array%d;\n", argIndex);
255                 fprintf(out, "    const char* str%d;\n", argIndex);
256                 fprintf(out, "    int str%d_length = 0;\n", argIndex);
257                 fprintf(out,
258                         "    if (arg%d != NULL && env->GetArrayLength(arg%d) > "
259                         "0) {\n",
260                         argIndex, argIndex);
261                 fprintf(out,
262                         "        jbyte_array%d = "
263                         "env->GetByteArrayElements(arg%d, NULL);\n",
264                         argIndex, argIndex);
265                 fprintf(out,
266                         "        str%d_length = env->GetArrayLength(arg%d);\n",
267                         argIndex, argIndex);
268                 fprintf(out,
269                         "        str%d = "
270                         "reinterpret_cast<char*>(env->GetByteArrayElements(arg%"
271                         "d, NULL));\n",
272                         argIndex, argIndex);
273                 fprintf(out, "    } else {\n");
274                 fprintf(out, "        jbyte_array%d = NULL;\n", argIndex);
275                 fprintf(out, "        str%d = NULL;\n", argIndex);
276                 fprintf(out, "    }\n");
277 
278                 fprintf(out,
279                         "    android::util::BytesField bytesField%d(str%d, "
280                         "str%d_length);",
281                         argIndex, argIndex, argIndex);
282 
283             } else if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
284                 hadStringOrChain = true;
285                 for (auto chainField : attributionDecl.fields) {
286                     fprintf(out, "    size_t %s_length = env->GetArrayLength(%s);\n",
287                         chainField.name.c_str(), chainField.name.c_str());
288                     if (chainField.name != attributionDecl.fields.front().name) {
289                         fprintf(out, "    if (%s_length != %s_length) {\n",
290                             chainField.name.c_str(),
291                             attributionDecl.fields.front().name.c_str());
292                         fprintf(out, "        return -EINVAL;\n");
293                         fprintf(out, "    }\n");
294                     }
295                     if (chainField.javaType == JAVA_TYPE_INT) {
296                         fprintf(out, "    jint* %s_array = env->GetIntArrayElements(%s, NULL);\n",
297                             chainField.name.c_str(), chainField.name.c_str());
298                     } else if (chainField.javaType == JAVA_TYPE_STRING) {
299                         fprintf(out, "    std::vector<%s> %s_vec;\n",
300                             cpp_type_name(chainField.javaType), chainField.name.c_str());
301                         fprintf(out, "    std::vector<ScopedUtfChars*> scoped_%s_vec;\n",
302                             chainField.name.c_str());
303                         fprintf(out, "    for (size_t i = 0; i < %s_length; ++i) {\n",
304                             chainField.name.c_str());
305                         fprintf(out, "        jstring jstr = "
306                             "(jstring)env->GetObjectArrayElement(%s, i);\n",
307                              chainField.name.c_str());
308                         fprintf(out, "        if (jstr == NULL) {\n");
309                         fprintf(out, "            %s_vec.push_back(NULL);\n",
310                             chainField.name.c_str());
311                         fprintf(out, "        } else {\n");
312                         fprintf(out, "            ScopedUtfChars* scoped_%s = "
313                             "new ScopedUtfChars(env, jstr);\n",
314                              chainField.name.c_str());
315                         fprintf(out, "            %s_vec.push_back(scoped_%s->c_str());\n",
316                                 chainField.name.c_str(), chainField.name.c_str());
317                         fprintf(out, "            scoped_%s_vec.push_back(scoped_%s);\n",
318                                 chainField.name.c_str(), chainField.name.c_str());
319                         fprintf(out, "        }\n");
320                         fprintf(out, "    }\n");
321                     }
322                     fprintf(out, "\n");
323                 }
324             } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
325                 isKeyValuePairAtom = true;
326             }
327             argIndex++;
328         }
329         // Emit this to quiet the unused parameter warning if there were no strings or attribution
330         // chains.
331         if (!hadStringOrChain && !isKeyValuePairAtom) {
332             fprintf(out, "    (void)env;\n");
333         }
334         if (isKeyValuePairAtom) {
335             write_key_value_map_jni(out);
336         }
337 
338         // stats_write call
339         argIndex = 1;
340         fprintf(out, "\n    int ret =  android::util::%s(code",
341                 cpp_method_name.c_str());
342         for (vector<java_type_t>::const_iterator arg = signature.begin();
343                 arg != signature.end(); arg++) {
344             if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
345                 for (auto chainField : attributionDecl.fields) {
346                     if (chainField.javaType == JAVA_TYPE_INT) {
347                         fprintf(out, ", (const %s*)%s_array, %s_length",
348                             cpp_type_name(chainField.javaType),
349                             chainField.name.c_str(), chainField.name.c_str());
350                     } else if (chainField.javaType == JAVA_TYPE_STRING) {
351                         fprintf(out, ", %s_vec", chainField.name.c_str());
352                     }
353                 }
354             } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
355                 fprintf(out, ", int32_t_map, int64_t_map, string_map, float_map");
356             } else if (*arg == JAVA_TYPE_BYTE_ARRAY) {
357                 fprintf(out, ", bytesField%d", argIndex);
358             } else {
359                 const char* argName =
360                         (*arg == JAVA_TYPE_STRING) ? "str" : "arg";
361                 fprintf(out, ", (%s)%s%d", cpp_type_name(*arg), argName, argIndex);
362             }
363             argIndex++;
364         }
365         fprintf(out, ");\n");
366         fprintf(out, "\n");
367 
368         // Clean up strings
369         argIndex = 1;
370         for (vector<java_type_t>::const_iterator arg = signature.begin();
371                 arg != signature.end(); arg++) {
372             if (*arg == JAVA_TYPE_STRING) {
373                 fprintf(out, "    if (str%d != NULL) {\n", argIndex);
374                 fprintf(out, "        env->ReleaseStringUTFChars(arg%d, str%d);\n",
375                         argIndex, argIndex);
376                 fprintf(out, "    }\n");
377             } else if (*arg == JAVA_TYPE_BYTE_ARRAY) {
378                 fprintf(out, "    if (str%d != NULL) { \n", argIndex);
379                 fprintf(out,
380                         "        env->ReleaseByteArrayElements(arg%d, "
381                         "jbyte_array%d, 0);\n",
382                         argIndex, argIndex);
383                 fprintf(out, "    }\n");
384             } else if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
385                 for (auto chainField : attributionDecl.fields) {
386                     if (chainField.javaType == JAVA_TYPE_INT) {
387                         fprintf(out, "    env->ReleaseIntArrayElements(%s, %s_array, 0);\n",
388                             chainField.name.c_str(), chainField.name.c_str());
389                     } else if (chainField.javaType == JAVA_TYPE_STRING) {
390                         fprintf(out, "    for (size_t i = 0; i < scoped_%s_vec.size(); ++i) {\n",
391                             chainField.name.c_str());
392                         fprintf(out, "        delete scoped_%s_vec[i];\n", chainField.name.c_str());
393                         fprintf(out, "    }\n");
394                     }
395                 }
396             }
397             argIndex++;
398         }
399 
400         fprintf(out, "    return ret;\n");
401 
402         fprintf(out, "}\n");
403         fprintf(out, "\n");
404     }
405 
406 
407     return 0;
408 }
409 
write_jni_registration(FILE * out,const string & java_method_name,const map<vector<java_type_t>,set<string>> & signatures_to_modules,const AtomDecl & attributionDecl)410 void write_jni_registration(FILE* out, const string& java_method_name,
411         const map<vector<java_type_t>, set<string>>& signatures_to_modules,
412         const AtomDecl &attributionDecl) {
413     for (auto signature_to_modules_it = signatures_to_modules.begin();
414             signature_to_modules_it != signatures_to_modules.end(); signature_to_modules_it++) {
415         vector<java_type_t> signature = signature_to_modules_it->first;
416         fprintf(out, "    { \"%s\", \"%s\", (void*)%s },\n",
417             java_method_name.c_str(),
418             jni_function_signature(signature, attributionDecl).c_str(),
419             jni_function_name(java_method_name, signature).c_str());
420     }
421 }
422 #endif // JNI helpers.
423 
424 static int
425 #if defined(STATS_SCHEMA_LEGACY)
write_stats_log_jni(FILE * out,const Atoms & atoms,const AtomDecl & attributionDecl)426 write_stats_log_jni(FILE* out, const Atoms& atoms, const AtomDecl &attributionDecl)
427 #else
428 // Write empty JNI file that doesn't contain any JNI methods.
429 // TODO(b/145100015): remove this function and all JNI autogen code once StatsEvent migration is
430 // complete.
431 write_stats_log_jni(FILE* out)
432 #endif
433 {
434     // Print prelude
435     fprintf(out, "// This file is autogenerated\n");
436     fprintf(out, "\n");
437 
438 #if defined(STATS_SCHEMA_LEGACY)
439     fprintf(out, "#include <statslog.h>\n");
440     fprintf(out, "\n");
441     fprintf(out, "#include <nativehelper/JNIHelp.h>\n");
442     fprintf(out, "#include <nativehelper/ScopedUtfChars.h>\n");
443     fprintf(out, "#include <utils/Vector.h>\n");
444 #endif
445     fprintf(out, "#include \"core_jni_helpers.h\"\n");
446     fprintf(out, "#include \"jni.h\"\n");
447     fprintf(out, "\n");
448 #if defined(STATS_SCHEMA_LEGACY)
449     fprintf(out, "#define UNUSED  __attribute__((__unused__))\n");
450     fprintf(out, "\n");
451 #endif
452 
453     fprintf(out, "namespace android {\n");
454     fprintf(out, "\n");
455 
456 #if defined(STATS_SCHEMA_LEGACY)
457     write_stats_log_jni_method(out, "write", "stats_write", atoms.signatures_to_modules, attributionDecl);
458     write_stats_log_jni_method(out, "write_non_chained", "stats_write_non_chained",
459             atoms.non_chained_signatures_to_modules, attributionDecl);
460 #endif
461 
462     // Print registration function table
463     fprintf(out, "/*\n");
464     fprintf(out, " * JNI registration.\n");
465     fprintf(out, " */\n");
466     fprintf(out, "static const JNINativeMethod gRegisterMethods[] = {\n");
467 #if defined(STATS_SCHEMA_LEGACY)
468     write_jni_registration(out, "write", atoms.signatures_to_modules, attributionDecl);
469     write_jni_registration(out, "write_non_chained", atoms.non_chained_signatures_to_modules,
470             attributionDecl);
471 #endif
472     fprintf(out, "};\n");
473     fprintf(out, "\n");
474 
475     // Print registration function
476     fprintf(out, "int register_android_util_StatsLogInternal(JNIEnv* env) {\n");
477     fprintf(out, "    return RegisterMethodsOrDie(\n");
478     fprintf(out, "            env,\n");
479     fprintf(out, "            \"android/util/StatsLogInternal\",\n");
480     fprintf(out, "            gRegisterMethods, NELEM(gRegisterMethods));\n");
481     fprintf(out, "}\n");
482 
483     fprintf(out, "\n");
484     fprintf(out, "} // namespace android\n");
485     return 0;
486 }
487 
488 static void
print_usage()489 print_usage()
490 {
491     fprintf(stderr, "usage: stats-log-api-gen OPTIONS\n");
492     fprintf(stderr, "\n");
493     fprintf(stderr, "OPTIONS\n");
494     fprintf(stderr, "  --cpp FILENAME       the header file to output for write helpers\n");
495     fprintf(stderr, "  --header FILENAME    the cpp file to output for write helpers\n");
496     fprintf(stderr,
497             "  --atomsInfoCpp FILENAME       the header file to output for statsd metadata\n");
498     fprintf(stderr, "  --atomsInfoHeader FILENAME    the cpp file to output for statsd metadata\n");
499     fprintf(stderr, "  --help               this message\n");
500     fprintf(stderr, "  --java FILENAME      the java file to output\n");
501     fprintf(stderr, "  --jni FILENAME       the jni file to output\n");
502     fprintf(stderr, "  --module NAME        optional, module name to generate outputs for\n");
503     fprintf(stderr, "  --namespace COMMA,SEP,NAMESPACE   required for cpp/header with module\n");
504     fprintf(stderr, "                                    comma separated namespace of the files\n");
505     fprintf(stderr,"  --importHeader NAME  required for cpp/jni to say which header to import "
506             "for write helpers\n");
507     fprintf(stderr,"  --atomsInfoImportHeader NAME  required for cpp to say which header to import "
508             "for statsd metadata\n");
509     fprintf(stderr, "  --javaPackage PACKAGE             the package for the java file.\n");
510     fprintf(stderr, "                                    required for java with module\n");
511     fprintf(stderr, "  --javaClass CLASS    the class name of the java class.\n");
512     fprintf(stderr, "                       Optional for Java with module.\n");
513     fprintf(stderr, "                       Default is \"StatsLogInternal\"\n");
514     fprintf(stderr, "  --supportQ           Include runtime support for Android Q.\n");
515     fprintf(stderr, "  --worksource         Include support for logging WorkSource objects.\n");
516     fprintf(stderr, "  --compileQ           Include compile-time support for Android Q "
517             "(Java only).\n");
518 }
519 
520 /**
521  * Do the argument parsing and execute the tasks.
522  */
523 static int
run(int argc,char const * const * argv)524 run(int argc, char const*const* argv)
525 {
526     string cppFilename;
527     string headerFilename;
528     string javaFilename;
529     string jniFilename;
530     string atomsInfoCppFilename;
531     string atomsInfoHeaderFilename;
532 
533     string moduleName = DEFAULT_MODULE_NAME;
534     string cppNamespace = DEFAULT_CPP_NAMESPACE;
535     string cppHeaderImport = DEFAULT_CPP_HEADER_IMPORT;
536     string atomsInfoCppHeaderImport = DEFAULT_ATOMS_INFO_CPP_HEADER_IMPORT;
537     string javaPackage = DEFAULT_JAVA_PACKAGE;
538     string javaClass = DEFAULT_JAVA_CLASS;
539     bool supportQ = false;
540     bool supportWorkSource = false;
541     bool compileQ = false;
542 
543     int index = 1;
544     while (index < argc) {
545         if (0 == strcmp("--help", argv[index])) {
546             print_usage();
547             return 0;
548         } else if (0 == strcmp("--cpp", argv[index])) {
549             index++;
550             if (index >= argc) {
551                 print_usage();
552                 return 1;
553             }
554             cppFilename = argv[index];
555         } else if (0 == strcmp("--header", argv[index])) {
556             index++;
557             if (index >= argc) {
558                 print_usage();
559                 return 1;
560             }
561             headerFilename = argv[index];
562         } else if (0 == strcmp("--java", argv[index])) {
563             index++;
564             if (index >= argc) {
565                 print_usage();
566                 return 1;
567             }
568             javaFilename = argv[index];
569         } else if (0 == strcmp("--jni", argv[index])) {
570             index++;
571             if (index >= argc) {
572                 print_usage();
573                 return 1;
574             }
575             jniFilename = argv[index];
576         } else if (0 == strcmp("--module", argv[index])) {
577             index++;
578             if (index >= argc) {
579                 print_usage();
580                 return 1;
581             }
582             moduleName = argv[index];
583         } else if (0 == strcmp("--namespace", argv[index])) {
584             index++;
585             if (index >= argc) {
586                 print_usage();
587                 return 1;
588             }
589             cppNamespace = argv[index];
590         } else if (0 == strcmp("--importHeader", argv[index])) {
591             index++;
592             if (index >= argc) {
593                 print_usage();
594                 return 1;
595             }
596             cppHeaderImport = argv[index];
597         } else if (0 == strcmp("--javaPackage", argv[index])) {
598             index++;
599             if (index >= argc) {
600                 print_usage();
601                 return 1;
602             }
603             javaPackage = argv[index];
604         } else if (0 == strcmp("--javaClass", argv[index])) {
605             index++;
606             if (index >= argc) {
607                 print_usage();
608                 return 1;
609             }
610             javaClass = argv[index];
611         } else if (0 == strcmp("--atomsInfoHeader", argv[index])) {
612             index++;
613             if (index >= argc) {
614                 print_usage();
615                 return 1;
616             }
617             atomsInfoHeaderFilename = argv[index];
618         } else if (0 == strcmp("--atomsInfoCpp", argv[index])) {
619             index++;
620             if (index >= argc) {
621                 print_usage();
622                 return 1;
623             }
624             atomsInfoCppFilename = argv[index];
625         } else if (0 == strcmp("--atomsInfoImportHeader", argv[index])) {
626             index++;
627             if (index >= argc) {
628                 print_usage();
629                 return 1;
630             }
631             atomsInfoCppHeaderImport = argv[index];
632         } else if (0 == strcmp("--supportQ", argv[index])) {
633             supportQ = true;
634         } else if (0 == strcmp("--worksource", argv[index])) {
635             supportWorkSource = true;
636         } else if (0 == strcmp("--compileQ", argv[index])) {
637             compileQ = true;
638         }
639 
640         index++;
641     }
642 
643     if (cppFilename.size() == 0
644             && headerFilename.size() == 0
645             && javaFilename.size() == 0
646             && jniFilename.size() == 0
647             && atomsInfoHeaderFilename.size() == 0
648             && atomsInfoCppFilename.size() == 0) {
649         print_usage();
650         return 1;
651     }
652 
653     if (DEFAULT_MODULE_NAME == moduleName && (supportQ || compileQ)) {
654         // Support for Q schema is not needed for default module.
655         fprintf(stderr, "%s cannot support Q schema\n", moduleName.c_str());
656         return 1;
657     }
658 
659     if (supportQ && compileQ) {
660         // Runtime Q support is redundant if compile-time Q support is required.
661         fprintf(stderr, "Cannot specify compileQ and supportQ simultaneously.\n");
662         return 1;
663     }
664 
665     // Collate the parameters
666     Atoms atoms;
667     int errorCount = collate_atoms(Atom::descriptor(), &atoms);
668     if (errorCount != 0) {
669         return 1;
670     }
671 
672     AtomDecl attributionDecl;
673     vector<java_type_t> attributionSignature;
674     collate_atom(android::os::statsd::AttributionNode::descriptor(),
675                  &attributionDecl, &attributionSignature);
676 
677     // Write the atoms info .cpp file
678     if (atomsInfoCppFilename.size() != 0) {
679         FILE* out = fopen(atomsInfoCppFilename.c_str(), "w");
680         if (out == NULL) {
681             fprintf(stderr, "Unable to open file for write: %s\n", atomsInfoCppFilename.c_str());
682             return 1;
683         }
684         errorCount = android::stats_log_api_gen::write_atoms_info_cpp(
685             out, atoms, cppNamespace, atomsInfoCppHeaderImport, cppHeaderImport);
686         fclose(out);
687     }
688 
689     // Write the atoms info .h file
690     if (atomsInfoHeaderFilename.size() != 0) {
691         FILE* out = fopen(atomsInfoHeaderFilename.c_str(), "w");
692         if (out == NULL) {
693             fprintf(stderr, "Unable to open file for write: %s\n", atomsInfoHeaderFilename.c_str());
694             return 1;
695         }
696         errorCount = android::stats_log_api_gen::write_atoms_info_header(out, atoms, cppNamespace);
697         fclose(out);
698     }
699 
700 
701     // Write the .cpp file
702     if (cppFilename.size() != 0) {
703         FILE* out = fopen(cppFilename.c_str(), "w");
704         if (out == NULL) {
705             fprintf(stderr, "Unable to open file for write: %s\n", cppFilename.c_str());
706             return 1;
707         }
708         // If this is for a specific module, the namespace must also be provided.
709         if (moduleName != DEFAULT_MODULE_NAME && cppNamespace == DEFAULT_CPP_NAMESPACE) {
710             fprintf(stderr, "Must supply --namespace if supplying a specific module\n");
711             return 1;
712         }
713         // If this is for a specific module, the header file to import must also be provided.
714         if (moduleName != DEFAULT_MODULE_NAME && cppHeaderImport == DEFAULT_CPP_HEADER_IMPORT) {
715             fprintf(stderr, "Must supply --headerImport if supplying a specific module\n");
716             return 1;
717         }
718         errorCount = android::stats_log_api_gen::write_stats_log_cpp(
719             out, atoms, attributionDecl, moduleName, cppNamespace, cppHeaderImport, supportQ);
720         fclose(out);
721     }
722 
723     // Write the .h file
724     if (headerFilename.size() != 0) {
725         FILE* out = fopen(headerFilename.c_str(), "w");
726         if (out == NULL) {
727             fprintf(stderr, "Unable to open file for write: %s\n", headerFilename.c_str());
728             return 1;
729         }
730         // If this is for a specific module, the namespace must also be provided.
731         if (moduleName != DEFAULT_MODULE_NAME && cppNamespace == DEFAULT_CPP_NAMESPACE) {
732             fprintf(stderr, "Must supply --namespace if supplying a specific module\n");
733         }
734         errorCount = android::stats_log_api_gen::write_stats_log_header(
735             out, atoms, attributionDecl, moduleName, cppNamespace);
736         fclose(out);
737     }
738 
739     // Write the .java file
740     if (javaFilename.size() != 0) {
741         FILE* out = fopen(javaFilename.c_str(), "w");
742         if (out == NULL) {
743             fprintf(stderr, "Unable to open file for write: %s\n", javaFilename.c_str());
744             return 1;
745         }
746 
747 #if defined(STATS_SCHEMA_LEGACY)
748         if (moduleName == DEFAULT_MODULE_NAME) {
749             errorCount = android::stats_log_api_gen::write_stats_log_java_q(
750                     out, atoms, attributionDecl, supportWorkSource);
751         } else {
752             errorCount = android::stats_log_api_gen::write_stats_log_java_q_for_module(
753                     out, atoms, attributionDecl, moduleName, javaClass, javaPackage,
754                     supportWorkSource);
755 
756         }
757 #else
758         if (moduleName == DEFAULT_MODULE_NAME) {
759             javaClass = "StatsLogInternal";
760             javaPackage = "android.util";
761         }
762         if (compileQ) {
763             errorCount = android::stats_log_api_gen::write_stats_log_java_q_for_module(
764                     out, atoms, attributionDecl, moduleName, javaClass, javaPackage,
765                     supportWorkSource);
766         } else {
767             errorCount = android::stats_log_api_gen::write_stats_log_java(
768                     out, atoms, attributionDecl, moduleName, javaClass, javaPackage, supportQ,
769                     supportWorkSource);
770         }
771 #endif
772 
773         fclose(out);
774     }
775 
776     // Write the jni file
777     if (jniFilename.size() != 0) {
778         FILE* out = fopen(jniFilename.c_str(), "w");
779         if (out == NULL) {
780             fprintf(stderr, "Unable to open file for write: %s\n", jniFilename.c_str());
781             return 1;
782         }
783 
784 #if defined(STATS_SCHEMA_LEGACY)
785         errorCount = android::stats_log_api_gen::write_stats_log_jni(
786             out, atoms, attributionDecl);
787 #else
788         errorCount = android::stats_log_api_gen::write_stats_log_jni(out);
789 #endif
790 
791         fclose(out);
792     }
793 
794     return errorCount;
795 }
796 
797 }  // namespace stats_log_api_gen
798 }  // namespace android
799 
800 /**
801  * Main.
802  */
803 int
main(int argc,char const * const * argv)804 main(int argc, char const*const* argv)
805 {
806     GOOGLE_PROTOBUF_VERIFY_VERSION;
807 
808     return android::stats_log_api_gen::run(argc, argv);
809 }
810