• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 
2 
3 #include "Collation.h"
4 
5 #include "frameworks/base/cmds/statsd/src/atoms.pb.h"
6 
7 #include <set>
8 #include <vector>
9 
10 #include <getopt.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 
15 using namespace google::protobuf;
16 using namespace std;
17 
18 namespace android {
19 namespace stats_log_api_gen {
20 
21 const int PULL_ATOM_START_ID = 1000;
22 
23 int maxPushedAtomId = 2;
24 
25 using android::os::statsd::Atom;
26 
27 /**
28  * Turn lower and camel case into upper case with underscores.
29  */
30 static string
make_constant_name(const string & str)31 make_constant_name(const string& str)
32 {
33     string result;
34     const int N = str.size();
35     bool underscore_next = false;
36     for (int i=0; i<N; i++) {
37         char c = str[i];
38         if (c >= 'A' && c <= 'Z') {
39             if (underscore_next) {
40                 result += '_';
41                 underscore_next = false;
42             }
43         } else if (c >= 'a' && c <= 'z') {
44             c = 'A' + c - 'a';
45             underscore_next = true;
46         } else if (c == '_') {
47             underscore_next = false;
48         }
49         result += c;
50     }
51     return result;
52 }
53 
54 static const char*
cpp_type_name(java_type_t type)55 cpp_type_name(java_type_t type)
56 {
57     switch (type) {
58         case JAVA_TYPE_BOOLEAN:
59             return "bool";
60         case JAVA_TYPE_INT:
61         case JAVA_TYPE_ENUM:
62             return "int32_t";
63         case JAVA_TYPE_LONG:
64             return "int64_t";
65         case JAVA_TYPE_FLOAT:
66             return "float";
67         case JAVA_TYPE_DOUBLE:
68             return "double";
69         case JAVA_TYPE_STRING:
70             return "char const*";
71         default:
72             return "UNKNOWN";
73     }
74 }
75 
76 static const char*
java_type_name(java_type_t type)77 java_type_name(java_type_t type)
78 {
79     switch (type) {
80         case JAVA_TYPE_BOOLEAN:
81             return "boolean";
82         case JAVA_TYPE_INT:
83         case JAVA_TYPE_ENUM:
84             return "int";
85         case JAVA_TYPE_LONG:
86             return "long";
87         case JAVA_TYPE_FLOAT:
88             return "float";
89         case JAVA_TYPE_DOUBLE:
90             return "double";
91         case JAVA_TYPE_STRING:
92             return "java.lang.String";
93         default:
94             return "UNKNOWN";
95     }
96 }
97 
write_stats_log_cpp(FILE * out,const Atoms & atoms,const AtomDecl & attributionDecl)98 static int write_stats_log_cpp(FILE *out, const Atoms &atoms,
99                                const AtomDecl &attributionDecl) {
100     // Print prelude
101     fprintf(out, "// This file is autogenerated\n");
102     fprintf(out, "\n");
103 
104     fprintf(out, "#include <mutex>\n");
105     fprintf(out, "#include <chrono>\n");
106     fprintf(out, "#include <thread>\n");
107     fprintf(out, "#include <cutils/properties.h>\n");
108     fprintf(out, "#include <stats_event_list.h>\n");
109     fprintf(out, "#include <log/log.h>\n");
110     fprintf(out, "#include <statslog.h>\n");
111     fprintf(out, "#include <utils/SystemClock.h>\n");
112     fprintf(out, "\n");
113 
114     fprintf(out, "namespace android {\n");
115     fprintf(out, "namespace util {\n");
116     fprintf(out, "// the single event tag id for all stats logs\n");
117     fprintf(out, "const static int kStatsEventTag = 1937006964;\n");
118     fprintf(out, "const static bool kStatsdEnabled = property_get_bool(\"ro.statsd.enable\", true);\n");
119 
120     std::set<string> kTruncatingAtomNames = {"mobile_radio_power_state_changed",
121                                              "audio_state_changed",
122                                              "call_state_changed",
123                                              "phone_signal_strength_changed",
124                                              "mobile_bytes_transfer_by_fg_bg",
125                                              "mobile_bytes_transfer"};
126     fprintf(out,
127             "const std::set<int> "
128             "AtomsInfo::kNotTruncatingTimestampAtomWhiteList = {\n");
129     for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
130          atom != atoms.decls.end(); atom++) {
131         if (kTruncatingAtomNames.find(atom->name) ==
132             kTruncatingAtomNames.end()) {
133             string constant = make_constant_name(atom->name);
134             fprintf(out, " %s,\n", constant.c_str());
135         }
136     }
137     fprintf(out, "};\n");
138     fprintf(out, "\n");
139 
140     fprintf(out,
141             "const std::set<int> AtomsInfo::kAtomsWithAttributionChain = {\n");
142     for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
143          atom != atoms.decls.end(); atom++) {
144         for (vector<AtomField>::const_iterator field = atom->fields.begin();
145              field != atom->fields.end(); field++) {
146             if (field->javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
147                 string constant = make_constant_name(atom->name);
148                 fprintf(out, " %s,\n", constant.c_str());
149                 break;
150             }
151         }
152     }
153     fprintf(out, "};\n");
154     fprintf(out, "\n");
155 
156     fprintf(out, "static std::map<int, int> getAtomUidField() {\n");
157     fprintf(out, "  std::map<int, int> uidField;\n");
158     for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
159          atom != atoms.decls.end(); atom++) {
160         if (atom->uidField == 0) {
161             continue;
162         }
163         fprintf(out,
164                 "\n    // Adding uid field for atom "
165                 "(%d)%s\n",
166                 atom->code, atom->name.c_str());
167         fprintf(out, "    uidField[static_cast<int>(%s)] = %d;\n",
168                 make_constant_name(atom->name).c_str(), atom->uidField);
169     }
170 
171     fprintf(out, "    return uidField;\n");
172     fprintf(out, "};\n");
173 
174     fprintf(out,
175             "const std::map<int, int> AtomsInfo::kAtomsWithUidField = "
176             "getAtomUidField();\n");
177 
178     fprintf(out,
179             "static std::map<int, StateAtomFieldOptions> "
180             "getStateAtomFieldOptions() {\n");
181     fprintf(out, "    std::map<int, StateAtomFieldOptions> options;\n");
182     fprintf(out, "    StateAtomFieldOptions opt;\n");
183     for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
184          atom != atoms.decls.end(); atom++) {
185         if (atom->primaryFields.size() == 0 && atom->exclusiveField == 0) {
186             continue;
187         }
188         fprintf(out,
189                 "\n    // Adding primary and exclusive fields for atom "
190                 "(%d)%s\n",
191                 atom->code, atom->name.c_str());
192         fprintf(out, "    opt.primaryFields.clear();\n");
193         for (const auto& field : atom->primaryFields) {
194             fprintf(out, "    opt.primaryFields.push_back(%d);\n", field);
195         }
196 
197         fprintf(out, "    opt.exclusiveField = %d;\n", atom->exclusiveField);
198         fprintf(out, "    options[static_cast<int>(%s)] = opt;\n",
199                 make_constant_name(atom->name).c_str());
200     }
201 
202     fprintf(out, "    return options;\n");
203     fprintf(out, "  }\n");
204 
205     fprintf(out,
206             "const std::map<int, StateAtomFieldOptions> "
207             "AtomsInfo::kStateAtomsFieldOptions = "
208             "getStateAtomFieldOptions();\n");
209 
210 
211     fprintf(out, "int64_t lastRetryTimestampNs = -1;\n");
212     fprintf(out, "const int64_t kMinRetryIntervalNs = NS_PER_SEC * 60 * 20; // 20 minutes\n");
213     fprintf(out, "static std::mutex mLogdRetryMutex;\n");
214 
215     // Print write methods
216     fprintf(out, "\n");
217     for (set<vector<java_type_t>>::const_iterator signature = atoms.signatures.begin();
218         signature != atoms.signatures.end(); signature++) {
219         int argIndex;
220 
221         fprintf(out, "int\n");
222         fprintf(out, "try_stats_write(int32_t code");
223         argIndex = 1;
224         for (vector<java_type_t>::const_iterator arg = signature->begin();
225             arg != signature->end(); arg++) {
226             if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
227                 for (auto chainField : attributionDecl.fields) {
228                     if (chainField.javaType == JAVA_TYPE_STRING) {
229                             fprintf(out, ", const std::vector<%s>& %s",
230                                  cpp_type_name(chainField.javaType),
231                                  chainField.name.c_str());
232                     } else {
233                             fprintf(out, ", const %s* %s, size_t %s_length",
234                                  cpp_type_name(chainField.javaType),
235                                  chainField.name.c_str(), chainField.name.c_str());
236                     }
237                 }
238             } else {
239                 fprintf(out, ", %s arg%d", cpp_type_name(*arg), argIndex);
240             }
241             argIndex++;
242         }
243         fprintf(out, ")\n");
244 
245         fprintf(out, "{\n");
246         argIndex = 1;
247         fprintf(out, "  if (kStatsdEnabled) {\n");
248         fprintf(out, "    stats_event_list event(kStatsEventTag);\n");
249         fprintf(out, "    event << android::elapsedRealtimeNano();\n\n");
250         fprintf(out, "    event << code;\n\n");
251         for (vector<java_type_t>::const_iterator arg = signature->begin();
252             arg != signature->end(); arg++) {
253             if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
254                 for (const auto &chainField : attributionDecl.fields) {
255                     if (chainField.javaType == JAVA_TYPE_STRING) {
256                         fprintf(out, "    if (%s_length != %s.size()) {\n",
257                             attributionDecl.fields.front().name.c_str(), chainField.name.c_str());
258                         fprintf(out, "        return -EINVAL;\n");
259                         fprintf(out, "    }\n");
260                     }
261                 }
262                 fprintf(out, "\n    event.begin();\n");
263                 fprintf(out, "    for (size_t i = 0; i < %s_length; ++i) {\n",
264                     attributionDecl.fields.front().name.c_str());
265                 fprintf(out, "        event.begin();\n");
266                 for (const auto &chainField : attributionDecl.fields) {
267                     if (chainField.javaType == JAVA_TYPE_STRING) {
268                         fprintf(out, "        if (%s[i] != NULL) {\n", chainField.name.c_str());
269                         fprintf(out, "           event << %s[i];\n", chainField.name.c_str());
270                         fprintf(out, "        } else {\n");
271                         fprintf(out, "           event << \"\";\n");
272                         fprintf(out, "        }\n");
273                     } else {
274                         fprintf(out, "        event << %s[i];\n", chainField.name.c_str());
275                     }
276                 }
277                 fprintf(out, "        event.end();\n");
278                 fprintf(out, "    }\n");
279                 fprintf(out, "    event.end();\n\n");
280             } else {
281                 if (*arg == JAVA_TYPE_STRING) {
282                     fprintf(out, "    if (arg%d == NULL) {\n", argIndex);
283                     fprintf(out, "        arg%d = \"\";\n", argIndex);
284                     fprintf(out, "    }\n");
285                 }
286                 fprintf(out, "    event << arg%d;\n", argIndex);
287             }
288             argIndex++;
289         }
290 
291         fprintf(out, "    return event.write(LOG_ID_STATS);\n");
292         fprintf(out, "  } else {\n");
293         fprintf(out, "    return 1;\n");
294         fprintf(out, "  }\n");
295         fprintf(out, "}\n");
296         fprintf(out, "\n");
297     }
298 
299    for (set<vector<java_type_t>>::const_iterator signature = atoms.signatures.begin();
300        signature != atoms.signatures.end(); signature++) {
301        int argIndex;
302 
303        fprintf(out, "int \n");
304        fprintf(out, "stats_write(int32_t code");
305        argIndex = 1;
306        for (vector<java_type_t>::const_iterator arg = signature->begin();
307            arg != signature->end(); arg++) {
308            if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
309                for (auto chainField : attributionDecl.fields) {
310                    if (chainField.javaType == JAVA_TYPE_STRING) {
311                            fprintf(out, ", const std::vector<%s>& %s",
312                                 cpp_type_name(chainField.javaType),
313                                 chainField.name.c_str());
314                    } else {
315                            fprintf(out, ", const %s* %s, size_t %s_length",
316                                 cpp_type_name(chainField.javaType),
317                                 chainField.name.c_str(), chainField.name.c_str());
318                    }
319                }
320            } else {
321                fprintf(out, ", %s arg%d", cpp_type_name(*arg), argIndex);
322            }
323            argIndex++;
324        }
325        fprintf(out, ")\n");
326 
327        fprintf(out, "{\n");
328        fprintf(out, "  int ret = 0;\n");
329 
330        fprintf(out, "  for(int retry = 0; retry < 2; ++retry) {\n");
331        fprintf(out, "      ret =  try_stats_write(code");
332 
333        argIndex = 1;
334        for (vector<java_type_t>::const_iterator arg = signature->begin();
335            arg != signature->end(); arg++) {
336            if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
337                for (auto chainField : attributionDecl.fields) {
338                    if (chainField.javaType == JAVA_TYPE_STRING) {
339                            fprintf(out, ", %s",
340                                 chainField.name.c_str());
341                    } else {
342                            fprintf(out, ",  %s,  %s_length",
343                                 chainField.name.c_str(), chainField.name.c_str());
344                    }
345                }
346            } else {
347                fprintf(out, ", arg%d", argIndex);
348            }
349            argIndex++;
350        }
351        fprintf(out, ");\n");
352        fprintf(out, "      if (ret >= 0) { return retry; }\n");
353 
354 
355        fprintf(out, "      {\n");
356        fprintf(out, "          std::lock_guard<std::mutex> lock(mLogdRetryMutex);\n");
357        fprintf(out, "          if ((android::elapsedRealtimeNano() - lastRetryTimestampNs) <= "
358                                 "kMinRetryIntervalNs) break;\n");
359        fprintf(out, "          lastRetryTimestampNs = android::elapsedRealtimeNano();\n");
360        fprintf(out, "      }\n");
361        fprintf(out, "      std::this_thread::sleep_for(std::chrono::milliseconds(10));\n");
362        fprintf(out, "  }\n");
363        fprintf(out, "  return ret;\n");
364        fprintf(out, "}\n");
365        fprintf(out, "\n");
366    }
367 
368     for (set<vector<java_type_t>>::const_iterator signature = atoms.non_chained_signatures.begin();
369         signature != atoms.non_chained_signatures.end(); signature++) {
370         int argIndex;
371 
372         fprintf(out, "int\n");
373         fprintf(out, "try_stats_write_non_chained(int32_t code");
374         argIndex = 1;
375         for (vector<java_type_t>::const_iterator arg = signature->begin();
376             arg != signature->end(); arg++) {
377             fprintf(out, ", %s arg%d", cpp_type_name(*arg), argIndex);
378             argIndex++;
379         }
380         fprintf(out, ")\n");
381 
382         fprintf(out, "{\n");
383         argIndex = 1;
384         fprintf(out, "  if (kStatsdEnabled) {\n");
385         fprintf(out, "    stats_event_list event(kStatsEventTag);\n");
386         fprintf(out, "    event << android::elapsedRealtimeNano();\n\n");
387         fprintf(out, "    event << code;\n\n");
388         for (vector<java_type_t>::const_iterator arg = signature->begin();
389             arg != signature->end(); arg++) {
390             if (argIndex == 1) {
391                 fprintf(out, "    event.begin();\n\n");
392                 fprintf(out, "    event.begin();\n");
393             }
394             if (*arg == JAVA_TYPE_STRING) {
395                 fprintf(out, "    if (arg%d == NULL) {\n", argIndex);
396                 fprintf(out, "        arg%d = \"\";\n", argIndex);
397                 fprintf(out, "    }\n");
398             }
399             fprintf(out, "    event << arg%d;\n", argIndex);
400             if (argIndex == 2) {
401                 fprintf(out, "    event.end();\n\n");
402                 fprintf(out, "    event.end();\n\n");
403             }
404             argIndex++;
405         }
406 
407         fprintf(out, "    return event.write(LOG_ID_STATS);\n");
408         fprintf(out, "  } else {\n");
409         fprintf(out, "    return 1;\n");
410         fprintf(out, "  }\n");
411         fprintf(out, "}\n");
412         fprintf(out, "\n");
413     }
414 
415    for (set<vector<java_type_t>>::const_iterator signature = atoms.non_chained_signatures.begin();
416        signature != atoms.non_chained_signatures.end(); signature++) {
417        int argIndex;
418 
419        fprintf(out, "int\n");
420        fprintf(out, "stats_write_non_chained(int32_t code");
421        argIndex = 1;
422        for (vector<java_type_t>::const_iterator arg = signature->begin();
423            arg != signature->end(); arg++) {
424            fprintf(out, ", %s arg%d", cpp_type_name(*arg), argIndex);
425            argIndex++;
426        }
427        fprintf(out, ")\n");
428 
429        fprintf(out, "{\n");
430 
431        fprintf(out, "  int ret = 0;\n");
432        fprintf(out, "  for(int retry = 0; retry < 2; ++retry) {\n");
433        fprintf(out, "      ret =  try_stats_write_non_chained(code");
434 
435        argIndex = 1;
436        for (vector<java_type_t>::const_iterator arg = signature->begin();
437            arg != signature->end(); arg++) {
438            fprintf(out, ", arg%d",   argIndex);
439            argIndex++;
440        }
441        fprintf(out, ");\n");
442        fprintf(out, "      if (ret >= 0) { return retry; }\n");
443 
444        fprintf(out, "      {\n");
445        fprintf(out, "          std::lock_guard<std::mutex> lock(mLogdRetryMutex);\n");
446        fprintf(out, "          if ((android::elapsedRealtimeNano() - lastRetryTimestampNs) <= "
447                                 "kMinRetryIntervalNs) break;\n");
448        fprintf(out, "          lastRetryTimestampNs = android::elapsedRealtimeNano();\n");
449        fprintf(out, "      }\n");
450 
451        fprintf(out, "      std::this_thread::sleep_for(std::chrono::milliseconds(10));\n");
452        fprintf(out, "  }\n");
453        fprintf(out, "  return ret;\n");
454        fprintf(out, "}\n");
455 
456        fprintf(out, "\n");
457    }
458 
459 
460     // Print footer
461     fprintf(out, "\n");
462     fprintf(out, "} // namespace util\n");
463     fprintf(out, "} // namespace android\n");
464 
465     return 0;
466 }
467 
build_non_chained_decl_map(const Atoms & atoms,std::map<int,set<AtomDecl>::const_iterator> * decl_map)468 void build_non_chained_decl_map(const Atoms& atoms,
469                                 std::map<int, set<AtomDecl>::const_iterator>* decl_map){
470     for (set<AtomDecl>::const_iterator atom = atoms.non_chained_decls.begin();
471         atom != atoms.non_chained_decls.end(); atom++) {
472         decl_map->insert(std::make_pair(atom->code, atom));
473     }
474 }
475 
write_cpp_usage(FILE * out,const string & method_name,const string & atom_code_name,const AtomDecl & atom,const AtomDecl & attributionDecl)476 static void write_cpp_usage(
477     FILE* out, const string& method_name, const string& atom_code_name,
478     const AtomDecl& atom, const AtomDecl &attributionDecl) {
479     fprintf(out, "     * Usage: %s(StatsLog.%s", method_name.c_str(), atom_code_name.c_str());
480     for (vector<AtomField>::const_iterator field = atom.fields.begin();
481             field != atom.fields.end(); field++) {
482         if (field->javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
483             for (auto chainField : attributionDecl.fields) {
484                 if (chainField.javaType == JAVA_TYPE_STRING) {
485                     fprintf(out, ", const std::vector<%s>& %s",
486                          cpp_type_name(chainField.javaType),
487                          chainField.name.c_str());
488                 } else {
489                     fprintf(out, ", const %s* %s, size_t %s_length",
490                          cpp_type_name(chainField.javaType),
491                          chainField.name.c_str(), chainField.name.c_str());
492                 }
493             }
494         } else {
495             fprintf(out, ", %s %s", cpp_type_name(field->javaType), field->name.c_str());
496         }
497     }
498     fprintf(out, ");\n");
499 }
500 
write_cpp_method_header(FILE * out,const string & method_name,const set<vector<java_type_t>> & signatures,const AtomDecl & attributionDecl)501 static void write_cpp_method_header(
502     FILE* out, const string& method_name, const set<vector<java_type_t>>& signatures,
503     const AtomDecl &attributionDecl) {
504     for (set<vector<java_type_t>>::const_iterator signature = signatures.begin();
505             signature != signatures.end(); signature++) {
506         fprintf(out, "int %s(int32_t code ", method_name.c_str());
507         int argIndex = 1;
508         for (vector<java_type_t>::const_iterator arg = signature->begin();
509             arg != signature->end(); arg++) {
510             if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
511                 for (auto chainField : attributionDecl.fields) {
512                     if (chainField.javaType == JAVA_TYPE_STRING) {
513                         fprintf(out, ", const std::vector<%s>& %s",
514                             cpp_type_name(chainField.javaType), chainField.name.c_str());
515                     } else {
516                         fprintf(out, ", const %s* %s, size_t %s_length",
517                             cpp_type_name(chainField.javaType),
518                             chainField.name.c_str(), chainField.name.c_str());
519                     }
520                 }
521             } else {
522                 fprintf(out, ", %s arg%d", cpp_type_name(*arg), argIndex);
523             }
524             argIndex++;
525         }
526         fprintf(out, ");\n");
527 
528     }
529 }
530 
531 static int
write_stats_log_header(FILE * out,const Atoms & atoms,const AtomDecl & attributionDecl)532 write_stats_log_header(FILE* out, const Atoms& atoms, const AtomDecl &attributionDecl)
533 {
534     // Print prelude
535     fprintf(out, "// This file is autogenerated\n");
536     fprintf(out, "\n");
537     fprintf(out, "#pragma once\n");
538     fprintf(out, "\n");
539     fprintf(out, "#include <stdint.h>\n");
540     fprintf(out, "#include <vector>\n");
541     fprintf(out, "#include <map>\n");
542     fprintf(out, "#include <set>\n");
543     fprintf(out, "\n");
544 
545     fprintf(out, "namespace android {\n");
546     fprintf(out, "namespace util {\n");
547     fprintf(out, "\n");
548     fprintf(out, "/*\n");
549     fprintf(out, " * API For logging statistics events.\n");
550     fprintf(out, " */\n");
551     fprintf(out, "\n");
552     fprintf(out, "/**\n");
553     fprintf(out, " * Constants for atom codes.\n");
554     fprintf(out, " */\n");
555     fprintf(out, "enum {\n");
556 
557     std::map<int, set<AtomDecl>::const_iterator> atom_code_to_non_chained_decl_map;
558     build_non_chained_decl_map(atoms, &atom_code_to_non_chained_decl_map);
559 
560     size_t i = 0;
561     // Print constants
562     for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
563         atom != atoms.decls.end(); atom++) {
564         string constant = make_constant_name(atom->name);
565         fprintf(out, "\n");
566         fprintf(out, "    /**\n");
567         fprintf(out, "     * %s %s\n", atom->message.c_str(), atom->name.c_str());
568         write_cpp_usage(out, "stats_write", constant, *atom, attributionDecl);
569 
570         auto non_chained_decl = atom_code_to_non_chained_decl_map.find(atom->code);
571         if (non_chained_decl != atom_code_to_non_chained_decl_map.end()) {
572             write_cpp_usage(out, "stats_write_non_chained", constant, *non_chained_decl->second,
573                 attributionDecl);
574         }
575         fprintf(out, "     */\n");
576         char const* const comma = (i == atoms.decls.size() - 1) ? "" : ",";
577         fprintf(out, "    %s = %d%s\n", constant.c_str(), atom->code, comma);
578         if (atom->code < PULL_ATOM_START_ID && atom->code > maxPushedAtomId) {
579             maxPushedAtomId = atom->code;
580         }
581         i++;
582     }
583     fprintf(out, "\n");
584     fprintf(out, "};\n");
585     fprintf(out, "\n");
586 
587     fprintf(out, "struct StateAtomFieldOptions {\n");
588     fprintf(out, "  std::vector<int> primaryFields;\n");
589     fprintf(out, "  int exclusiveField;\n");
590     fprintf(out, "};\n");
591     fprintf(out, "\n");
592 
593     fprintf(out, "struct AtomsInfo {\n");
594     fprintf(out,
595             "  const static std::set<int> "
596             "kNotTruncatingTimestampAtomWhiteList;\n");
597     fprintf(out, "  const static std::map<int, int> kAtomsWithUidField;\n");
598     fprintf(out,
599             "  const static std::set<int> kAtomsWithAttributionChain;\n");
600     fprintf(out,
601             "  const static std::map<int, StateAtomFieldOptions> "
602             "kStateAtomsFieldOptions;\n");
603     fprintf(out, "};\n");
604 
605     fprintf(out, "const static int kMaxPushedAtomId = %d;\n\n",
606             maxPushedAtomId);
607 
608     // Print write methods
609     fprintf(out, "//\n");
610     fprintf(out, "// Write methods\n");
611     fprintf(out, "//\n");
612     write_cpp_method_header(out, "stats_write", atoms.signatures, attributionDecl);
613 
614     fprintf(out, "//\n");
615     fprintf(out, "// Write flattened methods\n");
616     fprintf(out, "//\n");
617     write_cpp_method_header(out, "stats_write_non_chained", atoms.non_chained_signatures,
618         attributionDecl);
619 
620     fprintf(out, "\n");
621     fprintf(out, "} // namespace util\n");
622     fprintf(out, "} // namespace android\n");
623 
624     return 0;
625 }
626 
write_java_usage(FILE * out,const string & method_name,const string & atom_code_name,const AtomDecl & atom,const AtomDecl & attributionDecl)627 static void write_java_usage(
628     FILE* out, const string& method_name, const string& atom_code_name,
629     const AtomDecl& atom, const AtomDecl &attributionDecl) {
630     fprintf(out, "     * Usage: StatsLog.%s(StatsLog.%s",
631         method_name.c_str(), atom_code_name.c_str());
632     for (vector<AtomField>::const_iterator field = atom.fields.begin();
633         field != atom.fields.end(); field++) {
634         if (field->javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
635             for (auto chainField : attributionDecl.fields) {
636                 fprintf(out, ", %s[] %s",
637                     java_type_name(chainField.javaType), chainField.name.c_str());
638             }
639         } else {
640             fprintf(out, ", %s %s", java_type_name(field->javaType), field->name.c_str());
641         }
642     }
643     fprintf(out, ");\n");
644 }
645 
write_java_method(FILE * out,const string & method_name,const set<vector<java_type_t>> & signatures,const AtomDecl & attributionDecl)646 static void write_java_method(
647     FILE* out, const string& method_name, const set<vector<java_type_t>>& signatures,
648     const AtomDecl &attributionDecl) {
649     for (set<vector<java_type_t>>::const_iterator signature = signatures.begin();
650         signature != signatures.end(); signature++) {
651         fprintf(out, "    public static native int %s(int code", method_name.c_str());
652         int argIndex = 1;
653         for (vector<java_type_t>::const_iterator arg = signature->begin();
654             arg != signature->end(); arg++) {
655             if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
656                 for (auto chainField : attributionDecl.fields) {
657                     fprintf(out, ", %s[] %s",
658                         java_type_name(chainField.javaType), chainField.name.c_str());
659                 }
660             } else {
661                 fprintf(out, ", %s arg%d", java_type_name(*arg), argIndex);
662             }
663             argIndex++;
664         }
665         fprintf(out, ");\n");
666     }
667 }
668 
669 
670 static int
write_stats_log_java(FILE * out,const Atoms & atoms,const AtomDecl & attributionDecl)671 write_stats_log_java(FILE* out, const Atoms& atoms, const AtomDecl &attributionDecl)
672 {
673     // Print prelude
674     fprintf(out, "// This file is autogenerated\n");
675     fprintf(out, "\n");
676     fprintf(out, "package android.util;\n");
677     fprintf(out, "\n");
678     fprintf(out, "\n");
679     fprintf(out, "/**\n");
680     fprintf(out, " * API For logging statistics events.\n");
681     fprintf(out, " * @hide\n");
682     fprintf(out, " */\n");
683     fprintf(out, "public class StatsLogInternal {\n");
684     fprintf(out, "    // Constants for atom codes.\n");
685 
686     std::map<int, set<AtomDecl>::const_iterator> atom_code_to_non_chained_decl_map;
687     build_non_chained_decl_map(atoms, &atom_code_to_non_chained_decl_map);
688 
689     // Print constants for the atom codes.
690     for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
691             atom != atoms.decls.end(); atom++) {
692         string constant = make_constant_name(atom->name);
693         fprintf(out, "\n");
694         fprintf(out, "    /**\n");
695         fprintf(out, "     * %s %s\n", atom->message.c_str(), atom->name.c_str());
696         write_java_usage(out, "write", constant, *atom, attributionDecl);
697         auto non_chained_decl = atom_code_to_non_chained_decl_map.find(atom->code);
698         if (non_chained_decl != atom_code_to_non_chained_decl_map.end()) {
699             write_java_usage(out, "write_non_chained", constant, *non_chained_decl->second,
700              attributionDecl);
701         }
702         fprintf(out, "     */\n");
703         fprintf(out, "    public static final int %s = %d;\n", constant.c_str(), atom->code);
704     }
705     fprintf(out, "\n");
706 
707     // Print constants for the enum values.
708     fprintf(out, "    // Constants for enum values.\n\n");
709     for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
710         atom != atoms.decls.end(); atom++) {
711         for (vector<AtomField>::const_iterator field = atom->fields.begin();
712             field != atom->fields.end(); field++) {
713             if (field->javaType == JAVA_TYPE_ENUM) {
714                 fprintf(out, "    // Values for %s.%s\n", atom->message.c_str(),
715                     field->name.c_str());
716                 for (map<int, string>::const_iterator value = field->enumValues.begin();
717                     value != field->enumValues.end(); value++) {
718                     fprintf(out, "    public static final int %s__%s__%s = %d;\n",
719                         make_constant_name(atom->message).c_str(),
720                         make_constant_name(field->name).c_str(),
721                         make_constant_name(value->second).c_str(),
722                         value->first);
723                 }
724                 fprintf(out, "\n");
725             }
726         }
727     }
728 
729     // Print write methods
730     fprintf(out, "    // Write methods\n");
731     write_java_method(out, "write", atoms.signatures, attributionDecl);
732     write_java_method(out, "write_non_chained", atoms.non_chained_signatures, attributionDecl);
733 
734     fprintf(out, "}\n");
735 
736     return 0;
737 }
738 
739 static const char*
jni_type_name(java_type_t type)740 jni_type_name(java_type_t type)
741 {
742     switch (type) {
743         case JAVA_TYPE_BOOLEAN:
744             return "jboolean";
745         case JAVA_TYPE_INT:
746         case JAVA_TYPE_ENUM:
747             return "jint";
748         case JAVA_TYPE_LONG:
749             return "jlong";
750         case JAVA_TYPE_FLOAT:
751             return "jfloat";
752         case JAVA_TYPE_DOUBLE:
753             return "jdouble";
754         case JAVA_TYPE_STRING:
755             return "jstring";
756         default:
757             return "UNKNOWN";
758     }
759 }
760 
761 static const char*
jni_array_type_name(java_type_t type)762 jni_array_type_name(java_type_t type)
763 {
764     switch (type) {
765         case JAVA_TYPE_INT:
766             return "jintArray";
767         case JAVA_TYPE_STRING:
768             return "jobjectArray";
769         default:
770             return "UNKNOWN";
771     }
772 }
773 
774 static string
jni_function_name(const string & method_name,const vector<java_type_t> & signature)775 jni_function_name(const string& method_name, const vector<java_type_t>& signature)
776 {
777     string result("StatsLog_" + method_name);
778     for (vector<java_type_t>::const_iterator arg = signature.begin();
779         arg != signature.end(); arg++) {
780         switch (*arg) {
781             case JAVA_TYPE_BOOLEAN:
782                 result += "_boolean";
783                 break;
784             case JAVA_TYPE_INT:
785             case JAVA_TYPE_ENUM:
786                 result += "_int";
787                 break;
788             case JAVA_TYPE_LONG:
789                 result += "_long";
790                 break;
791             case JAVA_TYPE_FLOAT:
792                 result += "_float";
793                 break;
794             case JAVA_TYPE_DOUBLE:
795                 result += "_double";
796                 break;
797             case JAVA_TYPE_STRING:
798                 result += "_String";
799                 break;
800             case JAVA_TYPE_ATTRIBUTION_CHAIN:
801               result += "_AttributionChain";
802               break;
803             default:
804                 result += "_UNKNOWN";
805                 break;
806         }
807     }
808     return result;
809 }
810 
811 static const char*
java_type_signature(java_type_t type)812 java_type_signature(java_type_t type)
813 {
814     switch (type) {
815         case JAVA_TYPE_BOOLEAN:
816             return "Z";
817         case JAVA_TYPE_INT:
818         case JAVA_TYPE_ENUM:
819             return "I";
820         case JAVA_TYPE_LONG:
821             return "J";
822         case JAVA_TYPE_FLOAT:
823             return "F";
824         case JAVA_TYPE_DOUBLE:
825             return "D";
826         case JAVA_TYPE_STRING:
827             return "Ljava/lang/String;";
828         default:
829             return "UNKNOWN";
830     }
831 }
832 
833 static string
jni_function_signature(const vector<java_type_t> & signature,const AtomDecl & attributionDecl)834 jni_function_signature(const vector<java_type_t>& signature, const AtomDecl &attributionDecl)
835 {
836     string result("(I");
837     for (vector<java_type_t>::const_iterator arg = signature.begin();
838         arg != signature.end(); arg++) {
839         if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
840             for (auto chainField : attributionDecl.fields) {
841                 result += "[";
842                 result += java_type_signature(chainField.javaType);
843             }
844         } else {
845             result += java_type_signature(*arg);
846         }
847     }
848     result += ")I";
849     return result;
850 }
851 
852 static int
write_stats_log_jni(FILE * out,const string & java_method_name,const string & cpp_method_name,const set<vector<java_type_t>> & signatures,const AtomDecl & attributionDecl)853 write_stats_log_jni(FILE* out, const string& java_method_name, const string& cpp_method_name,
854     const set<vector<java_type_t>>& signatures, const AtomDecl &attributionDecl)
855 {
856     // Print write methods
857     for (set<vector<java_type_t>>::const_iterator signature = signatures.begin();
858         signature != signatures.end(); signature++) {
859         int argIndex;
860 
861         fprintf(out, "static int\n");
862         fprintf(out, "%s(JNIEnv* env, jobject clazz UNUSED, jint code",
863                 jni_function_name(java_method_name, *signature).c_str());
864         argIndex = 1;
865         for (vector<java_type_t>::const_iterator arg = signature->begin();
866                 arg != signature->end(); arg++) {
867             if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
868                 for (auto chainField : attributionDecl.fields) {
869                     fprintf(out, ", %s %s", jni_array_type_name(chainField.javaType),
870                         chainField.name.c_str());
871                 }
872             } else {
873                 fprintf(out, ", %s arg%d", jni_type_name(*arg), argIndex);
874             }
875             argIndex++;
876         }
877         fprintf(out, ")\n");
878 
879         fprintf(out, "{\n");
880 
881         // Prepare strings
882         argIndex = 1;
883         bool hadStringOrChain = false;
884         for (vector<java_type_t>::const_iterator arg = signature->begin();
885                 arg != signature->end(); arg++) {
886             if (*arg == JAVA_TYPE_STRING) {
887                 hadStringOrChain = true;
888                 fprintf(out, "    const char* str%d;\n", argIndex);
889                 fprintf(out, "    if (arg%d != NULL) {\n", argIndex);
890                 fprintf(out, "        str%d = env->GetStringUTFChars(arg%d, NULL);\n",
891                         argIndex, argIndex);
892                 fprintf(out, "    } else {\n");
893                 fprintf(out, "        str%d = NULL;\n", argIndex);
894                 fprintf(out, "    }\n");
895             } else if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
896                 hadStringOrChain = true;
897                 for (auto chainField : attributionDecl.fields) {
898                     fprintf(out, "    size_t %s_length = env->GetArrayLength(%s);\n",
899                         chainField.name.c_str(), chainField.name.c_str());
900                     if (chainField.name != attributionDecl.fields.front().name) {
901                         fprintf(out, "    if (%s_length != %s_length) {\n",
902                             chainField.name.c_str(),
903                             attributionDecl.fields.front().name.c_str());
904                         fprintf(out, "        return -EINVAL;\n");
905                         fprintf(out, "    }\n");
906                     }
907                     if (chainField.javaType == JAVA_TYPE_INT) {
908                         fprintf(out, "    jint* %s_array = env->GetIntArrayElements(%s, NULL);\n",
909                             chainField.name.c_str(), chainField.name.c_str());
910                     } else if (chainField.javaType == JAVA_TYPE_STRING) {
911                         fprintf(out, "    std::vector<%s> %s_vec;\n",
912                             cpp_type_name(chainField.javaType), chainField.name.c_str());
913                         fprintf(out, "    std::vector<ScopedUtfChars*> scoped_%s_vec;\n",
914                             chainField.name.c_str());
915                         fprintf(out, "    for (size_t i = 0; i < %s_length; ++i) {\n",
916                             chainField.name.c_str());
917                         fprintf(out, "        jstring jstr = "
918                             "(jstring)env->GetObjectArrayElement(%s, i);\n",
919                              chainField.name.c_str());
920                         fprintf(out, "        if (jstr == NULL) {\n");
921                         fprintf(out, "            %s_vec.push_back(NULL);\n",
922                             chainField.name.c_str());
923                         fprintf(out, "        } else {\n");
924                         fprintf(out, "            ScopedUtfChars* scoped_%s = "
925                             "new ScopedUtfChars(env, jstr);\n",
926                              chainField.name.c_str());
927                         fprintf(out, "            %s_vec.push_back(scoped_%s->c_str());\n",
928                                 chainField.name.c_str(), chainField.name.c_str());
929                         fprintf(out, "            scoped_%s_vec.push_back(scoped_%s);\n",
930                                 chainField.name.c_str(), chainField.name.c_str());
931                         fprintf(out, "        }\n");
932                         fprintf(out, "    }\n");
933                     }
934                     fprintf(out, "\n");
935                 }
936             }
937             argIndex++;
938         }
939         // Emit this to quiet the unused parameter warning if there were no strings or attribution
940         // chains.
941         if (!hadStringOrChain) {
942             fprintf(out, "    (void)env;\n");
943         }
944 
945         // stats_write call
946         argIndex = 1;
947         fprintf(out, "   int ret =  android::util::%s(code", cpp_method_name.c_str());
948         for (vector<java_type_t>::const_iterator arg = signature->begin();
949                 arg != signature->end(); arg++) {
950             if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
951                 for (auto chainField : attributionDecl.fields) {
952                     if (chainField.javaType == JAVA_TYPE_INT) {
953                         fprintf(out, ", (const %s*)%s_array, %s_length",
954                             cpp_type_name(chainField.javaType),
955                             chainField.name.c_str(), chainField.name.c_str());
956                     } else if (chainField.javaType == JAVA_TYPE_STRING) {
957                         fprintf(out, ", %s_vec", chainField.name.c_str());
958                     }
959                 }
960             } else {
961                 const char *argName = (*arg == JAVA_TYPE_STRING) ? "str" : "arg";
962                 fprintf(out, ", (%s)%s%d", cpp_type_name(*arg), argName, argIndex);
963             }
964             argIndex++;
965         }
966         fprintf(out, ");\n");
967         fprintf(out, "\n");
968 
969         // Clean up strings
970         argIndex = 1;
971         for (vector<java_type_t>::const_iterator arg = signature->begin();
972                 arg != signature->end(); arg++) {
973             if (*arg == JAVA_TYPE_STRING) {
974                 fprintf(out, "    if (str%d != NULL) {\n", argIndex);
975                 fprintf(out, "        env->ReleaseStringUTFChars(arg%d, str%d);\n",
976                         argIndex, argIndex);
977                 fprintf(out, "    }\n");
978             } else if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
979                 for (auto chainField : attributionDecl.fields) {
980                     if (chainField.javaType == JAVA_TYPE_INT) {
981                         fprintf(out, "    env->ReleaseIntArrayElements(%s, %s_array, 0);\n",
982                             chainField.name.c_str(), chainField.name.c_str());
983                     } else if (chainField.javaType == JAVA_TYPE_STRING) {
984                         fprintf(out, "    for (size_t i = 0; i < scoped_%s_vec.size(); ++i) {\n",
985                             chainField.name.c_str());
986                         fprintf(out, "        delete scoped_%s_vec[i];\n", chainField.name.c_str());
987                         fprintf(out, "    }\n");
988                     }
989                 }
990             }
991             argIndex++;
992         }
993         fprintf(out, "    return ret;\n");
994 
995         fprintf(out, "}\n");
996         fprintf(out, "\n");
997     }
998 
999 
1000     return 0;
1001 }
1002 
write_jni_registration(FILE * out,const string & java_method_name,const set<vector<java_type_t>> & signatures,const AtomDecl & attributionDecl)1003 void write_jni_registration(FILE* out, const string& java_method_name,
1004     const set<vector<java_type_t>>& signatures, const AtomDecl &attributionDecl) {
1005     for (set<vector<java_type_t>>::const_iterator signature = signatures.begin();
1006             signature != signatures.end(); signature++) {
1007         fprintf(out, "    { \"%s\", \"%s\", (void*)%s },\n",
1008             java_method_name.c_str(),
1009             jni_function_signature(*signature, attributionDecl).c_str(),
1010             jni_function_name(java_method_name, *signature).c_str());
1011     }
1012 }
1013 
1014 static int
write_stats_log_jni(FILE * out,const Atoms & atoms,const AtomDecl & attributionDecl)1015 write_stats_log_jni(FILE* out, const Atoms& atoms, const AtomDecl &attributionDecl)
1016 {
1017     // Print prelude
1018     fprintf(out, "// This file is autogenerated\n");
1019     fprintf(out, "\n");
1020 
1021     fprintf(out, "#include <statslog.h>\n");
1022     fprintf(out, "\n");
1023     fprintf(out, "#include <nativehelper/JNIHelp.h>\n");
1024     fprintf(out, "#include <nativehelper/ScopedUtfChars.h>\n");
1025     fprintf(out, "#include <utils/Vector.h>\n");
1026     fprintf(out, "#include \"core_jni_helpers.h\"\n");
1027     fprintf(out, "#include \"jni.h\"\n");
1028     fprintf(out, "\n");
1029     fprintf(out, "#define UNUSED  __attribute__((__unused__))\n");
1030     fprintf(out, "\n");
1031 
1032     fprintf(out, "namespace android {\n");
1033     fprintf(out, "\n");
1034 
1035     write_stats_log_jni(out, "write", "stats_write", atoms.signatures, attributionDecl);
1036     write_stats_log_jni(out, "write_non_chained", "stats_write_non_chained",
1037         atoms.non_chained_signatures, attributionDecl);
1038 
1039     // Print registration function table
1040     fprintf(out, "/*\n");
1041     fprintf(out, " * JNI registration.\n");
1042     fprintf(out, " */\n");
1043     fprintf(out, "static const JNINativeMethod gRegisterMethods[] = {\n");
1044     write_jni_registration(out, "write", atoms.signatures, attributionDecl);
1045     write_jni_registration(out, "write_non_chained", atoms.non_chained_signatures, attributionDecl);
1046     fprintf(out, "};\n");
1047     fprintf(out, "\n");
1048 
1049     // Print registration function
1050     fprintf(out, "int register_android_util_StatsLog(JNIEnv* env) {\n");
1051     fprintf(out, "    return RegisterMethodsOrDie(\n");
1052     fprintf(out, "            env,\n");
1053     fprintf(out, "            \"android/util/StatsLog\",\n");
1054     fprintf(out, "            gRegisterMethods, NELEM(gRegisterMethods));\n");
1055     fprintf(out, "}\n");
1056 
1057     fprintf(out, "\n");
1058     fprintf(out, "} // namespace android\n");
1059     return 0;
1060 }
1061 
1062 static void
print_usage()1063 print_usage()
1064 {
1065     fprintf(stderr, "usage: stats-log-api-gen OPTIONS\n");
1066     fprintf(stderr, "\n");
1067     fprintf(stderr, "OPTIONS\n");
1068     fprintf(stderr, "  --cpp FILENAME       the header file to output\n");
1069     fprintf(stderr, "  --header FILENAME    the cpp file to output\n");
1070     fprintf(stderr, "  --help               this message\n");
1071     fprintf(stderr, "  --java FILENAME      the java file to output\n");
1072     fprintf(stderr, "  --jni FILENAME       the jni file to output\n");
1073 }
1074 
1075 /**
1076  * Do the argument parsing and execute the tasks.
1077  */
1078 static int
run(int argc,char const * const * argv)1079 run(int argc, char const*const* argv)
1080 {
1081     string cppFilename;
1082     string headerFilename;
1083     string javaFilename;
1084     string jniFilename;
1085 
1086     int index = 1;
1087     while (index < argc) {
1088         if (0 == strcmp("--help", argv[index])) {
1089             print_usage();
1090             return 0;
1091         } else if (0 == strcmp("--cpp", argv[index])) {
1092             index++;
1093             if (index >= argc) {
1094                 print_usage();
1095                 return 1;
1096             }
1097             cppFilename = argv[index];
1098         } else if (0 == strcmp("--header", argv[index])) {
1099             index++;
1100             if (index >= argc) {
1101                 print_usage();
1102                 return 1;
1103             }
1104             headerFilename = argv[index];
1105         } else if (0 == strcmp("--java", argv[index])) {
1106             index++;
1107             if (index >= argc) {
1108                 print_usage();
1109                 return 1;
1110             }
1111             javaFilename = argv[index];
1112         } else if (0 == strcmp("--jni", argv[index])) {
1113             index++;
1114             if (index >= argc) {
1115                 print_usage();
1116                 return 1;
1117             }
1118             jniFilename = argv[index];
1119         }
1120         index++;
1121     }
1122 
1123     if (cppFilename.size() == 0
1124             && headerFilename.size() == 0
1125             && javaFilename.size() == 0
1126             && jniFilename.size() == 0) {
1127         print_usage();
1128         return 1;
1129     }
1130 
1131     // Collate the parameters
1132     Atoms atoms;
1133     int errorCount = collate_atoms(Atom::descriptor(), &atoms);
1134     if (errorCount != 0) {
1135         return 1;
1136     }
1137 
1138     AtomDecl attributionDecl;
1139     vector<java_type_t> attributionSignature;
1140     collate_atom(android::os::statsd::AttributionNode::descriptor(),
1141                  &attributionDecl, &attributionSignature);
1142 
1143     // Write the .cpp file
1144     if (cppFilename.size() != 0) {
1145         FILE* out = fopen(cppFilename.c_str(), "w");
1146         if (out == NULL) {
1147             fprintf(stderr, "Unable to open file for write: %s\n", cppFilename.c_str());
1148             return 1;
1149         }
1150         errorCount = android::stats_log_api_gen::write_stats_log_cpp(
1151             out, atoms, attributionDecl);
1152         fclose(out);
1153     }
1154 
1155     // Write the .h file
1156     if (headerFilename.size() != 0) {
1157         FILE* out = fopen(headerFilename.c_str(), "w");
1158         if (out == NULL) {
1159             fprintf(stderr, "Unable to open file for write: %s\n", headerFilename.c_str());
1160             return 1;
1161         }
1162         errorCount = android::stats_log_api_gen::write_stats_log_header(
1163             out, atoms, attributionDecl);
1164         fclose(out);
1165     }
1166 
1167     // Write the .java file
1168     if (javaFilename.size() != 0) {
1169         FILE* out = fopen(javaFilename.c_str(), "w");
1170         if (out == NULL) {
1171             fprintf(stderr, "Unable to open file for write: %s\n", javaFilename.c_str());
1172             return 1;
1173         }
1174         errorCount = android::stats_log_api_gen::write_stats_log_java(
1175             out, atoms, attributionDecl);
1176         fclose(out);
1177     }
1178 
1179     // Write the jni file
1180     if (jniFilename.size() != 0) {
1181         FILE* out = fopen(jniFilename.c_str(), "w");
1182         if (out == NULL) {
1183             fprintf(stderr, "Unable to open file for write: %s\n", jniFilename.c_str());
1184             return 1;
1185         }
1186         errorCount = android::stats_log_api_gen::write_stats_log_jni(
1187             out, atoms, attributionDecl);
1188         fclose(out);
1189     }
1190 
1191     return 0;
1192 }
1193 
1194 }
1195 }
1196 
1197 /**
1198  * Main.
1199  */
1200 int
main(int argc,char const * const * argv)1201 main(int argc, char const*const* argv)
1202 {
1203     GOOGLE_PROTOBUF_VERIFY_VERSION;
1204 
1205     return android::stats_log_api_gen::run(argc, argv);
1206 }
1207