1 /*
2  * Copyright (C) 2019, The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "native_writer.h"
18 
19 #include <stdio.h>
20 
21 #include "Collation.h"
22 #include "utils.h"
23 
24 namespace android {
25 namespace stats_log_api_gen {
26 
write_native_annotation_constants(FILE * out)27 static void write_native_annotation_constants(FILE* out) {
28     fprintf(out, "// Annotation constants.\n");
29 
30     const map<AnnotationId, AnnotationStruct>& ANNOTATION_ID_CONSTANTS =
31             get_annotation_id_constants(ANNOTATION_CONSTANT_NAME_PREFIX);
32     for (const auto& [id, annotation] : ANNOTATION_ID_CONSTANTS) {
33         fprintf(out, "const uint8_t %s = %hhu;\n", annotation.name.c_str(), id);
34     }
35     fprintf(out, "\n");
36 }
37 
write_annotations(FILE * out,int argIndex,const FieldNumberToAtomDeclSet & fieldNumberToAtomDeclSet,const string & methodPrefix,const string & methodSuffix,const int minApiLevel)38 static void write_annotations(FILE* out, int argIndex,
39                               const FieldNumberToAtomDeclSet& fieldNumberToAtomDeclSet,
40                               const string& methodPrefix, const string& methodSuffix,
41                               const int minApiLevel) {
42     const FieldNumberToAtomDeclSet::const_iterator fieldNumberToAtomDeclSetIt =
43             fieldNumberToAtomDeclSet.find(argIndex);
44     if (fieldNumberToAtomDeclSet.end() == fieldNumberToAtomDeclSetIt) {
45         return;
46     }
47     const AtomDeclSet& atomDeclSet = fieldNumberToAtomDeclSetIt->second;
48     const map<AnnotationId, AnnotationStruct>& ANNOTATION_ID_CONSTANTS =
49             get_annotation_id_constants(ANNOTATION_CONSTANT_NAME_PREFIX);
50     const string constantPrefix = minApiLevel > API_R ? "ASTATSLOG_" : "";
51     for (const shared_ptr<AtomDecl>& atomDecl : atomDeclSet) {
52         const string atomConstant = make_constant_name(atomDecl->name);
53         fprintf(out, "    if (%s == code) {\n", atomConstant.c_str());
54         const AnnotationSet& annotations = atomDecl->fieldNumberToAnnotations.at(argIndex);
55         int resetState = -1;
56         int defaultState = -1;
57         for (const shared_ptr<Annotation>& annotation : annotations) {
58             const string& annotationConstant =
59                     ANNOTATION_ID_CONSTANTS.at(annotation->annotationId).name;
60             switch (annotation->type) {
61                 case ANNOTATION_TYPE_INT:
62                     if (ANNOTATION_ID_TRIGGER_STATE_RESET == annotation->annotationId) {
63                         resetState = annotation->value.intValue;
64                     } else if (ANNOTATION_ID_DEFAULT_STATE == annotation->annotationId) {
65                         defaultState = annotation->value.intValue;
66                     } else if (ANNOTATION_ID_RESTRICTION_CATEGORY == annotation->annotationId) {
67                         fprintf(out, "        %saddInt32Annotation(%s%s%s,\n",
68                                 methodPrefix.c_str(), methodSuffix.c_str(), constantPrefix.c_str(),
69                                 annotationConstant.c_str());
70                         fprintf(out, "                                       %s%s);\n",
71                                 constantPrefix.c_str(),
72                                 get_restriction_category_str(annotation->value.intValue).c_str());
73                     } else {
74                         fprintf(out, "        %saddInt32Annotation(%s%s%s, %d);\n",
75                                 methodPrefix.c_str(), methodSuffix.c_str(), constantPrefix.c_str(),
76                                 annotationConstant.c_str(), annotation->value.intValue);
77                     }
78                     break;
79                 case ANNOTATION_TYPE_BOOL:
80                     fprintf(out, "        %saddBoolAnnotation(%s%s%s, %s);\n", methodPrefix.c_str(),
81                             methodSuffix.c_str(), constantPrefix.c_str(),
82                             annotationConstant.c_str(),
83                             annotation->value.boolValue ? "true" : "false");
84                     break;
85                 default:
86                     break;
87             }
88         }
89         if (defaultState != -1 && resetState != -1) {
90             const string& annotationConstant =
91                     ANNOTATION_ID_CONSTANTS.at(ANNOTATION_ID_TRIGGER_STATE_RESET).name;
92             fprintf(out, "        if (arg%d == %d) {\n", argIndex, resetState);
93             fprintf(out, "            %saddInt32Annotation(%s%s%s, %d);\n", methodPrefix.c_str(),
94                     methodSuffix.c_str(), constantPrefix.c_str(), annotationConstant.c_str(),
95                     defaultState);
96             fprintf(out, "        }\n");
97         }
98         fprintf(out, "    }\n");
99     }
100 }
101 
write_native_method_body(FILE * out,const vector<java_type_t> & signature,const FieldNumberToAtomDeclSet & fieldNumberToAtomDeclSet,const AtomDecl & attributionDecl,const int minApiLevel)102 static int write_native_method_body(FILE* out, const vector<java_type_t>& signature,
103                                     const FieldNumberToAtomDeclSet& fieldNumberToAtomDeclSet,
104                                     const AtomDecl& attributionDecl, const int minApiLevel) {
105     int argIndex = 1;
106     fprintf(out, "    AStatsEvent_setAtomId(event, code);\n");
107     write_annotations(out, ATOM_ID_FIELD_NUMBER, fieldNumberToAtomDeclSet, "AStatsEvent_",
108                       "event, ", minApiLevel);
109     for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
110          arg++) {
111         if (minApiLevel < API_T && is_repeated_field(*arg)) {
112             fprintf(stderr, "Found repeated field type with min api level < T.");
113             return 1;
114         }
115         switch (*arg) {
116             case JAVA_TYPE_ATTRIBUTION_CHAIN: {
117                 const char* uidName = attributionDecl.fields.front().name.c_str();
118                 const char* tagName = attributionDecl.fields.back().name.c_str();
119                 fprintf(out,
120                         "    AStatsEvent_writeAttributionChain(event, "
121                         "reinterpret_cast<const uint32_t*>(%s), %s.data(), "
122                         "static_cast<uint8_t>(%s_length));\n",
123                         uidName, tagName, uidName);
124                 break;
125             }
126             case JAVA_TYPE_BYTE_ARRAY:
127                 fprintf(out,
128                         "    AStatsEvent_writeByteArray(event, "
129                         "reinterpret_cast<const uint8_t*>(arg%d.arg), "
130                         "arg%d.arg_length);\n",
131                         argIndex, argIndex);
132                 break;
133             case JAVA_TYPE_BOOLEAN:
134                 fprintf(out, "    AStatsEvent_writeBool(event, arg%d);\n", argIndex);
135                 break;
136             case JAVA_TYPE_INT:
137                 [[fallthrough]];
138             case JAVA_TYPE_ENUM:
139                 fprintf(out, "    AStatsEvent_writeInt32(event, arg%d);\n", argIndex);
140                 break;
141             case JAVA_TYPE_FLOAT:
142                 fprintf(out, "    AStatsEvent_writeFloat(event, arg%d);\n", argIndex);
143                 break;
144             case JAVA_TYPE_LONG:
145                 fprintf(out, "    AStatsEvent_writeInt64(event, arg%d);\n", argIndex);
146                 break;
147             case JAVA_TYPE_STRING:
148                 fprintf(out, "    AStatsEvent_writeString(event, arg%d);\n", argIndex);
149                 break;
150             case JAVA_TYPE_BOOLEAN_ARRAY:
151                 fprintf(out, "    AStatsEvent_writeBoolArray(event, arg%d, arg%d_length);\n",
152                         argIndex, argIndex);
153                 break;
154             case JAVA_TYPE_INT_ARRAY:
155                 [[fallthrough]];
156             case JAVA_TYPE_ENUM_ARRAY:
157                 fprintf(out,
158                         "    AStatsEvent_writeInt32Array(event, arg%d.data(), arg%d.size());\n",
159                         argIndex, argIndex);
160                 break;
161             case JAVA_TYPE_FLOAT_ARRAY:
162                 fprintf(out,
163                         "    AStatsEvent_writeFloatArray(event, arg%d.data(), arg%d.size());\n",
164                         argIndex, argIndex);
165                 break;
166             case JAVA_TYPE_LONG_ARRAY:
167                 fprintf(out,
168                         "    AStatsEvent_writeInt64Array(event, arg%d.data(), arg%d.size());\n",
169                         argIndex, argIndex);
170                 break;
171             case JAVA_TYPE_STRING_ARRAY:
172                 fprintf(out,
173                         "    AStatsEvent_writeStringArray(event, arg%d.data(), arg%d.size());\n",
174                         argIndex, argIndex);
175                 break;
176 
177             default:
178                 // Unsupported types: OBJECT, DOUBLE
179                 fprintf(stderr, "Encountered unsupported type.\n");
180                 return 1;
181         }
182         write_annotations(out, argIndex, fieldNumberToAtomDeclSet, "AStatsEvent_", "event, ",
183                           minApiLevel);
184         argIndex++;
185     }
186     return 0;
187 }
188 
write_native_method_call(FILE * out,const string & methodName,const vector<java_type_t> & signature,const AtomDecl & attributionDecl,int argIndex)189 static void write_native_method_call(FILE* out, const string& methodName,
190                                      const vector<java_type_t>& signature,
191                                      const AtomDecl& attributionDecl, int argIndex) {
192     fprintf(out, "%s(code", methodName.c_str());
193     for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
194          arg++) {
195         if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
196             for (const auto& chainField : attributionDecl.fields) {
197                 if (chainField.javaType == JAVA_TYPE_STRING) {
198                     fprintf(out, ", %s", chainField.name.c_str());
199                 } else {
200                     fprintf(out, ",  %s,  %s_length", chainField.name.c_str(),
201                             chainField.name.c_str());
202                 }
203             }
204         } else {
205             fprintf(out, ", arg%d", argIndex);
206 
207             if (*arg == JAVA_TYPE_BOOLEAN_ARRAY) {
208                 fprintf(out, ", arg%d_length", argIndex);
209             }
210         }
211         argIndex++;
212     }
213     fprintf(out, ");\n");
214 }
215 
write_native_stats_write_methods(FILE * out,const SignatureInfoMap & signatureInfoMap,const AtomDecl & attributionDecl,const int minApiLevel,bool bootstrap)216 static int write_native_stats_write_methods(FILE* out, const SignatureInfoMap& signatureInfoMap,
217                                             const AtomDecl& attributionDecl, const int minApiLevel,
218                                             bool bootstrap) {
219     fprintf(out, "\n");
220     for (const auto& [signature, fieldNumberToAtomDeclSet] : signatureInfoMap) {
221         write_native_method_signature(out, "int stats_write(", signature, attributionDecl, " {");
222 
223         // Write method body.
224         if (bootstrap) {
225             fprintf(out, "    ::android::os::StatsBootstrapAtom atom;\n");
226             fprintf(out, "    atom.atomId = code;\n");
227             const FieldNumberToAtomDeclSet::const_iterator fieldNumberToAtomDeclSetIt =
228                     fieldNumberToAtomDeclSet.find(ATOM_ID_FIELD_NUMBER);
229             if (fieldNumberToAtomDeclSet.end() != fieldNumberToAtomDeclSetIt) {
230                 fprintf(stderr, "Bootstrap atoms do not support annotations\n");
231                 return 1;
232             }
233             int argIndex = 1;
234             const char* atomVal = "::android::os::StatsBootstrapAtomValue::";
235             for (vector<java_type_t>::const_iterator arg = signature.begin();
236                  arg != signature.end(); arg++) {
237                 switch (*arg) {
238                     case JAVA_TYPE_BYTE_ARRAY:
239                         fprintf(out,
240                                 "    const uint8_t* arg%dbyte = reinterpret_cast<const "
241                                 "uint8_t*>(arg%d.arg);\n",
242                                 argIndex, argIndex);
243                         fprintf(out,
244                                 "    "
245                                 "atom.values.push_back(%smake<%sbytesValue>(std::vector(arg%dbyte, "
246                                 "arg%dbyte + arg%d.arg_length)));\n",
247                                 atomVal, atomVal, argIndex, argIndex, argIndex);
248                         break;
249                     case JAVA_TYPE_BOOLEAN:
250                         fprintf(out, "    atom.values.push_back(%smake<%sboolValue>(arg%d));\n",
251                                 atomVal, atomVal, argIndex);
252                         break;
253                     case JAVA_TYPE_INT:  // Fall through.
254                     case JAVA_TYPE_ENUM:
255                         fprintf(out, "    atom.values.push_back(%smake<%sintValue>(arg%d));\n",
256                                 atomVal, atomVal, argIndex);
257                         break;
258                     case JAVA_TYPE_FLOAT:
259                         fprintf(out, "    atom.values.push_back(%smake<%sfloatValue>(arg%d));\n",
260                                 atomVal, atomVal, argIndex);
261                         break;
262                     case JAVA_TYPE_LONG:
263                         fprintf(out, "    atom.values.push_back(%smake<%slongValue>(arg%d));\n",
264                                 atomVal, atomVal, argIndex);
265                         break;
266                     case JAVA_TYPE_STRING:
267                         fprintf(out,
268                                 "    atom.values.push_back(%smake<%sstringValue>("
269                                 "::android::String16(arg%d)));\n",
270                                 atomVal, atomVal, argIndex);
271                         break;
272                     default:
273                         // Unsupported types: OBJECT, DOUBLE, ATTRIBUTION_CHAIN,
274                         // and all repeated fields
275                         fprintf(stderr, "Encountered unsupported type.\n");
276                         return 1;
277                 }
278                 const FieldNumberToAtomDeclSet::const_iterator fieldNumberToAtomDeclSetIt =
279                         fieldNumberToAtomDeclSet.find(argIndex);
280                 if (fieldNumberToAtomDeclSet.end() != fieldNumberToAtomDeclSetIt) {
281                     fprintf(stderr, "Bootstrap atoms do not support annotations\n");
282                     return 1;
283                 }
284                 argIndex++;
285             }
286             fprintf(out,
287                     "    bool success = "
288                     "::android::os::stats::StatsBootstrapAtomClient::reportBootstrapAtom(atom);\n");
289             fprintf(out, "    return success? 0 : -1;\n");
290 
291         } else if (minApiLevel == API_Q) {
292             int argIndex = 1;
293             fprintf(out, "    StatsEventCompat event;\n");
294             fprintf(out, "    event.setAtomId(code);\n");
295             write_annotations(out, ATOM_ID_FIELD_NUMBER, fieldNumberToAtomDeclSet, "event.", "",
296                               minApiLevel);
297             for (vector<java_type_t>::const_iterator arg = signature.begin();
298                  arg != signature.end(); arg++) {
299                 switch (*arg) {
300                     case JAVA_TYPE_ATTRIBUTION_CHAIN: {
301                         const char* uidName = attributionDecl.fields.front().name.c_str();
302                         const char* tagName = attributionDecl.fields.back().name.c_str();
303                         fprintf(out, "    event.writeAttributionChain(%s, %s_length, %s);\n",
304                                 uidName, uidName, tagName);
305                         break;
306                     }
307                     case JAVA_TYPE_BYTE_ARRAY:
308                         fprintf(out, "    event.writeByteArray(arg%d.arg, arg%d.arg_length);\n",
309                                 argIndex, argIndex);
310                         break;
311                     case JAVA_TYPE_BOOLEAN:
312                         fprintf(out, "    event.writeBool(arg%d);\n", argIndex);
313                         break;
314                     case JAVA_TYPE_INT:  // Fall through.
315                     case JAVA_TYPE_ENUM:
316                         fprintf(out, "    event.writeInt32(arg%d);\n", argIndex);
317                         break;
318                     case JAVA_TYPE_FLOAT:
319                         fprintf(out, "    event.writeFloat(arg%d);\n", argIndex);
320                         break;
321                     case JAVA_TYPE_LONG:
322                         fprintf(out, "    event.writeInt64(arg%d);\n", argIndex);
323                         break;
324                     case JAVA_TYPE_STRING:
325                         fprintf(out, "    event.writeString(arg%d);\n", argIndex);
326                         break;
327                     default:
328                         // Unsupported types: OBJECT, DOUBLE, and all repeated
329                         // fields.
330                         fprintf(stderr, "Encountered unsupported type.\n");
331                         return 1;
332                 }
333                 write_annotations(out, argIndex, fieldNumberToAtomDeclSet, "event.", "",
334                                   minApiLevel);
335                 argIndex++;
336             }
337             fprintf(out, "    return event.writeToSocket();\n");  // end method body.
338         } else {
339             fprintf(out, "    AStatsEvent* event = AStatsEvent_obtain();\n");
340             const int ret = write_native_method_body(out, signature, fieldNumberToAtomDeclSet,
341                                                      attributionDecl, minApiLevel);
342             if (ret != 0) {
343                 return ret;
344             }
345             fprintf(out, "    const int ret = AStatsEvent_write(event);\n");
346             fprintf(out, "    AStatsEvent_release(event);\n");
347             fprintf(out, "    return ret;\n");  // end method body.
348         }
349         fprintf(out, "}\n\n");  // end method.
350     }
351     return 0;
352 }
353 
write_native_stats_write_non_chained_methods(FILE * out,const SignatureInfoMap & signatureInfoMap,const AtomDecl & attributionDecl)354 static void write_native_stats_write_non_chained_methods(FILE* out,
355                                                          const SignatureInfoMap& signatureInfoMap,
356                                                          const AtomDecl& attributionDecl) {
357     fprintf(out, "\n");
358     for (const auto& [signature, _] : signatureInfoMap) {
359         write_native_method_signature(out, "int stats_write_non_chained(", signature,
360                                       attributionDecl, " {");
361 
362         vector<java_type_t> newSignature;
363 
364         // First two args form the attribution node so size goes down by 1.
365         newSignature.reserve(signature.size() - 1);
366 
367         // First arg is Attribution Chain.
368         newSignature.push_back(JAVA_TYPE_ATTRIBUTION_CHAIN);
369 
370         // Followed by the originial signature except the first 2 args.
371         newSignature.insert(newSignature.end(), signature.begin() + 2, signature.end());
372 
373         const char* uidName = attributionDecl.fields.front().name.c_str();
374         const char* tagName = attributionDecl.fields.back().name.c_str();
375         fprintf(out, "    const int32_t* %s = &arg1;\n", uidName);
376         fprintf(out, "    const size_t %s_length = 1;\n", uidName);
377         fprintf(out, "    const std::vector<char const*> %s(1, arg2);\n", tagName);
378         fprintf(out, "    return ");
379         write_native_method_call(out, "stats_write", newSignature, attributionDecl, 2);
380 
381         fprintf(out, "}\n\n");
382     }
383 }
384 
write_native_build_stats_event_methods(FILE * out,const SignatureInfoMap & signatureInfoMap,const AtomDecl & attributionDecl,const int minApiLevel)385 static int write_native_build_stats_event_methods(FILE* out,
386                                                   const SignatureInfoMap& signatureInfoMap,
387                                                   const AtomDecl& attributionDecl,
388                                                   const int minApiLevel) {
389     fprintf(out, "\n");
390     for (const auto& [signature, fieldNumberToAtomDeclSet] : signatureInfoMap) {
391         write_native_method_signature(out, "void addAStatsEvent(AStatsEventList* pulled_data, ",
392                                       signature, attributionDecl, " {");
393 
394         fprintf(out, "    AStatsEvent* event = AStatsEventList_addStatsEvent(pulled_data);\n");
395         const int ret = write_native_method_body(out, signature, fieldNumberToAtomDeclSet,
396                                                  attributionDecl, minApiLevel);
397         if (ret != 0) {
398             return ret;
399         }
400         fprintf(out, "    AStatsEvent_build(event);\n");  // end method body.
401 
402         fprintf(out, "}\n\n");  // end method.
403     }
404     return 0;
405 }
406 
write_stats_log_cpp(FILE * out,const Atoms & atoms,const AtomDecl & attributionDecl,const string & cppNamespace,const string & importHeader,const int minApiLevel,bool bootstrap)407 int write_stats_log_cpp(FILE* out, const Atoms& atoms, const AtomDecl& attributionDecl,
408                         const string& cppNamespace, const string& importHeader,
409                         const int minApiLevel, bool bootstrap) {
410     // Print prelude
411     fprintf(out, "// This file is autogenerated\n");
412     fprintf(out, "\n");
413 
414     fprintf(out, "#include <%s>\n", importHeader.c_str());
415     if (!bootstrap) {
416         if (minApiLevel == API_Q) {
417             fprintf(out, "#include <StatsEventCompat.h>\n");
418         } else {
419             fprintf(out, "#include <stats_event.h>\n");
420         }
421 
422         if (minApiLevel > API_R) {
423             fprintf(out, "#include <stats_annotations.h>\n");
424         }
425 
426         if (minApiLevel > API_Q && !atoms.pulledAtomsSignatureInfoMap.empty()) {
427             fprintf(out, "#include <stats_pull_atom_callback.h>\n");
428         }
429     } else {
430         fprintf(out, "#include <StatsBootstrapAtomClient.h>\n");
431         fprintf(out, "#include <android/os/StatsBootstrapAtom.h>\n");
432         fprintf(out, "#include <utils/String16.h>\n");
433     }
434 
435     fprintf(out, "\n");
436     write_namespace(out, cppNamespace);
437 
438     int ret = write_native_stats_write_methods(out, atoms.signatureInfoMap, attributionDecl,
439                                                minApiLevel, bootstrap);
440     if (ret != 0) {
441         return ret;
442     }
443     if (!bootstrap) {
444         write_native_stats_write_non_chained_methods(out, atoms.nonChainedSignatureInfoMap,
445                                                      attributionDecl);
446         ret = write_native_build_stats_event_methods(out, atoms.pulledAtomsSignatureInfoMap,
447                                                      attributionDecl, minApiLevel);
448         if (ret != 0) {
449             return ret;
450         }
451     }
452 
453     // Print footer
454     fprintf(out, "\n");
455     write_closing_namespace(out, cppNamespace);
456 
457     return 0;
458 }
459 
write_stats_log_header(FILE * out,const Atoms & atoms,const AtomDecl & attributionDecl,const string & cppNamespace,const int minApiLevel,bool bootstrap)460 int write_stats_log_header(FILE* out, const Atoms& atoms, const AtomDecl& attributionDecl,
461                            const string& cppNamespace, const int minApiLevel, bool bootstrap) {
462     const bool includePull = !atoms.pulledAtomsSignatureInfoMap.empty() && !bootstrap;
463     write_native_header_preamble(out, cppNamespace, includePull);
464     write_native_atom_constants(out, atoms, attributionDecl);
465     write_native_atom_enums(out, atoms);
466 
467     if (minApiLevel <= API_R) {
468         write_native_annotation_constants(out);
469     }
470 
471     fprintf(out, "struct BytesField {\n");
472     fprintf(out,
473             "  BytesField(char const* array, size_t len) : arg(array), "
474             "arg_length(len) {}\n");
475     fprintf(out, "  char const* arg;\n");
476     fprintf(out, "  size_t arg_length;\n");
477     fprintf(out, "};\n");
478     fprintf(out, "\n");
479 
480     // Print write methods
481     fprintf(out, "//\n");
482     fprintf(out, "// Write methods\n");
483     fprintf(out, "//\n");
484     write_native_method_header(out, "int stats_write(", atoms.signatureInfoMap, attributionDecl);
485     fprintf(out, "\n");
486 
487     // Attribution chains and pulled atoms are not supported for bootstrap processes.
488     if (!bootstrap) {
489         fprintf(out, "//\n");
490         fprintf(out, "// Write flattened methods\n");
491         fprintf(out, "//\n");
492         write_native_method_header(out, "int stats_write_non_chained(",
493                                    atoms.nonChainedSignatureInfoMap, attributionDecl);
494         fprintf(out, "\n");
495 
496         // Print pulled atoms methods.
497         fprintf(out, "//\n");
498         fprintf(out, "// Add AStatsEvent methods\n");
499         fprintf(out, "//\n");
500         write_native_method_header(out, "void addAStatsEvent(AStatsEventList* pulled_data, ",
501                                    atoms.pulledAtomsSignatureInfoMap, attributionDecl);
502         fprintf(out, "\n");
503     }
504 
505     write_native_header_epilogue(out, cppNamespace);
506 
507     return 0;
508 }
509 
510 }  // namespace stats_log_api_gen
511 }  // namespace android
512