Stats log api for attribution chain.

Test: all unit test passed.
Change-Id: I628d409e517f4f95c8da1d0c7fd4d514c1d9196d
diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp
index 423d028..8183a3f 100644
--- a/tools/stats_log_api_gen/main.cpp
+++ b/tools/stats_log_api_gen/main.cpp
@@ -24,8 +24,6 @@
 
 using android::os::statsd::Atom;
 
-// TODO: Support WorkSources
-
 /**
  * Turn lower and camel case into upper case with underscores.
  */
@@ -97,13 +95,13 @@
     }
 }
 
-static int
-write_stats_log_cpp(FILE* out, const Atoms& atoms)
-{
+static int write_stats_log_cpp(FILE *out, const Atoms &atoms,
+                               const AtomDecl &attributionDecl) {
     // Print prelude
     fprintf(out, "// This file is autogenerated\n");
     fprintf(out, "\n");
 
+    fprintf(out, "#include <exception>\n");
     fprintf(out, "#include <log/log_event_list.h>\n");
     fprintf(out, "#include <log/log.h>\n");
     fprintf(out, "#include <statslog.h>\n");
@@ -117,15 +115,29 @@
     // Print write methods
     fprintf(out, "\n");
     for (set<vector<java_type_t>>::const_iterator signature = atoms.signatures.begin();
-            signature != atoms.signatures.end(); signature++) {
+        signature != atoms.signatures.end(); signature++) {
         int argIndex;
 
         fprintf(out, "void\n");
         fprintf(out, "stats_write(int32_t 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);
+            arg != signature->end(); arg++) {
+            if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+                for (auto chainField : attributionDecl.fields) {
+                    if (chainField.javaType == JAVA_TYPE_STRING) {
+                            fprintf(out, ", const std::vector<%s>& %s",
+                                 cpp_type_name(chainField.javaType),
+                                 chainField.name.c_str());
+                    } else {
+                            fprintf(out, ", const %s* %s, size_t %s_length",
+                                 cpp_type_name(chainField.javaType),
+                                 chainField.name.c_str(), chainField.name.c_str());
+                    }
+                }
+            } else {
+                fprintf(out, ", %s arg%d", cpp_type_name(*arg), argIndex);
+            }
             argIndex++;
         }
         fprintf(out, ")\n");
@@ -133,15 +145,40 @@
         fprintf(out, "{\n");
         argIndex = 1;
         fprintf(out, "    android_log_event_list event(kStatsEventTag);\n");
-        fprintf(out, "    event << code;\n");
+        fprintf(out, "    event << code;\n\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);
+            arg != signature->end(); arg++) {
+            if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+                for (const auto &chainField : attributionDecl.fields) {
+                    if (chainField.javaType == JAVA_TYPE_STRING) {
+                        fprintf(out, "    if (%s_length != %s.size()) {\n",
+                            attributionDecl.fields.front().name.c_str(), chainField.name.c_str());
+                        fprintf(out, "        throw std::invalid_argument(\"attribution fields with"
+                            " diff length: %s vs %s\");\n",
+                            attributionDecl.fields.front().name.c_str(),
+                            chainField.name.c_str());
+                        fprintf(out, "        return;\n");
+                        fprintf(out, "    }\n");
+                    }
+                }
+                fprintf(out, "\n    event.begin();\n");
+                fprintf(out, "    for (size_t i = 0; i < %s_length; ++i) {\n",
+                    attributionDecl.fields.front().name.c_str());
+                fprintf(out, "        event.begin();\n");
+                for (const auto &chainField : attributionDecl.fields) {
+                    fprintf(out, "        event << %s[i];\n", chainField.name.c_str());
+                }
+                fprintf(out, "        event.end();\n");
                 fprintf(out, "    }\n");
+                fprintf(out, "    event.end();\n\n");
+            } else {
+                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);
             }
-            fprintf(out, "    event << arg%d;\n", argIndex);
             argIndex++;
         }
 
