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\",\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 << "    host_supported: true,\n";
224     out << "    frozen: false,\n";
225     out << "    srcs: [\"" << AidlHelper::getAidlPackagePath(fqName) << "/*.aidl\"],\n";
226     out << "    stability: \"vintf\",\n";
227     out << "    backend: {\n";
228     out << "        cpp: {\n";
229     out << "            // FIXME should this be disabled?\n";
230     out << "            // prefer NDK backend which can be used anywhere\n";
231     out << "            // If you disable this, you also need to delete the C++\n";
232     out << "            // translate code.\n";
233     out << "            enabled: true,\n";
234     out << "        },\n";
235     out << "        java: {\n";
236     out << "            sdk_version: \"system_current\",\n";
237     out << "        },\n";
238     out << "    },\n";
239     out << "}\n\n";
240 
241     if (!needsTranslation) return;
242 
243     for (auto backend : {AidlBackend::CPP, AidlBackend::NDK}) {
244         out << "cc_library {\n";
245         out << "    name: \"" << aidlTranslateLibraryName(fqName, backend) << +"\",\n";
246         if (backend == AidlBackend::NDK) {
247             out << "    vendor_available: true,\n";
248         }
249         out << "    srcs: [\"" << AidlHelper::translateSourceFile(fqName, backend) + "\"],\n";
250         out << "    shared_libs: [\n";
251         emitAidlSharedLibs(out, fqName, backend);
252         emitHidlSharedLibs(out, targets, backend);
253         out << "    ],\n";
254         out << "    export_include_dirs: [\"include\"],\n";
255         out << "}\n\n";
256     }
257 
258     out << "java_library {\n";
259     out << "    name: \"" << aidlTranslateLibraryName(fqName, AidlBackend::JAVA) << +"\",\n";
260     out << "    srcs: [\"" << AidlHelper::translateSourceFile(fqName, AidlBackend::JAVA) + "\"],\n";
261     out << "    libs: [\n";
262     emitAidlSharedLibs(out, fqName, AidlBackend::JAVA);
263     emitHidlSharedLibs(out, targets, AidlBackend::JAVA);
264     out << "    ],\n";
265     out << "    sdk_version: \"system_current\",\n";
266     out << "}\n\n";
267 }
268 
269 // hidl is intentionally leaky. Turn off LeakSanitizer by default.
__asan_default_options()270 extern "C" const char* __asan_default_options() {
271     return "detect_leaks=0";
272 }
273 
main(int argc,char ** argv)274 int main(int argc, char** argv) {
275     const char* me = argv[0];
276     if (argc == 1) {
277         usage(me);
278         std::cerr << "ERROR: no fqname specified." << std::endl;
279         exit(1);
280     }
281 
282     Coordinator coordinator;
283     std::string outputPath;
284     std::string fileHeader;
285     bool forceConvertOldInterfaces = false;
286     coordinator.parseOptions(argc, argv, "fho:l:e", [&](int res, char* arg) {
287         switch (res) {
288             case 'o': {
289                 if (!outputPath.empty()) {
290                     fprintf(stderr, "ERROR: -o <output path> can only be specified once.\n");
291                     exit(1);
292                 }
293                 outputPath = arg;
294                 break;
295             }
296             case 'l':
297                 if (!fileHeader.empty()) {
298                     fprintf(stderr, "ERROR: -l <header file> can only be specified once.\n");
299                     exit(1);
300                 }
301                 fileHeader = arg;
302                 break;
303             case 'f':
304                 forceConvertOldInterfaces = true;
305                 break;
306             case 'e':
307                 AidlHelper::setExpandExtended(true);
308                 break;
309             case 'h':
310             case '?':
311             default: {
312                 usage(me);
313                 exit(1);
314                 break;
315             }
316         }
317     });
318 
319     if (!outputPath.empty() && outputPath.back() != '/') {
320         outputPath += "/";
321     }
322     coordinator.setOutputPath(outputPath);
323     AidlHelper::setFileHeader(fileHeader);
324 
325     argc -= optind;
326     argv += optind;
327 
328     if (argc == 0) {
329         usage(me);
330         std::cerr << "ERROR: no fqname specified." << std::endl;
331         exit(1);
332     }
333 
334     if (argc > 1) {
335         usage(me);
336         std::cerr << "ERROR: only one fqname can be specified." << std::endl;
337         exit(1);
338     }
339 
340     const char* arg = argv[0];
341 
342     FQName fqName;
343     if (!FQName::parse(arg, &fqName)) {
344         std::cerr << "ERROR: Invalid fully-qualified name as argument: " << arg << "." << std::endl;
345         exit(1);
346     }
347 
348     if (fqName.isFullyQualified()) {
349         std::cerr << "ERROR: hidl2aidl only supports converting an entire package, try "
350                      "converting "
351                   << fqName.getPackageAndVersion().string() << " instead." << std::endl;
352         exit(1);
353     }
354 
355     if (!packageExists(coordinator, fqName)) {
356         std::cerr << "ERROR: Could not get sources for: " << arg << "." << std::endl;
357         exit(1);
358     }
359 
360     if (!forceConvertOldInterfaces) {
361         const FQName highestFqName = getHighestExistingFqName(coordinator, fqName);
362         if (fqName != highestFqName) {
363             std::cerr << "ERROR: A newer minor version of " << fqName.string() << " exists ("
364                       << highestFqName.string()
365                       << "). In general, prefer to convert that instead. If you really mean to "
366                          "use an old minor version use '-f'."
367                       << std::endl;
368             exit(1);
369         }
370     }
371 
372     // This is the list of all targets which should be converted
373     std::vector<FQName> targets;
374     for (FQName version = getLowestExistingFqName(coordinator, fqName);
375          version.getPackageMinorVersion() <= fqName.getPackageMinorVersion();
376          version = version.upRev()) {
377         std::vector<FQName> newTargets;
378         status_t err = coordinator.appendPackageInterfacesToVector(version, &newTargets);
379         if (err != OK) exit(1);
380 
381         targets.insert(targets.end(), newTargets.begin(), newTargets.end());
382     }
383 
384     // targets should not contain duplicates since appendPackageInterfaces is only called once
385     // per version. now remove all the elements that are not the "newest"
386     const auto& newEnd =
387             std::remove_if(targets.begin(), targets.end(), [&](const FQName& fqName) -> bool {
388                 if (fqName.name() == "types") return false;
389 
390                 return getLatestMinorVersionFQNameFromList(fqName, targets) != fqName;
391             });
392     targets.erase(newEnd, targets.end());
393 
394     // Set up AIDL conversion log
395     Formatter err =
396             coordinator.getFormatter(fqName, Coordinator::Location::DIRECT, "conversion.log");
397     err << "Notes relating to hidl2aidl conversion of " << fqName.string() << " to "
398         << AidlHelper::getAidlPackage(fqName) << " (if any) follow:\n";
399     AidlHelper::setNotes(&err);
400 
401     // Gather all the types and interfaces
402     std::set<const NamedType*> namedTypesInPackage;
403     for (const FQName& target : targets) {
404 
405         AST* ast = parse(coordinator, target);
406         CHECK(ast);
407 
408         const Interface* iface = ast->getInterface();
409         if (iface) {
410             namedTypesInPackage.insert(iface);
411 
412             // Get all of the types defined in the interface chain(includes self)
413             for (const Interface* interface : iface->typeChain()) {
414                 if (!AidlHelper::shouldBeExpanded(iface->fqName(), interface->fqName())) {
415                     break;
416                 }
417                 getSubTypes(*interface, &namedTypesInPackage);
418             }
419         } else {
420             getSubTypes(ast->getRootScope(), &namedTypesInPackage);
421         }
422     }
423 
424     // Remove all of the older versions of types and keep the latest
425     for (auto it = namedTypesInPackage.begin(); it != namedTypesInPackage.end();) {
426         if (getLatestMinorVersionNamedTypeFromList((*it)->fqName(), namedTypesInPackage) !=
427             (*it)->fqName()) {
428             it = namedTypesInPackage.erase(it);
429         } else {
430             it++;
431         }
432     }
433 
434     // Process and flatten all of the types. Many types include fields of older
435     // versions of that type.
436     // This step recursively finds all of those fields and adds their fields to
437     // the most recent top level type.
438     std::map<const NamedType*, const ProcessedCompoundType> processedTypesInPackage;
439     for (const auto& namedType : namedTypesInPackage) {
440         if (namedType->isCompoundType()) {
441             ProcessedCompoundType processed;
442             AidlHelper::processCompoundType(static_cast<const CompoundType&>(*namedType),
443                                             &processed, std::string());
444             processedTypesInPackage.insert(
445                     std::pair<const NamedType*, const ProcessedCompoundType>(namedType, processed));
446         }
447     }
448 
449     Formatter buildFile =
450             coordinator.getFormatter(fqName, Coordinator::Location::DIRECT, "Android.bp");
451     emitBuildFile(buildFile, fqName, targets, !processedTypesInPackage.empty());
452     AidlHelper::emitTranslation(coordinator, fqName, namedTypesInPackage, processedTypesInPackage);
453 
454     // Emit all types and interfaces
455     // The interfaces and types are still be further manipulated inside
456     // emitAidl. The interfaces are consolidating methods from their typechains
457     // and the composite types are being flattened.
458     for (const auto& namedType : namedTypesInPackage) {
459         // Nested types do not get their own files
460         if (namedType->fqName().names().size() > 1) continue;
461         Formatter out =
462                 AidlHelper::getFileWithHeader(*namedType, coordinator, processedTypesInPackage);
463         AidlHelper::emitAidl(*namedType, out, processedTypesInPackage);
464     }
465 
466     err << "END OF LOG\n";
467 
468     return 0;
469 }
470