Revert "Revert "Start auto-generating the stats log API.""

Test: builds successfully

This reverts commit 931945399859ab91545ba2c2a914f044092d5e2e.

Change-Id: I22bca4a32adf86040b9d72ad5b45999aba28f586
diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp
new file mode 100644
index 0000000..aaea4f6
--- /dev/null
+++ b/tools/stats_log_api_gen/main.cpp
@@ -0,0 +1,623 @@
+
+
+#include "Collation.h"
+
+#include "frameworks/base/cmds/statsd/src/stats_events.pb.h"
+
+#include <set>
+#include <vector>
+
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+using namespace google::protobuf;
+using namespace std;
+
+namespace android {
+namespace stats_log_api_gen {
+
+using android::os::statsd::StatsEvent;
+
+// TODO: Support WorkSources
+
+/**
+ * Turn lower and camel case into upper case with underscores.
+ */
+static string
+make_constant_name(const string& str)
+{
+    string result;
+    const int N = str.size();
+    bool underscore_next = false;
+    for (int i=0; i<N; i++) {
+        char c = str[i];
+        if (c >= 'A' && c <= 'Z') {
+            if (underscore_next) {
+                result += '_';
+                underscore_next = false;
+            }
+        } else if (c >= 'a' && c <= 'z') {
+            c = 'A' + c - 'a';
+            underscore_next = true;
+        } else if (c == '_') {
+            underscore_next = false;
+        }
+        result += c;
+    }
+    return result;
+}
+
+static const char*
+cpp_type_name(java_type_t type)
+{
+    switch (type) {
+        case JAVA_TYPE_BOOLEAN:
+            return "bool";
+        case JAVA_TYPE_INT:
+            return "int32_t";
+        case JAVA_TYPE_LONG:
+            return "int64_t";
+        case JAVA_TYPE_FLOAT:
+            return "float";
+        case JAVA_TYPE_DOUBLE:
+            return "double";
+        case JAVA_TYPE_STRING:
+            return "char const*";
+        default:
+            return "UNKNOWN";
+    }
+}
+
+static const char*
+java_type_name(java_type_t type)
+{
+    switch (type) {
+        case JAVA_TYPE_BOOLEAN:
+            return "boolean";
+        case JAVA_TYPE_INT:
+            return "int";
+        case JAVA_TYPE_LONG:
+            return "long";
+        case JAVA_TYPE_FLOAT:
+            return "float";
+        case JAVA_TYPE_DOUBLE:
+            return "double";
+        case JAVA_TYPE_STRING:
+            return "java.lang.String";
+        default:
+            return "UNKNOWN";
+    }
+}
+
+static int
+write_stats_log_cpp(FILE* out, const Atoms& atoms)
+{
+    int errorCount;
+
+    // Print prelude
+    fprintf(out, "// This file is autogenerated\n");
+    fprintf(out, "\n");
+
+    fprintf(out, "#include <log/log_event_list.h>\n");
+    fprintf(out, "#include <log/log.h>\n");
+    fprintf(out, "#include <statslog.h>\n");
+    fprintf(out, "\n");
+
+    fprintf(out, "namespace android {\n");
+    fprintf(out, "namespace util {\n");
+
+    // Print write methods
+    fprintf(out, "\n");
+    for (set<vector<java_type_t>>::const_iterator signature = atoms.signatures.begin();
+            signature != atoms.signatures.end(); signature++) {
+        int argIndex;
+
+        fprintf(out, "void\n");
+        fprintf(out, "stats_write(int code");
+        argIndex = 1;
+        for (vector<java_type_t>::const_iterator arg = signature->begin();
+                arg != signature->end(); arg++) {
+            fprintf(out, ", %s arg%d", cpp_type_name(*arg), argIndex);
+            argIndex++;
+        }
+        fprintf(out, ")\n");
+
+        fprintf(out, "{\n");
+        argIndex = 1;
+        fprintf(out, "    android_log_event_list event(code);\n");
+        for (vector<java_type_t>::const_iterator arg = signature->begin();
+                arg != signature->end(); arg++) {
+            if (*arg == JAVA_TYPE_STRING) {
+                fprintf(out, "    if (arg%d == NULL) {\n", argIndex);
+                fprintf(out, "        arg%d = \"\";\n", argIndex);
+                fprintf(out, "    }\n");
+            }
+            fprintf(out, "    event << arg%d;\n", argIndex);
+            argIndex++;
+        }
+
+        fprintf(out, "    event.write(LOG_ID_STATS);\n");
+        fprintf(out, "}\n");
+        fprintf(out, "\n");
+    }
+
+    // Print footer
+    fprintf(out, "\n");
+    fprintf(out, "} // namespace util\n");
+    fprintf(out, "} // namespace android\n");
+
+    return 0;
+}
+
+
+static int
+write_stats_log_header(FILE* out, const Atoms& atoms)
+{
+    int errorCount;
+
+    // Print prelude
+    fprintf(out, "// This file is autogenerated\n");
+    fprintf(out, "\n");
+    fprintf(out, "#pragma once\n");
+    fprintf(out, "\n");
+    fprintf(out, "#include <stdint.h>\n");
+    fprintf(out, "\n");
+
+    fprintf(out, "namespace android {\n");
+    fprintf(out, "namespace util {\n");
+    fprintf(out, "\n");
+    fprintf(out, "/*\n");
+    fprintf(out, " * API For logging statistics events.\n");
+    fprintf(out, " */\n");
+    fprintf(out, "\n");
+    fprintf(out, "/**\n");
+    fprintf(out, " * Constants for event codes.\n");
+    fprintf(out, " */\n");
+    fprintf(out, "enum {\n");
+
+    size_t i = 0;
+    // Print constants
+    for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
+            atom != atoms.decls.end(); atom++) {
+        string constant = make_constant_name(atom->name);
+        fprintf(out, "\n");
+        fprintf(out, "    /**\n");
+        fprintf(out, "     * %s %s\n", atom->message.c_str(), atom->name.c_str());
+        fprintf(out, "     * Usage: stats_write(StatsLog.%s", constant.c_str());
+        for (vector<AtomField>::const_iterator field = atom->fields.begin();
+                field != atom->fields.end(); field++) {
+            fprintf(out, ", %s %s", cpp_type_name(field->javaType), field->name.c_str());
+        }
+        fprintf(out, ");\n");
+        fprintf(out, "     */\n");
+        char const* const comma = (i == atoms.decls.size() - 1) ? "" : ",";
+        fprintf(out, "    %s = %d%s\n", constant.c_str(), atom->code, comma);
+        i++;
+    }
+    fprintf(out, "\n");
+    fprintf(out, "};\n");
+    fprintf(out, "\n");
+
+    // Print write methods
+    fprintf(out, "//\n");
+    fprintf(out, "// Write methods\n");
+    fprintf(out, "//\n");
+    for (set<vector<java_type_t>>::const_iterator signature = atoms.signatures.begin();
+            signature != atoms.signatures.end(); signature++) {
+
+        fprintf(out, "void stats_write(int code");
+        int argIndex = 1;
+        for (vector<java_type_t>::const_iterator arg = signature->begin();
+                arg != signature->end(); arg++) {
+            fprintf(out, ", %s arg%d", cpp_type_name(*arg), argIndex);
+            argIndex++;
+        }
+        fprintf(out, ");\n");
+    }
+
+    fprintf(out, "\n");
+    fprintf(out, "} // namespace util\n");
+    fprintf(out, "} // namespace android\n");
+
+    return 0;
+}
+
+static int
+write_stats_log_java(FILE* out, const Atoms& atoms)
+{
+    int errorCount;
+
+    // Print prelude
+    fprintf(out, "// This file is autogenerated\n");
+    fprintf(out, "\n");
+    fprintf(out, "package android.util;\n");
+    fprintf(out, "\n");
+    fprintf(out, "\n");
+    fprintf(out, "/**\n");
+    fprintf(out, " * API For logging statistics events.\n");
+    fprintf(out, " * @hide\n");
+    fprintf(out, " */\n");
+    fprintf(out, "public final class StatsLog {\n");
+    fprintf(out, "    // Constants for event codes.\n");
+
+    // Print constants
+    for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
+            atom != atoms.decls.end(); atom++) {
+        string constant = make_constant_name(atom->name);
+        fprintf(out, "\n");
+        fprintf(out, "    /**\n");
+        fprintf(out, "     * %s %s\n", atom->message.c_str(), atom->name.c_str());
+        fprintf(out, "     * Usage: StatsLog.write(StatsLog.%s", constant.c_str());
+        for (vector<AtomField>::const_iterator field = atom->fields.begin();
+                field != atom->fields.end(); field++) {
+            fprintf(out, ", %s %s", java_type_name(field->javaType), field->name.c_str());
+        }
+        fprintf(out, ");\n");
+        fprintf(out, "     */\n");
+        fprintf(out, "    public static final int %s = %d;\n", constant.c_str(), atom->code);
+    }
+    fprintf(out, "\n");
+
+    // Print write methods
+    fprintf(out, "    // Write methods\n");
+    for (set<vector<java_type_t>>::const_iterator signature = atoms.signatures.begin();
+            signature != atoms.signatures.end(); signature++) {
+        fprintf(out, "    public static native void write(int code");
+        int argIndex = 1;
+        for (vector<java_type_t>::const_iterator arg = signature->begin();
+                arg != signature->end(); arg++) {
+            fprintf(out, ", %s arg%d", java_type_name(*arg), argIndex);
+            argIndex++;
+        }
+        fprintf(out, ");\n");
+    }
+
+    fprintf(out, "}\n");
+
+    return 0;
+}
+
+static const char*
+jni_type_name(java_type_t type)
+{
+    switch (type) {
+        case JAVA_TYPE_BOOLEAN:
+            return "jboolean";
+        case JAVA_TYPE_INT:
+            return "jint";
+        case JAVA_TYPE_LONG:
+            return "jlong";
+        case JAVA_TYPE_FLOAT:
+            return "jfloat";
+        case JAVA_TYPE_DOUBLE:
+            return "jdouble";
+        case JAVA_TYPE_STRING:
+            return "jstring";
+        default:
+            return "UNKNOWN";
+    }
+}
+
+static string
+jni_function_name(const vector<java_type_t>& signature)
+{
+    string result("StatsLog_write");
+    for (vector<java_type_t>::const_iterator arg = signature.begin();
+            arg != signature.end(); arg++) {
+        switch (*arg) {
+            case JAVA_TYPE_BOOLEAN:
+                result += "_boolean";
+                break;
+            case JAVA_TYPE_INT:
+                result += "_int";
+                break;
+            case JAVA_TYPE_LONG:
+                result += "_long";
+                break;
+            case JAVA_TYPE_FLOAT:
+                result += "_float";
+                break;
+            case JAVA_TYPE_DOUBLE:
+                result += "_double";
+                break;
+            case JAVA_TYPE_STRING:
+                result += "_String";
+                break;
+            default:
+                result += "_UNKNOWN";
+                break;
+        }
+    }
+    return result;
+}
+
+static const char*
+java_type_signature(java_type_t type)
+{
+    switch (type) {
+        case JAVA_TYPE_BOOLEAN:
+            return "Z";
+        case JAVA_TYPE_INT:
+            return "I";
+        case JAVA_TYPE_LONG:
+            return "J";
+        case JAVA_TYPE_FLOAT:
+            return "F";
+        case JAVA_TYPE_DOUBLE:
+            return "D";
+        case JAVA_TYPE_STRING:
+            return "Ljava/lang/String;";
+        default:
+            return "UNKNOWN";
+    }
+}
+
+static string
+jni_function_signature(const vector<java_type_t>& signature)
+{
+    string result("(I");
+    for (vector<java_type_t>::const_iterator arg = signature.begin();
+            arg != signature.end(); arg++) {
+        result += java_type_signature(*arg);
+    }
+    result += ")V";
+    return result;
+}
+
+static int
+write_stats_log_jni(FILE* out, const Atoms& atoms)
+{
+    int errorCount;
+
+    // Print prelude
+    fprintf(out, "// This file is autogenerated\n");
+    fprintf(out, "\n");
+
+    fprintf(out, "#include <statslog.h>\n");
+    fprintf(out, "\n");
+    fprintf(out, "#include <nativehelper/JNIHelp.h>\n");
+    fprintf(out, "#include \"core_jni_helpers.h\"\n");
+    fprintf(out, "#include \"jni.h\"\n");
+    fprintf(out, "\n");
+    fprintf(out, "#define UNUSED  __attribute__((__unused__))\n");
+    fprintf(out, "\n");
+
+    fprintf(out, "namespace android {\n");
+    fprintf(out, "\n");
+
+    // Print write methods
+    for (set<vector<java_type_t>>::const_iterator signature = atoms.signatures.begin();
+            signature != atoms.signatures.end(); signature++) {
+        int argIndex;
+
+        fprintf(out, "static void\n");
+        fprintf(out, "%s(JNIEnv* env, jobject clazz UNUSED, jint code",
+                jni_function_name(*signature).c_str());
+        argIndex = 1;
+        for (vector<java_type_t>::const_iterator arg = signature->begin();
+                arg != signature->end(); arg++) {
+            fprintf(out, ", %s arg%d", jni_type_name(*arg), argIndex);
+            argIndex++;
+        }
+        fprintf(out, ")\n");
+
+        fprintf(out, "{\n");
+
+        // Prepare strings
+        argIndex = 1;
+        bool hadString = false;
+        for (vector<java_type_t>::const_iterator arg = signature->begin();
+                arg != signature->end(); arg++) {
+            if (*arg == JAVA_TYPE_STRING) {
+                fprintf(out, "    const char* str%d;\n", argIndex);
+                fprintf(out, "    if (arg%d != NULL) {\n", argIndex);
+                fprintf(out, "        str%d = env->GetStringUTFChars(arg%d, NULL);\n",
+                        argIndex, argIndex);
+                fprintf(out, "    } else {\n");
+                fprintf(out, "        str%d = NULL;\n", argIndex);
+                fprintf(out, "    }\n");
+                hadString = true;
+            }
+            argIndex++;
+        }
+
+        // Emit this to quiet the unused parameter warning if there were no strings.
+        if (!hadString) {
+            fprintf(out, "    (void)env;\n");
+        }
+
+        // stats_write call
+        argIndex = 1;
+        fprintf(out, "    android::util::stats_write(code");
+        for (vector<java_type_t>::const_iterator arg = signature->begin();
+                arg != signature->end(); arg++) {
+            const char* argName = (*arg == JAVA_TYPE_STRING) ? "str" : "arg";
+            fprintf(out, ", (%s)%s%d", cpp_type_name(*arg), argName, argIndex);
+            argIndex++;
+        }
+        fprintf(out, ");\n");
+
+        // Clean up strings
+        argIndex = 1;
+        for (vector<java_type_t>::const_iterator arg = signature->begin();
+                arg != signature->end(); arg++) {
+            if (*arg == JAVA_TYPE_STRING) {
+                fprintf(out, "    if (str%d != NULL) {\n", argIndex);
+                fprintf(out, "        env->ReleaseStringUTFChars(arg%d, str%d);\n",
+                        argIndex, argIndex);
+                fprintf(out, "    }\n");
+            }
+            argIndex++;
+        }
+
+        fprintf(out, "}\n");
+        fprintf(out, "\n");
+    }
+
+    // Print registration function table
+    fprintf(out, "/*\n");
+    fprintf(out, " * JNI registration.\n");
+    fprintf(out, " */\n");
+    fprintf(out, "static const JNINativeMethod gRegisterMethods[] = {\n");
+    for (set<vector<java_type_t>>::const_iterator signature = atoms.signatures.begin();
+            signature != atoms.signatures.end(); signature++) {
+        fprintf(out, "    { \"write\", \"%s\", (void*)%s },\n",
+                jni_function_signature(*signature).c_str(),
+                jni_function_name(*signature).c_str());
+    }
+    fprintf(out, "};\n");
+    fprintf(out, "\n");
+
+    // Print registration function
+    fprintf(out, "int register_android_util_StatsLog(JNIEnv* env) {\n");
+    fprintf(out, "    return RegisterMethodsOrDie(\n");
+    fprintf(out, "            env,\n");
+    fprintf(out, "            \"android/util/StatsLog\",\n");
+    fprintf(out, "            gRegisterMethods, NELEM(gRegisterMethods));\n");
+    fprintf(out, "}\n");
+
+    fprintf(out, "\n");
+    fprintf(out, "} // namespace android\n");
+
+    return 0;
+}
+
+
+static void
+print_usage()
+{
+    fprintf(stderr, "usage: stats-log-api-gen OPTIONS\n");
+    fprintf(stderr, "\n");
+    fprintf(stderr, "OPTIONS\n");
+    fprintf(stderr, "  --cpp FILENAME       the header file to output\n");
+    fprintf(stderr, "  --header FILENAME    the cpp file to output\n");
+    fprintf(stderr, "  --help               this message\n");
+    fprintf(stderr, "  --java FILENAME      the java file to output\n");
+    fprintf(stderr, "  --jni FILENAME       the jni file to output\n");
+}
+
+/**
+ * Do the argument parsing and execute the tasks.
+ */
+static int
+run(int argc, char const*const* argv)
+{
+    string cppFilename;
+    string headerFilename;
+    string javaFilename;
+    string jniFilename;
+
+    int index = 1;
+    while (index < argc) {
+        if (0 == strcmp("--help", argv[index])) {
+            print_usage();
+            return 0;
+        } else if (0 == strcmp("--cpp", argv[index])) {
+            index++;
+            if (index >= argc) {
+                print_usage();
+                return 1;
+            }
+            cppFilename = argv[index];
+        } else if (0 == strcmp("--header", argv[index])) {
+            index++;
+            if (index >= argc) {
+                print_usage();
+                return 1;
+            }
+            headerFilename = argv[index];
+        } else if (0 == strcmp("--java", argv[index])) {
+            index++;
+            if (index >= argc) {
+                print_usage();
+                return 1;
+            }
+            javaFilename = argv[index];
+        } else if (0 == strcmp("--jni", argv[index])) {
+            index++;
+            if (index >= argc) {
+                print_usage();
+                return 1;
+            }
+            jniFilename = argv[index];
+        }
+        index++;
+    }
+
+    if (cppFilename.size() == 0
+            && headerFilename.size() == 0
+            && javaFilename.size() == 0
+            && jniFilename.size() == 0) {
+        print_usage();
+        return 1;
+    }
+
+    // Collate the parameters
+    Atoms atoms;
+    int errorCount = collate_atoms(StatsEvent::descriptor(), &atoms);
+    if (errorCount != 0) {
+        return 1;
+    }
+
+    // Write the .cpp file
+    if (cppFilename.size() != 0) {
+        FILE* out = fopen(cppFilename.c_str(), "w");
+        if (out == NULL) {
+            fprintf(stderr, "Unable to open file for write: %s\n", cppFilename.c_str());
+            return 1;
+        }
+        errorCount = android::stats_log_api_gen::write_stats_log_cpp(out, atoms);
+        fclose(out);
+    }
+
+    // Write the .h file
+    if (headerFilename.size() != 0) {
+        FILE* out = fopen(headerFilename.c_str(), "w");
+        if (out == NULL) {
+            fprintf(stderr, "Unable to open file for write: %s\n", headerFilename.c_str());
+            return 1;
+        }
+        errorCount = android::stats_log_api_gen::write_stats_log_header(out, atoms);
+        fclose(out);
+    }
+
+    // Write the .java file
+    if (javaFilename.size() != 0) {
+        FILE* out = fopen(javaFilename.c_str(), "w");
+        if (out == NULL) {
+            fprintf(stderr, "Unable to open file for write: %s\n", javaFilename.c_str());
+            return 1;
+        }
+        errorCount = android::stats_log_api_gen::write_stats_log_java(out, atoms);
+        fclose(out);
+    }
+
+    // Write the jni file
+    if (jniFilename.size() != 0) {
+        FILE* out = fopen(jniFilename.c_str(), "w");
+        if (out == NULL) {
+            fprintf(stderr, "Unable to open file for write: %s\n", jniFilename.c_str());
+            return 1;
+        }
+        errorCount = android::stats_log_api_gen::write_stats_log_jni(out, atoms);
+        fclose(out);
+    }
+
+    return 0;
+}
+
+}
+}
+
+/**
+ * Main.
+ */
+int
+main(int argc, char const*const* argv)
+{
+    GOOGLE_PROTOBUF_VERIFY_VERSION;
+
+    return android::stats_log_api_gen::run(argc, argv);
+}