@@ -160,7 +197,7 @@
 
 
 static int
-write_stats_log_header(FILE* out, const Atoms& atoms)
+write_stats_log_header(FILE* out, const Atoms& atoms, const AtomDecl &attributionDecl)
 {
     // Print prelude
     fprintf(out, "// This file is autogenerated\n");
@@ -168,6 +205,7 @@
     fprintf(out, "#pragma once\n");
     fprintf(out, "\n");
     fprintf(out, "#include <stdint.h>\n");
+    fprintf(out, "#include <vector>\n");
     fprintf(out, "\n");
 
     fprintf(out, "namespace android {\n");
@@ -185,7 +223,7 @@
     size_t i = 0;
     // Print constants
     for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
-            atom != atoms.decls.end(); atom++) {
+        atom != atoms.decls.end(); atom++) {
         string constant = make_constant_name(atom->name);
         fprintf(out, "\n");
         fprintf(out, "    /**\n");
@@ -193,7 +231,21 @@
         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());
+            if (field->javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+                for (auto chainField : attributionDecl.fields) {
+                    if (chainField.javaType == JAVA_TYPE_STRING) {
+                        fprintf(out, ", const std::vector<%s>& %s",
+                             cpp_type_name(chainField.javaType),
+                             chainField.name.c_str());
+                    } else {
+                        fprintf(out, ", const %s* %s, size_t %s_length",
+                             cpp_type_name(chainField.javaType),
+                             chainField.name.c_str(), chainField.name.c_str());
+                    }
+                }
+            } else {
+                fprintf(out, ", %s %s", cpp_type_name(field->javaType), field->name.c_str());
+            }
         }
         fprintf(out, ");\n");
         fprintf(out, "     */\n");
@@ -208,8 +260,7 @@
     fprintf(out, "};\n");
     fprintf(out, "\n");
 
-    fprintf(out, "const static int kMaxPushedAtomId = %d;\n\n",
-            maxPushedAtomId);
+    fprintf(out, "const static int kMaxPushedAtomId = %d;\n\n", maxPushedAtomId);
 
     // Print write methods
     fprintf(out, "//\n");
@@ -220,8 +271,21 @@
         fprintf(out, "void stats_write(int32_t 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);
+            arg != signature->end(); arg++) {
+            if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+                for (auto chainField : attributionDecl.fields) {
+                    if (chainField.javaType == JAVA_TYPE_STRING) {
+                        fprintf(out, ", const std::vector<%s>& %s",
+                            cpp_type_name(chainField.javaType), chainField.name.c_str());
+                    } else {
+                        fprintf(out, ", const %s* %s, size_t %s_length",
+                            cpp_type_name(chainField.javaType),
+                            chainField.name.c_str(), chainField.name.c_str());
+                    }
+                }
+            } else {
+                fprintf(out, ", %s arg%d", cpp_type_name(*arg), argIndex);
+            }
             argIndex++;
         }
         fprintf(out, ");\n");
@@ -235,7 +299,7 @@
 }
 
 static int
-write_stats_log_java(FILE* out, const Atoms& atoms)
+write_stats_log_java(FILE* out, const Atoms& atoms, const AtomDecl &attributionDecl)
 {
     // Print prelude
     fprintf(out, "// This file is autogenerated\n");
@@ -259,8 +323,15 @@
         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());
+            field != atom->fields.end(); field++) {
+            if (field->javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+                for (auto chainField : attributionDecl.fields) {
+                    fprintf(out, ", %s[] %s",
+                        java_type_name(chainField.javaType), chainField.name.c_str());
+                }
+            } else {
+                fprintf(out, ", %s %s", java_type_name(field->javaType), field->name.c_str());
+            }
         }
         fprintf(out, ");\n");
         fprintf(out, "     */\n");
@@ -271,33 +342,41 @@
     // Print constants for the enum values.
     fprintf(out, "    // Constants for enum values.\n\n");
     for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
