Loading cmds/lshal/ListCommand.cpp +117 −118 Original line number Diff line number Diff line Loading @@ -19,11 +19,12 @@ #include <getopt.h> #include <fstream> #include <functional> #include <iomanip> #include <iostream> #include <map> #include <sstream> #include <regex> #include <sstream> #include <android-base/file.h> #include <android-base/parseint.h> Loading Loading @@ -101,21 +102,19 @@ Partition ListCommand::getPartition(pid_t pid) { // Give sensible defaults when nothing can be inferred from runtime. // process: Partition inferred from executable location or cmdline. Partition ListCommand::resolvePartition(Partition process, const FQName& fqName) const { if (fqName.inPackage("vendor") || fqName.inPackage("com")) { Partition ListCommand::resolvePartition(Partition process, const FqInstance& fqInstance) const { if (fqInstance.inPackage("vendor") || fqInstance.inPackage("com")) { return Partition::VENDOR; } if (fqName.inPackage("android.frameworks") || fqName.inPackage("android.system") || fqName.inPackage("android.hidl")) { if (fqInstance.inPackage("android.frameworks") || fqInstance.inPackage("android.system") || fqInstance.inPackage("android.hidl")) { return Partition::SYSTEM; } // Some android.hardware HALs are served from system. Check the value from executable // location / cmdline first. if (fqName.inPackage("android.hardware")) { if (fqInstance.inPackage("android.hardware")) { if (process != Partition::UNKNOWN) { return process; } Loading Loading @@ -284,62 +283,30 @@ void ListCommand::postprocess() { "These may return subclasses through their respective HIDL_FETCH_I* functions."); } static inline bool findAndBumpVersion(vintf::ManifestHal* hal, const vintf::Version& version) { for (vintf::Version& v : hal->versions) { if (v.majorVer == version.majorVer) { v.minorVer = std::max(v.minorVer, version.minorVer); return true; } } bool ListCommand::addEntryWithInstance(const TableEntry& entry, vintf::HalManifest* manifest) const { FqInstance fqInstance; if (!fqInstance.setTo(entry.interfaceName)) { err() << "Warning: '" << entry.interfaceName << "' is not a valid FqInstance." << std::endl; return false; } void ListCommand::dumpVintf(const NullableOStream<std::ostream>& out) const { using vintf::operator|=; using vintf::operator<<; vintf::HalManifest manifest; manifest.setType(toSchemaType(mVintfPartition)); forEachTable([this, &manifest] (const Table &table) { for (const TableEntry &entry : table) { std::string fqInstanceName = entry.interfaceName; if (&table == &mImplementationsTable) { // Quick hack to work around *'s replaceAll(&fqInstanceName, '*', 'D'); } auto splitFqInstanceName = splitFirst(fqInstanceName, '/'); FQName fqName; if (!FQName::parse(splitFqInstanceName.first, &fqName)) { err() << "Warning: '" << splitFqInstanceName.first << "' is not a valid FQName." << std::endl; continue; } if (fqName.package() == gIBaseFqName.package()) { continue; // always remove IBase from manifest if (fqInstance.getPackage() == gIBaseFqName.package()) { return true; // always remove IBase from manifest } Partition partition = resolvePartition(entry.partition, fqName); Partition partition = resolvePartition(entry.partition, fqInstance); if (partition == Partition::UNKNOWN) { err() << "Warning: Cannot guess the partition of instance " << fqInstanceName << ". It is removed from the generated manifest." << std::endl; continue; err() << "Warning: Cannot guess the partition of FqInstance " << fqInstance.string() << std::endl; return false; } if (partition != mVintfPartition) { continue; // strip out instances that is in a different partition. return true; // strip out instances that is in a different partition. } std::string interfaceName = &table == &mImplementationsTable ? "" : fqName.name(); std::string instanceName = &table == &mImplementationsTable ? "" : splitFqInstanceName.second; vintf::Version version{fqName.getPackageMajorVersion(), fqName.getPackageMinorVersion()}; vintf::Transport transport; vintf::Arch arch; if (entry.transport == "hwbinder") { Loading @@ -349,64 +316,88 @@ void ListCommand::dumpVintf(const NullableOStream<std::ostream>& out) const { transport = vintf::Transport::PASSTHROUGH; switch (entry.arch) { case lshal::ARCH32: arch = vintf::Arch::ARCH_32; break; arch = vintf::Arch::ARCH_32; break; case lshal::ARCH64: arch = vintf::Arch::ARCH_64; break; arch = vintf::Arch::ARCH_64; break; case lshal::ARCH_BOTH: arch = vintf::Arch::ARCH_32_64; break; arch = vintf::Arch::ARCH_32_64; break; case lshal::ARCH_UNKNOWN: // fallthrough default: err() << "Warning: '" << fqName.package() << "' doesn't have bitness info, assuming 32+64." << std::endl; arch = vintf::Arch::ARCH_32_64; err() << "Warning: '" << entry.interfaceName << "' doesn't have bitness info."; return false; } } else { err() << "Warning: '" << entry.transport << "' is not a valid transport." << std::endl; continue; return false; } bool done = false; for (vintf::ManifestHal *hal : manifest.getHals(fqName.package())) { if (hal->transport() != transport) { if (transport != vintf::Transport::PASSTHROUGH) { err() << "Fatal: should not reach here. Generated result may be wrong for '" << hal->name << "'." << std::endl; } done = true; break; std::string e; if (!manifest->insertInstance(fqInstance, transport, arch, vintf::HalFormat::HIDL, &e)) { err() << "Warning: Cannot insert '" << fqInstance.string() << ": " << e << std::endl; return false; } if (findAndBumpVersion(hal, version)) { if (&table != &mImplementationsTable) { hal->insertLegacyInstance(interfaceName, instanceName); return true; } hal->transportArch.arch |= arch; done = true; break; bool ListCommand::addEntryWithoutInstance(const TableEntry& entry, const vintf::HalManifest* manifest) const { const auto& packageAndVersion = splitFirst(splitFirst(entry.interfaceName, ':').first, '@'); const auto& package = packageAndVersion.first; vintf::Version version; if (!vintf::parse(packageAndVersion.second, &version)) { err() << "Warning: Cannot parse version '" << packageAndVersion.second << "' for entry '" << entry.interfaceName << "'" << std::endl; return false; } bool found = false; (void)manifest->forEachInstanceOfVersion(package, version, [&found](const auto&) { found = true; return false; // break }); return found; } if (done) { continue; // to next TableEntry void ListCommand::dumpVintf(const NullableOStream<std::ostream>& out) const { using vintf::operator|=; using vintf::operator<<; using namespace std::placeholders; vintf::HalManifest manifest; manifest.setType(toSchemaType(mVintfPartition)); std::vector<std::string> error; for (const TableEntry& entry : mServicesTable) if (!addEntryWithInstance(entry, &manifest)) error.push_back(entry.interfaceName); for (const TableEntry& entry : mPassthroughRefTable) if (!addEntryWithInstance(entry, &manifest)) error.push_back(entry.interfaceName); std::vector<std::string> passthrough; for (const TableEntry& entry : mImplementationsTable) if (!addEntryWithoutInstance(entry, &manifest)) passthrough.push_back(entry.interfaceName); out << "<!-- " << std::endl << " This is a skeleton " << manifest.type() << " manifest. Notes: " << std::endl << INIT_VINTF_NOTES; if (!error.empty()) { out << std::endl << " The following HALs are not added; see warnings." << std::endl; for (const auto& e : error) { out << " " << e << std::endl; } vintf::ManifestHal manifestHal{ vintf::HalFormat::HIDL, std::string{fqName.package()}, {version}, {transport, arch}, {}}; if (&table != &mImplementationsTable) { manifestHal.insertLegacyInstance(interfaceName, instanceName); } if (!manifest.add(std::move(manifestHal))) { err() << "Warning: cannot add hal '" << fqInstanceName << "'" << std::endl; if (!passthrough.empty()) { out << std::endl << " The following HALs are passthrough and no interface or instance " << std::endl << " names can be inferred." << std::endl; for (const auto& e : passthrough) { out << " " << e << std::endl; } } }); out << "<!-- " << std::endl << " This is a skeleton " << manifest.type() << " manifest. Notes: " << std::endl << INIT_VINTF_NOTES << "-->" << std::endl; out << vintf::gHalManifestConverter(manifest, vintf::SerializeFlag::HALS_NO_FQNAME); out << "-->" << std::endl; out << vintf::gHalManifestConverter(manifest, vintf::SerializeFlag::HALS_ONLY); } std::string ListCommand::INIT_VINTF_NOTES{ Loading @@ -416,6 +407,14 @@ std::string ListCommand::INIT_VINTF_NOTES{ " <interface> declared; users will have to write them by hand.\n" " 3. A HAL with lower minor version can be overridden by a HAL with\n" " higher minor version if they have the same name and major version.\n" " 4. This output is intended for launch devices.\n" " Upgrading devices should not use this tool to generate device\n" " manifest and replace the existing manifest directly, but should\n" " edit the existing manifest manually.\n" " Specifically, devices which launched at Android O-MR1 or earlier\n" " should not use the 'fqname' format for required HAL entries and\n" " should instead use the legacy package, name, instance-name format\n" " until they are updated.\n" }; static Architecture fromBaseArchitecture(::android::hidl::base::V1_0::DebugInfo::Architecture a) { Loading cmds/lshal/ListCommand.h +7 −2 Original line number Diff line number Diff line Loading @@ -26,7 +26,8 @@ #include <android-base/macros.h> #include <android/hidl/manager/1.0/IServiceManager.h> #include <hidl-util/FQName.h> #include <hidl-util/FqInstance.h> #include <vintf/HalManifest.h> #include "Command.h" #include "NullableOStream.h" Loading Loading @@ -113,7 +114,7 @@ protected: void removeDeadProcesses(Pids *pids); virtual Partition getPartition(pid_t pid); Partition resolvePartition(Partition processPartition, const FQName& fqName) const; Partition resolvePartition(Partition processPartition, const FqInstance &fqInstance) const; void forEachTable(const std::function<void(Table &)> &f); void forEachTable(const std::function<void(const Table &)> &f) const; Loading @@ -123,6 +124,10 @@ protected: void registerAllOptions(); // helper functions to dumpVintf. bool addEntryWithInstance(const TableEntry &entry, vintf::HalManifest *manifest) const; bool addEntryWithoutInstance(const TableEntry &entry, const vintf::HalManifest *manifest) const; Table mServicesTable{}; Table mPassthroughRefTable{}; Table mImplementationsTable{}; Loading cmds/lshal/test.cpp +26 −53 Original line number Diff line number Diff line Loading @@ -418,62 +418,35 @@ TEST_F(ListTest, Fetch) { } TEST_F(ListTest, DumpVintf) { const std::string expected = "<!-- \n" " This is a skeleton device manifest. Notes: \n" + ListCommand::INIT_VINTF_NOTES + "-->\n" "<manifest version=\"1.0\" type=\"device\">\n" const std::string expected = "<manifest version=\"1.0\" type=\"device\">\n" " <hal format=\"hidl\">\n" " <name>a.h.foo1</name>\n" " <transport>hwbinder</transport>\n" " <version>1.0</version>\n" " <interface>\n" " <name>IFoo</name>\n" " <instance>1</instance>\n" " </interface>\n" " <fqname>@1.0::IFoo/1</fqname>\n" " </hal>\n" " <hal format=\"hidl\">\n" " <name>a.h.foo2</name>\n" " <transport>hwbinder</transport>\n" " <version>2.0</version>\n" " <interface>\n" " <name>IFoo</name>\n" " <instance>2</instance>\n" " </interface>\n" " <fqname>@2.0::IFoo/2</fqname>\n" " </hal>\n" " <hal format=\"hidl\">\n" " <name>a.h.foo3</name>\n" " <transport arch=\"32\">passthrough</transport>\n" " <version>3.0</version>\n" " <interface>\n" " <name>IFoo</name>\n" " <instance>3</instance>\n" " </interface>\n" " <fqname>@3.0::IFoo/3</fqname>\n" " </hal>\n" " <hal format=\"hidl\">\n" " <name>a.h.foo4</name>\n" " <transport arch=\"32\">passthrough</transport>\n" " <version>4.0</version>\n" " <interface>\n" " <name>IFoo</name>\n" " <instance>4</instance>\n" " </interface>\n" " </hal>\n" " <hal format=\"hidl\">\n" " <name>a.h.foo5</name>\n" " <transport arch=\"32\">passthrough</transport>\n" " <version>5.0</version>\n" " <fqname>@4.0::IFoo/4</fqname>\n" " </hal>\n" " <hal format=\"hidl\">\n" " <name>a.h.foo6</name>\n" " <transport arch=\"32\">passthrough</transport>\n" " <version>6.0</version>\n" " </hal>\n" "</manifest>\n"; "</manifest>"; optind = 1; // mimic Lshal::parseArg() EXPECT_EQ(0u, mockList->main(createArg({"lshal", "--init-vintf"}))); EXPECT_EQ(expected, out.str()); auto output = out.str(); EXPECT_THAT(output, HasSubstr(expected)); EXPECT_THAT(output, HasSubstr("a.h.foo5@5.0::IFoo/5")); EXPECT_THAT(output, HasSubstr("a.h.foo6@6.0::IFoo/6")); EXPECT_EQ("", err.str()); vintf::HalManifest m; Loading Loading
cmds/lshal/ListCommand.cpp +117 −118 Original line number Diff line number Diff line Loading @@ -19,11 +19,12 @@ #include <getopt.h> #include <fstream> #include <functional> #include <iomanip> #include <iostream> #include <map> #include <sstream> #include <regex> #include <sstream> #include <android-base/file.h> #include <android-base/parseint.h> Loading Loading @@ -101,21 +102,19 @@ Partition ListCommand::getPartition(pid_t pid) { // Give sensible defaults when nothing can be inferred from runtime. // process: Partition inferred from executable location or cmdline. Partition ListCommand::resolvePartition(Partition process, const FQName& fqName) const { if (fqName.inPackage("vendor") || fqName.inPackage("com")) { Partition ListCommand::resolvePartition(Partition process, const FqInstance& fqInstance) const { if (fqInstance.inPackage("vendor") || fqInstance.inPackage("com")) { return Partition::VENDOR; } if (fqName.inPackage("android.frameworks") || fqName.inPackage("android.system") || fqName.inPackage("android.hidl")) { if (fqInstance.inPackage("android.frameworks") || fqInstance.inPackage("android.system") || fqInstance.inPackage("android.hidl")) { return Partition::SYSTEM; } // Some android.hardware HALs are served from system. Check the value from executable // location / cmdline first. if (fqName.inPackage("android.hardware")) { if (fqInstance.inPackage("android.hardware")) { if (process != Partition::UNKNOWN) { return process; } Loading Loading @@ -284,62 +283,30 @@ void ListCommand::postprocess() { "These may return subclasses through their respective HIDL_FETCH_I* functions."); } static inline bool findAndBumpVersion(vintf::ManifestHal* hal, const vintf::Version& version) { for (vintf::Version& v : hal->versions) { if (v.majorVer == version.majorVer) { v.minorVer = std::max(v.minorVer, version.minorVer); return true; } } bool ListCommand::addEntryWithInstance(const TableEntry& entry, vintf::HalManifest* manifest) const { FqInstance fqInstance; if (!fqInstance.setTo(entry.interfaceName)) { err() << "Warning: '" << entry.interfaceName << "' is not a valid FqInstance." << std::endl; return false; } void ListCommand::dumpVintf(const NullableOStream<std::ostream>& out) const { using vintf::operator|=; using vintf::operator<<; vintf::HalManifest manifest; manifest.setType(toSchemaType(mVintfPartition)); forEachTable([this, &manifest] (const Table &table) { for (const TableEntry &entry : table) { std::string fqInstanceName = entry.interfaceName; if (&table == &mImplementationsTable) { // Quick hack to work around *'s replaceAll(&fqInstanceName, '*', 'D'); } auto splitFqInstanceName = splitFirst(fqInstanceName, '/'); FQName fqName; if (!FQName::parse(splitFqInstanceName.first, &fqName)) { err() << "Warning: '" << splitFqInstanceName.first << "' is not a valid FQName." << std::endl; continue; } if (fqName.package() == gIBaseFqName.package()) { continue; // always remove IBase from manifest if (fqInstance.getPackage() == gIBaseFqName.package()) { return true; // always remove IBase from manifest } Partition partition = resolvePartition(entry.partition, fqName); Partition partition = resolvePartition(entry.partition, fqInstance); if (partition == Partition::UNKNOWN) { err() << "Warning: Cannot guess the partition of instance " << fqInstanceName << ". It is removed from the generated manifest." << std::endl; continue; err() << "Warning: Cannot guess the partition of FqInstance " << fqInstance.string() << std::endl; return false; } if (partition != mVintfPartition) { continue; // strip out instances that is in a different partition. return true; // strip out instances that is in a different partition. } std::string interfaceName = &table == &mImplementationsTable ? "" : fqName.name(); std::string instanceName = &table == &mImplementationsTable ? "" : splitFqInstanceName.second; vintf::Version version{fqName.getPackageMajorVersion(), fqName.getPackageMinorVersion()}; vintf::Transport transport; vintf::Arch arch; if (entry.transport == "hwbinder") { Loading @@ -349,64 +316,88 @@ void ListCommand::dumpVintf(const NullableOStream<std::ostream>& out) const { transport = vintf::Transport::PASSTHROUGH; switch (entry.arch) { case lshal::ARCH32: arch = vintf::Arch::ARCH_32; break; arch = vintf::Arch::ARCH_32; break; case lshal::ARCH64: arch = vintf::Arch::ARCH_64; break; arch = vintf::Arch::ARCH_64; break; case lshal::ARCH_BOTH: arch = vintf::Arch::ARCH_32_64; break; arch = vintf::Arch::ARCH_32_64; break; case lshal::ARCH_UNKNOWN: // fallthrough default: err() << "Warning: '" << fqName.package() << "' doesn't have bitness info, assuming 32+64." << std::endl; arch = vintf::Arch::ARCH_32_64; err() << "Warning: '" << entry.interfaceName << "' doesn't have bitness info."; return false; } } else { err() << "Warning: '" << entry.transport << "' is not a valid transport." << std::endl; continue; return false; } bool done = false; for (vintf::ManifestHal *hal : manifest.getHals(fqName.package())) { if (hal->transport() != transport) { if (transport != vintf::Transport::PASSTHROUGH) { err() << "Fatal: should not reach here. Generated result may be wrong for '" << hal->name << "'." << std::endl; } done = true; break; std::string e; if (!manifest->insertInstance(fqInstance, transport, arch, vintf::HalFormat::HIDL, &e)) { err() << "Warning: Cannot insert '" << fqInstance.string() << ": " << e << std::endl; return false; } if (findAndBumpVersion(hal, version)) { if (&table != &mImplementationsTable) { hal->insertLegacyInstance(interfaceName, instanceName); return true; } hal->transportArch.arch |= arch; done = true; break; bool ListCommand::addEntryWithoutInstance(const TableEntry& entry, const vintf::HalManifest* manifest) const { const auto& packageAndVersion = splitFirst(splitFirst(entry.interfaceName, ':').first, '@'); const auto& package = packageAndVersion.first; vintf::Version version; if (!vintf::parse(packageAndVersion.second, &version)) { err() << "Warning: Cannot parse version '" << packageAndVersion.second << "' for entry '" << entry.interfaceName << "'" << std::endl; return false; } bool found = false; (void)manifest->forEachInstanceOfVersion(package, version, [&found](const auto&) { found = true; return false; // break }); return found; } if (done) { continue; // to next TableEntry void ListCommand::dumpVintf(const NullableOStream<std::ostream>& out) const { using vintf::operator|=; using vintf::operator<<; using namespace std::placeholders; vintf::HalManifest manifest; manifest.setType(toSchemaType(mVintfPartition)); std::vector<std::string> error; for (const TableEntry& entry : mServicesTable) if (!addEntryWithInstance(entry, &manifest)) error.push_back(entry.interfaceName); for (const TableEntry& entry : mPassthroughRefTable) if (!addEntryWithInstance(entry, &manifest)) error.push_back(entry.interfaceName); std::vector<std::string> passthrough; for (const TableEntry& entry : mImplementationsTable) if (!addEntryWithoutInstance(entry, &manifest)) passthrough.push_back(entry.interfaceName); out << "<!-- " << std::endl << " This is a skeleton " << manifest.type() << " manifest. Notes: " << std::endl << INIT_VINTF_NOTES; if (!error.empty()) { out << std::endl << " The following HALs are not added; see warnings." << std::endl; for (const auto& e : error) { out << " " << e << std::endl; } vintf::ManifestHal manifestHal{ vintf::HalFormat::HIDL, std::string{fqName.package()}, {version}, {transport, arch}, {}}; if (&table != &mImplementationsTable) { manifestHal.insertLegacyInstance(interfaceName, instanceName); } if (!manifest.add(std::move(manifestHal))) { err() << "Warning: cannot add hal '" << fqInstanceName << "'" << std::endl; if (!passthrough.empty()) { out << std::endl << " The following HALs are passthrough and no interface or instance " << std::endl << " names can be inferred." << std::endl; for (const auto& e : passthrough) { out << " " << e << std::endl; } } }); out << "<!-- " << std::endl << " This is a skeleton " << manifest.type() << " manifest. Notes: " << std::endl << INIT_VINTF_NOTES << "-->" << std::endl; out << vintf::gHalManifestConverter(manifest, vintf::SerializeFlag::HALS_NO_FQNAME); out << "-->" << std::endl; out << vintf::gHalManifestConverter(manifest, vintf::SerializeFlag::HALS_ONLY); } std::string ListCommand::INIT_VINTF_NOTES{ Loading @@ -416,6 +407,14 @@ std::string ListCommand::INIT_VINTF_NOTES{ " <interface> declared; users will have to write them by hand.\n" " 3. A HAL with lower minor version can be overridden by a HAL with\n" " higher minor version if they have the same name and major version.\n" " 4. This output is intended for launch devices.\n" " Upgrading devices should not use this tool to generate device\n" " manifest and replace the existing manifest directly, but should\n" " edit the existing manifest manually.\n" " Specifically, devices which launched at Android O-MR1 or earlier\n" " should not use the 'fqname' format for required HAL entries and\n" " should instead use the legacy package, name, instance-name format\n" " until they are updated.\n" }; static Architecture fromBaseArchitecture(::android::hidl::base::V1_0::DebugInfo::Architecture a) { Loading
cmds/lshal/ListCommand.h +7 −2 Original line number Diff line number Diff line Loading @@ -26,7 +26,8 @@ #include <android-base/macros.h> #include <android/hidl/manager/1.0/IServiceManager.h> #include <hidl-util/FQName.h> #include <hidl-util/FqInstance.h> #include <vintf/HalManifest.h> #include "Command.h" #include "NullableOStream.h" Loading Loading @@ -113,7 +114,7 @@ protected: void removeDeadProcesses(Pids *pids); virtual Partition getPartition(pid_t pid); Partition resolvePartition(Partition processPartition, const FQName& fqName) const; Partition resolvePartition(Partition processPartition, const FqInstance &fqInstance) const; void forEachTable(const std::function<void(Table &)> &f); void forEachTable(const std::function<void(const Table &)> &f) const; Loading @@ -123,6 +124,10 @@ protected: void registerAllOptions(); // helper functions to dumpVintf. bool addEntryWithInstance(const TableEntry &entry, vintf::HalManifest *manifest) const; bool addEntryWithoutInstance(const TableEntry &entry, const vintf::HalManifest *manifest) const; Table mServicesTable{}; Table mPassthroughRefTable{}; Table mImplementationsTable{}; Loading
cmds/lshal/test.cpp +26 −53 Original line number Diff line number Diff line Loading @@ -418,62 +418,35 @@ TEST_F(ListTest, Fetch) { } TEST_F(ListTest, DumpVintf) { const std::string expected = "<!-- \n" " This is a skeleton device manifest. Notes: \n" + ListCommand::INIT_VINTF_NOTES + "-->\n" "<manifest version=\"1.0\" type=\"device\">\n" const std::string expected = "<manifest version=\"1.0\" type=\"device\">\n" " <hal format=\"hidl\">\n" " <name>a.h.foo1</name>\n" " <transport>hwbinder</transport>\n" " <version>1.0</version>\n" " <interface>\n" " <name>IFoo</name>\n" " <instance>1</instance>\n" " </interface>\n" " <fqname>@1.0::IFoo/1</fqname>\n" " </hal>\n" " <hal format=\"hidl\">\n" " <name>a.h.foo2</name>\n" " <transport>hwbinder</transport>\n" " <version>2.0</version>\n" " <interface>\n" " <name>IFoo</name>\n" " <instance>2</instance>\n" " </interface>\n" " <fqname>@2.0::IFoo/2</fqname>\n" " </hal>\n" " <hal format=\"hidl\">\n" " <name>a.h.foo3</name>\n" " <transport arch=\"32\">passthrough</transport>\n" " <version>3.0</version>\n" " <interface>\n" " <name>IFoo</name>\n" " <instance>3</instance>\n" " </interface>\n" " <fqname>@3.0::IFoo/3</fqname>\n" " </hal>\n" " <hal format=\"hidl\">\n" " <name>a.h.foo4</name>\n" " <transport arch=\"32\">passthrough</transport>\n" " <version>4.0</version>\n" " <interface>\n" " <name>IFoo</name>\n" " <instance>4</instance>\n" " </interface>\n" " </hal>\n" " <hal format=\"hidl\">\n" " <name>a.h.foo5</name>\n" " <transport arch=\"32\">passthrough</transport>\n" " <version>5.0</version>\n" " <fqname>@4.0::IFoo/4</fqname>\n" " </hal>\n" " <hal format=\"hidl\">\n" " <name>a.h.foo6</name>\n" " <transport arch=\"32\">passthrough</transport>\n" " <version>6.0</version>\n" " </hal>\n" "</manifest>\n"; "</manifest>"; optind = 1; // mimic Lshal::parseArg() EXPECT_EQ(0u, mockList->main(createArg({"lshal", "--init-vintf"}))); EXPECT_EQ(expected, out.str()); auto output = out.str(); EXPECT_THAT(output, HasSubstr(expected)); EXPECT_THAT(output, HasSubstr("a.h.foo5@5.0::IFoo/5")); EXPECT_THAT(output, HasSubstr("a.h.foo6@6.0::IFoo/6")); EXPECT_EQ("", err.str()); vintf::HalManifest m; Loading