1 
2 #include <google/protobuf/descriptor.h>
3 #include <google/protobuf/stubs/common.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <string.h>
7 
8 #include <cstdlib>
9 #include <filesystem>
10 
11 #include "Collation.h"
12 #include "frameworks/proto_logging/stats/atoms.pb.h"
13 #include "frameworks/proto_logging/stats/attribution_node.pb.h"
14 #include "java_writer.h"
15 #include "native_writer.h"
16 #include "rust_writer.h"
17 #include "utils.h"
18 
19 #ifdef WITH_VENDOR
20 #include "java_writer_vendor.h"
21 #include "native_writer_vendor.h"
22 #endif
23 
24 namespace android {
25 namespace stats_log_api_gen {
26 
27 namespace fs = std::filesystem;
28 using android::os::statsd::Atom;
29 
print_usage()30 static void print_usage() {
31     fprintf(stderr, "usage: stats-log-api-gen OPTIONS\n");
32     fprintf(stderr, "\n");
33     fprintf(stderr, "OPTIONS\n");
34     fprintf(stderr, "  --cpp FILENAME       the cpp file to output for write helpers\n");
35     fprintf(stderr, "  --header FILENAME    the header file to output for write helpers\n");
36     fprintf(stderr, "  --help               this message\n");
37     fprintf(stderr, "  --java FILENAME      the java file to output\n");
38     fprintf(stderr, "  --rust FILENAME      the rust file to output\n");
39     fprintf(stderr, "  --rustHeader FILENAME the rust file to output for write helpers\n");
40     fprintf(stderr,
41             "  --rustHeaderCrate NAME        header crate to be used while "
42             "generating the code. Note: this should be the same as the crate_name "
43             "created by rust_library for the header \n");
44     fprintf(stderr, "  --module NAME        optional, module name to generate outputs for\n");
45     fprintf(stderr,
46             "  --namespace COMMA,SEP,NAMESPACE   required for cpp/header with "
47             "module\n");
48     fprintf(stderr,
49             "                                    comma separated namespace of "
50             "the files\n");
51     fprintf(stderr,
52             "  --importHeader NAME  required for cpp/jni to say which header to "
53             "import "
54             "for write helpers\n");
55     fprintf(stderr, "  --javaPackage PACKAGE             the package for the java file.\n");
56     fprintf(stderr, "                                    required for java with module\n");
57     fprintf(stderr, "  --javaClass CLASS    the class name of the java class.\n");
58     fprintf(stderr, "  --minApiLevel API_LEVEL           lowest API level to support.\n");
59     fprintf(stderr, "                                    Default is \"current\".\n");
60     fprintf(stderr,
61             "  --worksource         Include support for logging WorkSource "
62             "objects.\n");
63     fprintf(stderr, "                                        Default is \"current\".\n");
64     fprintf(stderr,
65             "  --bootstrap          If this logging is from a bootstrap process. "
66             "Only supported for cpp. Do not use unless necessary.\n");
67 #ifdef WITH_VENDOR
68     fprintf(stderr,
69             "  --vendor-proto       Path to the proto file for vendor atoms logging\n"
70             "code generation.\n");
71 #endif
72 }
73 
74 /**
75  * Do the argument parsing and execute the tasks.
76  */
run(int argc,char const * const * argv)77 static int run(int argc, char const* const* argv) {
78     string cppFilename;
79     string headerFilename;
80     string javaFilename;
81     string javaPackage;
82     string javaClass;
83     string rustFilename;
84     string rustHeaderFilename;
85     string rustHeaderCrate;
86     string moduleName = DEFAULT_MODULE_NAME;
87     string cppNamespace = DEFAULT_CPP_NAMESPACE;
88     string cppHeaderImport = DEFAULT_CPP_HEADER_IMPORT;
89     string vendorProto;
90     bool supportWorkSource = false;
91     int minApiLevel = API_LEVEL_CURRENT;
92     bool bootstrap = false;
93 
94     int index = 1;
95     while (index < argc) {
96         if (0 == strcmp("--help", argv[index])) {
97             print_usage();
98             return 0;
99         } else if (0 == strcmp("--cpp", argv[index])) {
100             index++;
101             if (index >= argc) {
102                 print_usage();
103                 return 1;
104             }
105             cppFilename = argv[index];
106         } else if (0 == strcmp("--header", argv[index])) {
107             index++;
108             if (index >= argc) {
109                 print_usage();
110                 return 1;
111             }
112             headerFilename = argv[index];
113         } else if (0 == strcmp("--java", argv[index])) {
114             index++;
115             if (index >= argc) {
116                 print_usage();
117                 return 1;
118             }
119             javaFilename = argv[index];
120         } else if (0 == strcmp("--rust", argv[index])) {
121             index++;
122             if (index >= argc) {
123                 print_usage();
124                 return 1;
125             }
126             rustFilename = argv[index];
127         } else if (0 == strcmp("--rustHeader", argv[index])) {
128             index++;
129             if (index >= argc) {
130                 print_usage();
131                 return 1;
132             }
133             rustHeaderFilename = argv[index];
134         } else if (0 == strcmp("--rustHeaderCrate", argv[index])) {
135             index++;
136             if (index >= argc) {
137                 print_usage();
138                 return 1;
139             }
140             rustHeaderCrate = argv[index];
141         } else if (0 == strcmp("--module", argv[index])) {
142             index++;
143             if (index >= argc) {
144                 print_usage();
145                 return 1;
146             }
147             moduleName = argv[index];
148         } else if (0 == strcmp("--namespace", argv[index])) {
149             index++;
150             if (index >= argc) {
151                 print_usage();
152                 return 1;
153             }
154             cppNamespace = argv[index];
155         } else if (0 == strcmp("--importHeader", argv[index])) {
156             index++;
157             if (index >= argc) {
158                 print_usage();
159                 return 1;
160             }
161             cppHeaderImport = argv[index];
162         } else if (0 == strcmp("--javaPackage", argv[index])) {
163             index++;
164             if (index >= argc) {
165                 print_usage();
166                 return 1;
167             }
168             javaPackage = argv[index];
169         } else if (0 == strcmp("--javaClass", argv[index])) {
170             index++;
171             if (index >= argc) {
172                 print_usage();
173                 return 1;
174             }
175             javaClass = argv[index];
176         } else if (0 == strcmp("--supportQ", argv[index])) {
177             minApiLevel = API_Q;
178         } else if (0 == strcmp("--worksource", argv[index])) {
179             supportWorkSource = true;
180         } else if (0 == strcmp("--minApiLevel", argv[index])) {
181             index++;
182             if (index >= argc) {
183                 print_usage();
184                 return 1;
185             }
186             if (0 != strcmp("current", argv[index])) {
187                 minApiLevel = atoi(argv[index]);
188             }
189         } else if (0 == strcmp("--bootstrap", argv[index])) {
190             bootstrap = true;
191 #ifdef WITH_VENDOR
192         } else if (0 == strcmp("--vendor-proto", argv[index])) {
193             index++;
194             if (index >= argc) {
195                 print_usage();
196                 return 1;
197             }
198 
199             vendorProto = argv[index];
200 #endif
201         }
202 
203         index++;
204     }
205     if (index < argc) {
206         fprintf(stderr, "Error: Unknown command line argument\n");
207         print_usage();
208         return 1;
209     }
210 
211     if (cppFilename.empty() && headerFilename.empty() && javaFilename.empty() &&
212         rustFilename.empty() && rustHeaderFilename.empty()) {
213         print_usage();
214         return 1;
215     }
216     if (DEFAULT_MODULE_NAME == moduleName && minApiLevel != API_LEVEL_CURRENT) {
217         // Default module only supports current API level.
218         fprintf(stderr, "%s cannot support older API levels\n", moduleName.c_str());
219         return 1;
220     }
221 
222     if (minApiLevel < API_Q) {
223         // Cannot support pre-Q.
224         fprintf(stderr, "minApiLevel must be %d or higher.\n", API_Q);
225         return 1;
226     }
227 
228     if (bootstrap) {
229         if (cppFilename.empty() && headerFilename.empty()) {
230             fprintf(stderr, "Bootstrap flag can only be used for cpp/header files.\n");
231             return 1;
232         }
233         if (supportWorkSource) {
234             fprintf(stderr, "Bootstrap flag does not support worksources");
235             return 1;
236         }
237         if (minApiLevel != API_LEVEL_CURRENT) {
238             fprintf(stderr, "Bootstrap flag does not support older API levels");
239             return 1;
240         }
241     }
242 
243     // Collate the parameters
244     int errorCount = 0;
245 
246     Atoms atoms;
247 
248     MFErrorCollector errorCollector;
249     google::protobuf::compiler::DiskSourceTree sourceTree;
250     google::protobuf::compiler::Importer importer(&sourceTree, &errorCollector);
251 
252     if (vendorProto.empty()) {
253         errorCount = collate_atoms(*Atom::descriptor(), moduleName, atoms);
254     } else {
255         const google::protobuf::FileDescriptor* fileDescriptor;
256         sourceTree.MapPath("", fs::current_path().c_str());
257 
258         const char* androidBuildTop = std::getenv("ANDROID_BUILD_TOP");
259 
260         fs::path protobufSrc = androidBuildTop != nullptr ? androidBuildTop : fs::current_path();
261         protobufSrc /= "external/protobuf/src";
262         sourceTree.MapPath("", protobufSrc.c_str());
263 
264         if (androidBuildTop != nullptr) {
265             sourceTree.MapPath("", androidBuildTop);
266         }
267 
268         fileDescriptor = importer.Import(vendorProto);
269         errorCount =
270                 collate_atoms(*fileDescriptor->FindMessageTypeByName("Atom"), moduleName, atoms);
271     }
272 
273     if (errorCount != 0) {
274         return 1;
275     }
276 
277     AtomDecl attributionDecl;
278     vector<java_type_t> attributionSignature;
279     collate_atom(*android::os::statsd::AttributionNode::descriptor(), attributionDecl,
280                  attributionSignature);
281 
282     // Write the .cpp file
283     if (!cppFilename.empty()) {
284         // If this is for a specific module, the namespace must also be provided.
285         if (moduleName != DEFAULT_MODULE_NAME && cppNamespace == DEFAULT_CPP_NAMESPACE) {
286             fprintf(stderr, "Must supply --namespace if supplying a specific module\n");
287             return 1;
288         }
289         // If this is for a specific module, the header file to import must also be
290         // provided.
291         if (moduleName != DEFAULT_MODULE_NAME && cppHeaderImport == DEFAULT_CPP_HEADER_IMPORT) {
292             fprintf(stderr, "Must supply --headerImport if supplying a specific module\n");
293             return 1;
294         }
295         FILE* out = fopen(cppFilename.c_str(), "we");
296         if (out == nullptr) {
297             fprintf(stderr, "Unable to open file for write: %s\n", cppFilename.c_str());
298             return 1;
299         }
300         if (vendorProto.empty()) {
301             errorCount = android::stats_log_api_gen::write_stats_log_cpp(
302                     out, atoms, attributionDecl, cppNamespace, cppHeaderImport, minApiLevel,
303                     bootstrap);
304         } else {
305 #ifdef WITH_VENDOR
306             errorCount = android::stats_log_api_gen::write_stats_log_cpp_vendor(
307                     out, atoms, attributionDecl, cppNamespace, cppHeaderImport);
308 #endif
309         }
310         fclose(out);
311     }
312 
313     // Write the .h file
314     if (!headerFilename.empty()) {
315         // If this is for a specific module, the namespace must also be provided.
316         if (moduleName != DEFAULT_MODULE_NAME && cppNamespace == DEFAULT_CPP_NAMESPACE) {
317             fprintf(stderr, "Must supply --namespace if supplying a specific module\n");
318         }
319         FILE* out = fopen(headerFilename.c_str(), "we");
320         if (out == nullptr) {
321             fprintf(stderr, "Unable to open file for write: %s\n", headerFilename.c_str());
322             return 1;
323         }
324 
325         if (vendorProto.empty()) {
326             errorCount = android::stats_log_api_gen::write_stats_log_header(
327                     out, atoms, attributionDecl, cppNamespace, minApiLevel, bootstrap);
328         } else {
329 #ifdef WITH_VENDOR
330             errorCount = android::stats_log_api_gen::write_stats_log_header_vendor(
331                     out, atoms, attributionDecl, cppNamespace);
332 #endif
333         }
334         fclose(out);
335     }
336 
337     // Write the .java file
338     if (!javaFilename.empty()) {
339         if (javaClass.empty()) {
340             fprintf(stderr, "Must supply --javaClass if supplying a Java filename");
341             return 1;
342         }
343 
344         if (javaPackage.empty()) {
345             fprintf(stderr, "Must supply --javaPackage if supplying a Java filename");
346             return 1;
347         }
348 
349         if (moduleName.empty() || moduleName == DEFAULT_MODULE_NAME) {
350             fprintf(stderr, "Must supply --module if supplying a Java filename");
351             return 1;
352         }
353 
354         FILE* out = fopen(javaFilename.c_str(), "we");
355         if (out == nullptr) {
356             fprintf(stderr, "Unable to open file for write: %s\n", javaFilename.c_str());
357             return 1;
358         }
359 
360         if (vendorProto.empty()) {
361             errorCount = android::stats_log_api_gen::write_stats_log_java(
362                     out, atoms, attributionDecl, javaClass, javaPackage, minApiLevel,
363                     supportWorkSource);
364         } else {
365 #ifdef WITH_VENDOR
366             if (supportWorkSource) {
367                 fprintf(stderr, "The attribution chain is not supported for vendor atoms");
368                 return 1;
369             }
370 
371             errorCount = android::stats_log_api_gen::write_stats_log_java_vendor(out, atoms,
372                     javaClass, javaPackage);
373 #endif
374         }
375 
376         fclose(out);
377     }
378 
379     // Write the main .rs file
380     if (!rustFilename.empty()) {
381         if (rustHeaderCrate.empty()) {
382             fprintf(stderr, "rustHeaderCrate flag is either not passed or is empty");
383             return 1;
384         }
385 
386         FILE* out = fopen(rustFilename.c_str(), "we");
387         if (out == nullptr) {
388             fprintf(stderr, "Unable to open file for write: %s\n", rustFilename.c_str());
389             return 1;
390         }
391 
392         errorCount += android::stats_log_api_gen::write_stats_log_rust(
393                 out, atoms, attributionDecl, minApiLevel, rustHeaderCrate.c_str());
394 
395         fclose(out);
396     }
397 
398     // Write the header .rs file
399     if (!rustHeaderFilename.empty()) {
400         if (rustHeaderCrate.empty()) {
401             fprintf(stderr, "rustHeaderCrate flag is either not passed or is empty");
402             return 1;
403         }
404 
405         FILE* out = fopen(rustHeaderFilename.c_str(), "we");
406         if (out == nullptr) {
407             fprintf(stderr, "Unable to open file for write: %s\n", rustHeaderFilename.c_str());
408             return 1;
409         }
410 
411         android::stats_log_api_gen::write_stats_log_rust_header(out, atoms, attributionDecl,
412                                                                 rustHeaderCrate.c_str());
413 
414         fclose(out);
415     }
416 
417     return errorCount;
418 }
419 
420 }  // namespace stats_log_api_gen
421 }  // namespace android
422 
423 /**
424  * Main.
425  */
main(int argc,char const * const * argv)426 int main(int argc, char const* const* argv) {
427     GOOGLE_PROTOBUF_VERIFY_VERSION;
428 
429     return android::stats_log_api_gen::run(argc, argv);
430 }
431