-            atom != atoms.decls.end(); atom++) {
+        atom != atoms.decls.end(); atom++) {
         for (vector<AtomField>::const_iterator field = atom->fields.begin();
-                field != atom->fields.end(); field++) {
-          if (field->javaType == JAVA_TYPE_ENUM) {
-            fprintf(out, "    // Values for %s.%s\n", atom->message.c_str(), field->name.c_str());
-            for (map<int, string>::const_iterator value = field->enumValues.begin();
-                 value != field->enumValues.end(); value++) {
-              fprintf(out, "    public static final int %s__%s__%s = %d;\n",
-                      make_constant_name(atom->message).c_str(),
-                      make_constant_name(field->name).c_str(),
-                      make_constant_name(value->second).c_str(),
-                      value->first);
+            field != atom->fields.end(); field++) {
+            if (field->javaType == JAVA_TYPE_ENUM) {
+                fprintf(out, "    // Values for %s.%s\n", atom->message.c_str(),
+                    field->name.c_str());
+                for (map<int, string>::const_iterator value = field->enumValues.begin();
+                    value != field->enumValues.end(); value++) {
+                    fprintf(out, "    public static final int %s__%s__%s = %d;\n",
+                        make_constant_name(atom->message).c_str(),
+                        make_constant_name(field->name).c_str(),
+                        make_constant_name(value->second).c_str(),
+                        value->first);
+                }
+                fprintf(out, "\n");
             }
-            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++) {
+        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);
+            arg != signature->end(); arg++) {
+            if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+                for (auto chainField : attributionDecl.fields) {
+                    fprintf(out, ", %s[] %s",
+                        java_type_name(chainField.javaType), chainField.name.c_str());
+                }
+            } else {
+                fprintf(out, ", %s arg%d", java_type_name(*arg), argIndex);
+            }
             argIndex++;
         }
         fprintf(out, ");\n");
@@ -330,12 +409,25 @@
     }
 }
 
+static const char*
+jni_array_type_name(java_type_t type)
+{
+    switch (type) {
+        case JAVA_TYPE_INT:
+            return "jintArray";
+        case JAVA_TYPE_STRING:
+            return "jobjectArray";
+        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++) {
+        arg != signature.end(); arg++) {
         switch (*arg) {
             case JAVA_TYPE_BOOLEAN:
                 result += "_boolean";
@@ -356,6 +448,9 @@
             case JAVA_TYPE_STRING:
                 result += "_String";
                 break;
+            case JAVA_TYPE_ATTRIBUTION_CHAIN:
+              result += "_AttributionChain";
+              break;
             default:
                 result += "_UNKNOWN";
                 break;
@@ -387,19 +482,26 @@
 }
 
 static string
-jni_function_signature(const vector<java_type_t>& signature)
+jni_function_signature(const vector<java_type_t>& signature, const AtomDecl &attributionDecl)
 {
     string result("(I");
     for (vector<java_type_t>::const_iterator arg = signature.begin();
-            arg != signature.end(); arg++) {
-        result += java_type_signature(*arg);
+        arg != signature.end(); arg++) {
+        if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+            for (auto chainField : attributionDecl.fields) {
+                result += "[";
+                result += java_type_signature(chainField.javaType);
+            }
+        } else {
+            result += java_type_signature(*arg);
+        }
     }
     result += ")V";
     return result;
 }
 
 static int
-write_stats_log_jni(FILE* out, const Atoms& atoms)
+write_stats_log_jni(FILE* out, const Atoms& atoms, const AtomDecl &attributionDecl)
 {
     // Print prelude
     fprintf(out, "// This file is autogenerated\n");
@@ -408,6 +510,8 @@
     fprintf(out, "#include <statslog.h>\n");
     fprintf(out, "\n");
     fprintf(out, "#include <nativehelper/JNIHelp.h>\n");
+    fprintf(out, "#include <nativehelper/ScopedUtfChars.h>\n");
+    fprintf(out, "#include <utils/Vector.h>\n");
     fprintf(out, "#include \"core_jni_helpers.h\"\n");
     fprintf(out, "#include \"jni.h\"\n");
     fprintf(out, "\n");
@@ -419,7 +523,7 @@
 
     // Print write methods
     for (set<vector<java_type_t>>::const_iterator signature = atoms.signatures.begin();
-            signature != atoms.signatures.end(); signature++) {
+        signature != atoms.signatures.end(); signature++) {
         int argIndex;
 
         fprintf(out, "static void\n");
@@ -428,7 +532,14 @@
         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);
+            if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+                for (auto chainField : attributionDecl.fields) {
+                    fprintf(out, ", %s %s", jni_array_type_name(chainField.javaType),
+                        chainField.name.c_str());
+                }
+            } else {
+                fprintf(out, ", %s arg%d", jni_type_name(*arg), argIndex);
+            }
             argIndex++;
         }
         fprintf(out, ")\n");
