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 <android-base/logging.h>
18 #include <android-base/strings.h>
19 #include <hidl-util/FQName.h>
20 #include <hidl-util/Formatter.h>
21 
22 #include <algorithm>
23 #include <iostream>
24 #include <vector>
25 
26 #include "AST.h"
27 #include "AidlHelper.h"
28 #include "CompoundType.h"
29 #include "Coordinator.h"
30 #include "DocComment.h"
31 #include "Interface.h"
32 
33 using namespace android;
34 
usage(const char * me)35 static void usage(const char* me) {
36     Formatter out(stderr);
37 
38     out << "Usage: " << me << " [-fh] [-o <output path>] [-l <header file>] ";
39     Coordinator::emitOptionsUsageString(out);
40     out << " FQNAME\n\n";
41 
42     out << "Converts FQNAME, PACKAGE(.SUBPACKAGE)*@[0-9]+.[0-9]+(::TYPE)? to an aidl "
43            "equivalent.\n\n";
44 
45     out.indent();
46     out.indent();
47 
48     out << "-f: Force hidl2aidl to convert older packages\n";
49     out << "-e: Used for expanding extensions and types from other packages\n";
50     out << "-h: Prints this menu.\n";
51     out << "-o <output path>: Location to output files.\n";
52     out << "-l <header file>: File containing a header to prepend to generated files.\n";
53     Coordinator::emitOptionsDetailString(out);
54 
55     out.unindent();
56     out.unindent();
57 }
58 
getNewerFQName(const FQName & lhs,const FQName & rhs)59 static const FQName& getNewerFQName(const FQName& lhs, const FQName& rhs) {
60     CHECK(lhs.package() == rhs.package());
61     CHECK(lhs.name() == rhs.name());
62 
63     if (lhs.getPackageMajorVersion() > rhs.getPackageMajorVersion()) return lhs;
64     if (lhs.getPackageMajorVersion() < rhs.getPackageMajorVersion()) return rhs;
65 
66     if (lhs.getPackageMinorVersion() > rhs.getPackageMinorVersion()) return lhs;
67     return rhs;
68 }
69 
70 // If similar FQName is not found, the same one is returned
getLatestMinorVersionFQNameFromList(const FQName & fqName,const std::vector<FQName> & list)71 static FQName getLatestMinorVersionFQNameFromList(const FQName& fqName,
72                                                   const std::vector<FQName>& list) {
73     FQName currentCandidate = fqName;
74     bool found = false;
75     for (const FQName& current : list) {
76         if (current.package() == currentCandidate.package() &&
77             current.name() == currentCandidate.name() &&
78             current.getPackageMajorVersion() == currentCandidate.getPackageMajorVersion()) {
79             // Prioritize elements in the list over the provided fqName
80             currentCandidate = found ? getNewerFQName(current, currentCandidate) : current;
81             found = true;
82         }
83     }
84 
85     return currentCandidate;
86 }
87 
getLatestMinorVersionNamedTypeFromList(const FQName & fqName,const std::set<const NamedType * > & list)88 static FQName getLatestMinorVersionNamedTypeFromList(const FQName& fqName,
89                                                      const std::set<const NamedType*>& list) {
90     FQName currentCandidate = fqName;
91     bool found = false;
92     for (const NamedType* currentNamedType : list) {
93         const FQName& current = currentNamedType->fqName();
94         if (current.package() == currentCandidate.package() &&
95             current.name() == currentCandidate.name() &&
96             current.getPackageMajorVersion() == currentCandidate.getPackageMajorVersion()) {
97             // Prioritize elements in the list over the provided fqName
98             currentCandidate = found ? getNewerFQName(current, currentCandidate) : current;
99             found = true;
100         }
101     }
102 
103     return currentCandidate;
104 }
105 
packageExists(const Coordinator & coordinator,const FQName & fqName)106 static bool packageExists(const Coordinator& coordinator, const FQName& fqName) {
107     bool result;
108     status_t err = coordinator.packageExists(fqName, &result);
109     if (err != OK) {
110         std::cerr << "Error trying to find package " << fqName.string() << std::endl;
111         exit(1);
112     }
113 
114     return result;
115 }
116 
117 // assuming fqName exists, find oldest version which does exist
118 // e.g. android.hardware.foo@1.7 -> android.hardware.foo@1.1 (if foo@1.0 doesn't
119 // exist)
getLowestExistingFqName(const Coordinator & coordinator,const FQName & fqName)120 static FQName getLowestExistingFqName(const Coordinator& coordinator, const FQName& fqName) {
121     FQName lowest(fqName);
122     while (lowest.getPackageMinorVersion() != 0) {
123         if (!packageExists(coordinator, lowest.downRev())) break;
124 
125         lowest = lowest.downRev();
126     }
127     return lowest;
128 }
129 
130 // assuming fqName exists, find newest version which does exist
131 // e.g. android.hardware.foo@1.1 -> android.hardware.foo@1.7 if that's the
132 // newest
getHighestExistingFqName(const Coordinator & coordinator,const FQName & fqName)133 static FQName getHighestExistingFqName(const Coordinator& coordinator, const FQName& fqName) {
134     FQName highest(fqName);
135     while (packageExists(coordinator, highest.upRev())) {
136         highest = highest.upRev();
137     }
138     return highest;
139 }
140 
parse(const Coordinator & coordinator,const FQName & target)141 static AST* parse(const Coordinator& coordinator, const FQName& target) {
142     AST* ast = coordinator.parse(target);
143     if (ast == nullptr) {
144         std::cerr << "ERROR: Could not parse " << target.name() << ". Aborting." << std::endl;
145         exit(1);
146     }
147 
148     if (!ast->getUnhandledComments().empty()) {
149         AidlHelper::notes()
150                 << "Unhandled comments from " << target.string()
151                 << " follow. Consider using hidl-lint to locate these and fixup as many "
152                 << "as possible.\n";
153         for (const DocComment* docComment : ast->getUnhandledComments()) {
154             docComment->emit(AidlHelper::notes());
155         }
156         AidlHelper::notes() << "\n";
157     }
158 
159     return ast;
160 }
161 
getSubTypes(const NamedType & namedType,std::set<const NamedType * > * types)162 static void getSubTypes(const NamedType& namedType, std::set<const NamedType*>* types) {
163     if (namedType.isScope()) {
164         const Scope& compoundType = static_cast<const Scope&>(namedType);
165         for (const NamedType* subType : compoundType.getSubTypes()) {
166             types->insert(subType);
167             getSubTypes(*subType, types);
168         }
169     }
170 }
171 
emitAidlSharedLibs(Formatter & out,FQName fqName,AidlBackend backend)172 static void emitAidlSharedLibs(Formatter& out, FQName fqName, AidlBackend backend) {
173     if (backend == AidlBackend::NDK) {
174         out << "        \"libbinder_ndk\",\n";
175         out << "        \"libhidlbase\",\n";
176         out << "        \"" << AidlHelper::getAidlPackage(fqName) << "-V1-ndk_platform\",\n";
177     } else if (backend == AidlBackend::CPP) {
178         out << "        \"libbinder\",\n";
179         out << "        \"libhidlbase\",\n";
180         out << "        \"" << AidlHelper::getAidlPackage(fqName) << "-V1-cpp\",\n";
181         out << "        \"libutils\",\n";
182     } else {
183         out << "        \"" << AidlHelper::getAidlPackage(fqName) << "-V1-java\",\n";
184     }
185 }
186 
emitHidlSharedLibs(Formatter & out,std::vector<FQName> & targets,AidlBackend backend)187 static void emitHidlSharedLibs(Formatter& out, std::vector<FQName>& targets, AidlBackend backend) {
188     std::set<std::string> uniquePackages;
189     for (const auto& target : targets) {
190         if (backend == AidlBackend::JAVA) {
191             uniquePackages.insert(
192                     android::base::StringReplace(target.getPackageAndVersion().string(), "@", "-V",
193                                                  false /* all */) +
194                     "-java");
195         } else {
196             uniquePackages.insert(target.getPackageAndVersion().string());
197         }
198     }
199     for (const auto& package : uniquePackages) {
200         out << "        \"" << package << "\",\n";
201     }
202 }
203 
aidlTranslateLibraryName(FQName fqName,AidlBackend backend)204 static std::string aidlTranslateLibraryName(FQName fqName, AidlBackend backend) {
205     std::string postfix;
206     if (backend == AidlBackend::NDK) {
207         postfix = "-ndk";
208     } else if (backend == AidlBackend::CPP) {
209         postfix = "-cpp";
210     } else {
211         postfix = "-java";
212     }
213     return AidlHelper::getAidlPackage(fqName) + "-translate" + postfix;
214 }
215 
emitBuildFile(Formatter & out,const FQName & fqName,std::vector<FQName> & targets,bool needsTranslation)216 static void emitBuildFile(Formatter& out, const FQName& fqName, std::vector<FQName>& targets,
217                           bool needsTranslation) {
218     out << "// This is the expected build file, but it may not be right in all cases\n";
219     out << "\n";
220     out << "aidl_interface {\n";
221     out << "    name: \"" << AidlHelper::getAidlPackage(fqName) << "\",\n";
222     out << "    vendor_available: true,\n";
223     out << "    srcs: [\"" << AidlHelper::getAidlPackagePath(fqName) << "/*.aidl\"],\n";
224     out << "    stability: \"vintf\",\n";
225     out << "    backend: {\n";
226     out << "        cpp: {\n";
227     out << "            // FIXME should this be disabled?\n";
228     out << "            // prefer NDK backend which can be used anywhere\n";
229     out << "            enabled: true,\n";
230     out << "        },\n";
231     out << "        java: {\n";
232     out << "            sdk_version: \"module_current\",\n";
233     out << "        },\n";
234     out << "        ndk: {\n";
235     out << "            vndk: {\n";
236     out << "                enabled: true,\n";
237     out << "            },\n";
238     out << "        },\n";
239     out << "    },\n";
240     out << "}\n\n";
241 
242     if (!needsTranslation) return;
243 
244     for (auto backend : {AidlBackend::CPP, AidlBackend::NDK}) {
245         out << "cc_library {\n";
246         out << "    name: \"" << aidlTranslateLibraryName(fqName, backend) << +"\",\n";
247         if (backend == AidlBackend::NDK) {
248             out << "    vendor_available: true,\n";
249         }
250         out << "    srcs: [\"" << AidlHelper::translateSourceFile(fqName, backend) + "\"],\n";
251         out << "    shared_libs: [\n";
252         emitAidlSharedLibs(out, fqName, backend);
253         emitHidlSharedLibs(out, targets, backend);
254         out << "    ],\n";
255         out << "    export_include_dirs: [\"include\"],\n";
256         out << "}\n\n";
257     }
258 
259     out << "java_library {\n";
260     out << "    name: \"" << aidlTranslateLibraryName(fqName, AidlBackend::JAVA) << +"\",\n";
261     out << "    srcs: [\"" << AidlHelper::translateSourceFile(fqName, AidlBackend::JAVA) + "\"],\n";
262     out << "    libs: [\n";
263     emitAidlSharedLibs(out, fqName, AidlBackend::JAVA);
264     emitHidlSharedLibs(out, targets, AidlBackend::JAVA);
265     out << "    ],\n";
266     out << "    sdk_version: \"module_current\",\n";
267     out << "}\n\n";
268 }
269 
270 // hidl is intentionally leaky. Turn off LeakSanitizer by default.
__asan_default_options()271 extern "C" const char* __asan_default_options() {
272     return "detect_leaks=0";
273 }
274 
main(int argc,char ** argv)275 int main(int argc, char** argv) {
276     const char* me = argv[0];
277     if (argc == 1) {
278         usage(me);
279         std::cerr << "ERROR: no fqname specified." << std::endl;
280         exit(1);
281     }
282 
283     Coordinator coordinator;
284     std::string outputPath;
285     std::string fileHeader;
286     bool forceConvertOldInterfaces = false;
287     coordinator.parseOptions(argc, argv, "fho:l:e", [&](int res, char* arg) {
288         switch (res) {
289             case 'o': {
290                 if (!outputPath.empty()) {
291                     fprintf(stderr, "ERROR: -o <output path> can only be specified once.\n");
292                     exit(1);
293                 }
294                 outputPath = arg;
295                 break;
296             }
297             case 'l':
298                 if (!fileHeader.empty()) {
299                     fprintf(stderr, "ERROR: -l <header file> can only be specified once.\n");
300                     exit(1);
301                 }
302                 fileHeader = arg;
303                 break;
304             case 'f':
305                 forceConvertOldInterfaces = true;
306                 break;
307             case 'e':
308                 AidlHelper::setExpandExtended(true);
309                 break;
310             case 'h':
311             case '?':
312             default: {
313                 usage(me);
314                 exit(1);
315                 break;
316             }
317         }
318     });
319 
320     if (!outputPath.empty() && outputPath.back() != '/') {
321         outputPath += "/";
322     }
323     coordinator.setOutputPath(outputPath);
324     AidlHelper::setFileHeader(fileHeader);
325 
326     argc -= optind;
327     argv += optind;
328 
329     if (argc == 0) {
330         usage(me);
331         std::cerr << "ERROR: no fqname specified." << std::endl;
332         exit(1);
333     }
334 
335     if (argc > 1) {
336         usage(me);
337         std::cerr << "ERROR: only one fqname can be specified." << std::endl;
338         exit(1);
339     }
340 
341     const char* arg = argv[0];
342 
343     FQName fqName;
344     if (!FQName::parse(arg, &fqName)) {
345         std::cerr << "ERROR: Invalid fully-qualified name as argument: " << arg << "." << std::endl;
346         exit(1);
347     }
348 
349     if (fqName.isFullyQualified()) {
350         std::cerr << "ERROR: hidl2aidl only supports converting an entire package, try "
351                      "converting "
352                   << fqName.getPackageAndVersion().string() << " instead." << std::endl;
353         exit(1);
354     }
355 
356     if (!packageExists(coordinator, fqName)) {
357         std::cerr << "ERROR: Could not get sources for: " << arg << "." << std::endl;
358         exit(1);
359     }
360 
361     if (!forceConvertOldInterfaces) {
362         const FQName highestFqName = getHighestExistingFqName(coordinator, fqName);
363         if (fqName != highestFqName) {
364             std::cerr << "ERROR: A newer minor version of " << fqName.string() << " exists ("
365                       << highestFqName.string()
366                       << "). In general, prefer to convert that instead. If you really mean to "
367                          "use an old minor version use '-f'."
368                       << std::endl;
369             exit(1);
370         }
371     }
372 
373     // This is the list of all types which should be converted
374     std::vector<FQName> targets;
375     for (FQName version = getLowestExistingFqName(coordinator, fqName);
376          version.getPackageMinorVersion() <= fqName.getPackageMinorVersion();
377          version = version.upRev()) {
378         std::vector<FQName> newTargets;
379         status_t err = coordinator.appendPackageInterfacesToVector(version, &newTargets);
380         if (err != OK) exit(1);
381 
382         targets.insert(targets.end(), newTargets.begin(), newTargets.end());
383     }
384 
385     // targets should not contain duplicates since appendPackageInterfaces is only called once
386     // per version. now remove all the elements that are not the "newest"
387     const auto& newEnd =
388             std::remove_if(targets.begin(), targets.end(), [&](const FQName& fqName) -> bool {
389                 if (fqName.name() == "types") return false;
390 
391                 return getLatestMinorVersionFQNameFromList(fqName, targets) != fqName;
392             });
393     targets.erase(newEnd, targets.end());
394 
395     // Set up AIDL conversion log
396     Formatter err =
397             coordinator.getFormatter(fqName, Coordinator::Location::DIRECT, "conversion.log");
398     err << "Notes relating to hidl2aidl conversion of " << fqName.string() << " to "
399         << AidlHelper::getAidlPackage(fqName) << " (if any) follow:\n";
400     AidlHelper::setNotes(&err);
401 
402     // Gather all the types and interfaces
403     std::set<const NamedType*> namedTypesInPackage;
404     for (const FQName& target : targets) {
405 
406         AST* ast = parse(coordinator, target);
407         CHECK(ast);
408 
409         const Interface* iface = ast->getInterface();
410         if (iface) {
411             namedTypesInPackage.insert(iface);
412 
413             // Get all of the types defined in the interface chain(includes self)
414             for (const Interface* interface : iface->typeChain()) {
415                 if (!AidlHelper::shouldBeExpanded(iface->fqName(), interface->fqName())) {
416                     break;
417                 }
418                 getSubTypes(*interface, &namedTypesInPackage);
419             }
420         } else {
421             getSubTypes(ast->getRootScope(), &namedTypesInPackage);
422         }
423     }
424 
425     // Remove all of the older versions of types and keep the latest
426     for (auto it = namedTypesInPackage.begin(); it != namedTypesInPackage.end();) {
427         if (getLatestMinorVersionNamedTypeFromList((*it)->fqName(), namedTypesInPackage) !=
428             (*it)->fqName()) {
429             it = namedTypesInPackage.erase(it);
430         } else {
431             it++;
432         }
433     }
434 
435     // Process and flatten all of the types. Many types include fields of older
436     // versions of that type.
437     // This step recursively finds all of those fields and adds their fields to
438     // the most recent top level type.
439     std::map<const NamedType*, const ProcessedCompoundType> processedTypesInPackage;
440     for (const auto& namedType : namedTypesInPackage) {
441         if (namedType->isCompoundType()) {
442             ProcessedCompoundType processed;
443             AidlHelper::processCompoundType(static_cast<const CompoundType&>(*namedType),
444                                             &processed, std::string());
445             processedTypesInPackage.insert(
446                     std::pair<const NamedType*, const ProcessedCompoundType>(namedType, processed));
447         }
448     }
449 
450     Formatter buildFile =
451             coordinator.getFormatter(fqName, Coordinator::Location::DIRECT, "Android.bp");
452     emitBuildFile(buildFile, fqName, targets, !processedTypesInPackage.empty());
453     AidlHelper::emitTranslation(coordinator, fqName, namedTypesInPackage, processedTypesInPackage);
454 
455     // Emit all types and interfaces
456     // The interfaces and types are still be further manipulated inside
457     // emitAidl. The interfaces are consolidating methods from their typechains
458     // and the composite types are being flattened.
459     for (const auto& namedType : namedTypesInPackage) {
460         AidlHelper::emitAidl(*namedType, coordinator, processedTypesInPackage);
461     }
462 
463     err << "END OF LOG\n";
464 
465     return 0;
466 }
467