@@ -437,10 +548,11 @@
 
         // Prepare strings
         argIndex = 1;
-        bool hadString = false;
+        bool hadStringOrChain = false;
         for (vector<java_type_t>::const_iterator arg = signature->begin();
                 arg != signature->end(); arg++) {
             if (*arg == JAVA_TYPE_STRING) {
+                hadStringOrChain = true;
                 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",
@@ -448,13 +560,52 @@
                 fprintf(out, "    } else {\n");
                 fprintf(out, "        str%d = NULL;\n", argIndex);
                 fprintf(out, "    }\n");
-                hadString = true;
+            } else if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+                hadStringOrChain = true;
+                for (auto chainField : attributionDecl.fields) {
+                    fprintf(out, "    size_t %s_length = env->GetArrayLength(%s);\n",
+                        chainField.name.c_str(), chainField.name.c_str());
+                    if (chainField.name != attributionDecl.fields.front().name) {
+                        fprintf(out, "    if (%s_length != %s_length) {\n",
+                            chainField.name.c_str(),
+                            attributionDecl.fields.front().name.c_str());
+                        fprintf(out, "        jniThrowException(env, "
+                            "\"java/lang/IllegalArgumentException\", "
+                            "\"invalid attribution field(%s) length.\");\n",
+                            chainField.name.c_str());
+                        fprintf(out, "        return;\n");
+                        fprintf(out, "    }\n");
+                    }
+                    if (chainField.javaType == JAVA_TYPE_INT) {
+                        fprintf(out, "    jint* %s_array = env->GetIntArrayElements(%s, NULL);\n",
+                            chainField.name.c_str(), chainField.name.c_str());
+                    } else if (chainField.javaType == JAVA_TYPE_STRING) {
+                        fprintf(out, "    std::vector<%s> %s_vec;\n",
+                            cpp_type_name(chainField.javaType), chainField.name.c_str());
+                        fprintf(out, "    std::vector<ScopedUtfChars*> scoped_%s_vec;\n",
+                            chainField.name.c_str());
+                        fprintf(out, "    for (size_t i = 0; i < %s_length; ++i) {\n",
+                            chainField.name.c_str());
+                        fprintf(out, "        jstring jstr = "
+                            "(jstring)env->GetObjectArrayElement(%s, i);\n",
+                             chainField.name.c_str());
+                        fprintf(out, "        ScopedUtfChars* scoped_%s = "
+                            "new ScopedUtfChars(env, jstr);\n",
+                             chainField.name.c_str());
+                        fprintf(out, "        %s_vec.push_back(scoped_%s->c_str());\n",
+                                chainField.name.c_str(), chainField.name.c_str());
+                        fprintf(out, "        scoped_%s_vec.push_back(scoped_%s);\n",
+                                chainField.name.c_str(), chainField.name.c_str());
+                        fprintf(out, "    }\n");
+                    }
+                    fprintf(out, "\n");
+                }
             }
             argIndex++;
         }
-
-        // Emit this to quiet the unused parameter warning if there were no strings.
-        if (!hadString) {
+        // Emit this to quiet the unused parameter warning if there were no strings or attribution
+        // chains.
+        if (!hadStringOrChain) {
             fprintf(out, "    (void)env;\n");
         }
 
@@ -463,11 +614,24 @@
         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);
+            if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+                for (auto chainField : attributionDecl.fields) {
+                    if (chainField.javaType == JAVA_TYPE_INT) {
+                        fprintf(out, ", (const %s*)%s_array, %s_length",
+                            cpp_type_name(chainField.javaType),
+                            chainField.name.c_str(), chainField.name.c_str());
+                    } else if (chainField.javaType == JAVA_TYPE_STRING) {
+                        fprintf(out, ", %s_vec", chainField.name.c_str());
+                    }
+                }
+            } else {
+                const char *argName = (*arg == JAVA_TYPE_STRING) ? "str" : "arg";
+                fprintf(out, ", (%s)%s%d", cpp_type_name(*arg), argName, argIndex);
+            }
             argIndex++;
         }
         fprintf(out, ");\n");
+        fprintf(out, "\n");
 
         // Clean up strings
         argIndex = 1;
@@ -478,6 +642,18 @@
                 fprintf(out, "        env->ReleaseStringUTFChars(arg%d, str%d);\n",
                         argIndex, argIndex);
                 fprintf(out, "    }\n");
+            } else if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+                for (auto chainField : attributionDecl.fields) {
+                    if (chainField.javaType == JAVA_TYPE_INT) {
+                        fprintf(out, "    env->ReleaseIntArrayElements(%s, %s_array, 0);\n",
+                            chainField.name.c_str(), chainField.name.c_str());
+                    } else if (chainField.javaType == JAVA_TYPE_STRING) {
+                        fprintf(out, "    for (size_t i = 0; i < %s_length; ++i) {\n",
+                            chainField.name.c_str());
+                        fprintf(out, "        delete scoped_%s_vec[i];\n", chainField.name.c_str());
+                        fprintf(out, "    }\n");
+                    }
+                }
             }
             argIndex++;
         }
@@ -494,8 +670,8 @@
     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());
+            jni_function_signature(*signature, attributionDecl).c_str(),
+            jni_function_name(*signature).c_str());
     }
     fprintf(out, "};\n");
     fprintf(out, "\n");
@@ -591,6 +767,11 @@
         return 1;
     }
 
+    AtomDecl attributionDecl;
+    vector<java_type_t> attributionSignature;
+    collate_atom(android::os::statsd::Attribution::descriptor(),
+                 &attributionDecl, &attributionSignature);
+
     // Write the .cpp file
     if (cppFilename.size() != 0) {
         FILE* out = fopen(cppFilename.c_str(), "w");
@@ -598,7 +779,8 @@
             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);
+        errorCount = android::stats_log_api_gen::write_stats_log_cpp(
+            out, atoms, attributionDecl);
         fclose(out);
     }
 
@@ -609,7 +791,8 @@
             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);
+        errorCount = android::stats_log_api_gen::write_stats_log_header(
+            out, atoms, attributionDecl);
         fclose(out);
     }
 
@@ -620,7 +803,8 @@
             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);
+        errorCount = android::stats_log_api_gen::write_stats_log_java(
+            out, atoms, attributionDecl);
         fclose(out);
     }
 
@@ -631,7 +815,8 @@
             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);
+        errorCount = android::stats_log_api_gen::write_stats_log_jni(
+            out, atoms, attributionDecl);
         fclose(out);
     }
 
@@ -650,4 +835,4 @@
     GOOGLE_PROTOBUF_VERIFY_VERSION;
 
     return android::stats_log_api_gen::run(argc, argv);
-}
+}
\ No newline at end of file