diff options
author | 2018-05-25 14:20:00 -0700 | |
---|---|---|
committer | 2018-05-29 10:31:07 -0700 | |
commit | bdf44f81d0b7ffb2bf1d9a6c5fbbfb25b55ba565 (patch) | |
tree | cb0b17306fcf5508b16b40ededc05ccd19fbf56f | |
parent | 0ad64f522c7735c188fe50c989fdd39975cd127a (diff) |
lshal: Add VINTF column
Fixes: 71555570
Test: lshal_test
Change-Id: I7ac5ef5a920d41c0f534195c764b1a300429a367
-rw-r--r-- | cmds/lshal/ListCommand.cpp | 71 | ||||
-rw-r--r-- | cmds/lshal/ListCommand.h | 10 | ||||
-rw-r--r-- | cmds/lshal/TableEntry.cpp | 23 | ||||
-rw-r--r-- | cmds/lshal/TableEntry.h | 13 | ||||
-rw-r--r-- | cmds/lshal/test.cpp | 100 |
5 files changed, 217 insertions, 0 deletions
diff --git a/cmds/lshal/ListCommand.cpp b/cmds/lshal/ListCommand.cpp index 5dbac1b9a3..9c77f70adc 100644 --- a/cmds/lshal/ListCommand.cpp +++ b/cmds/lshal/ListCommand.cpp @@ -126,6 +126,67 @@ Partition ListCommand::resolvePartition(Partition process, const FqInstance& fqI return process; } +bool match(const vintf::ManifestInstance& instance, const FqInstance& fqInstance, + vintf::TransportArch ta) { + // For hwbinder libs, allow missing arch in manifest. + // For passthrough libs, allow missing interface/instance in table. + return (ta.transport == instance.transport()) && + (ta.transport == vintf::Transport::HWBINDER || + vintf::contains(instance.arch(), ta.arch)) && + (!fqInstance.hasInterface() || fqInstance.getInterface() == instance.interface()) && + (!fqInstance.hasInstance() || fqInstance.getInstance() == instance.instance()); +} + +bool match(const vintf::MatrixInstance& instance, const FqInstance& fqInstance, + vintf::TransportArch /* ta */) { + return (!fqInstance.hasInterface() || fqInstance.getInterface() == instance.interface()) && + (!fqInstance.hasInstance() || instance.matchInstance(fqInstance.getInstance())); +} + +template <typename ObjectType> +VintfInfo getVintfInfo(const std::shared_ptr<const ObjectType>& object, + const FqInstance& fqInstance, vintf::TransportArch ta, VintfInfo value) { + bool found = false; + (void)object->forEachInstanceOfVersion(fqInstance.getPackage(), fqInstance.getVersion(), + [&](const auto& instance) { + found = match(instance, fqInstance, ta); + return !found; // continue if not found + }); + return found ? value : VINTF_INFO_EMPTY; +} + +std::shared_ptr<const vintf::HalManifest> ListCommand::getDeviceManifest() const { + return vintf::VintfObject::GetDeviceHalManifest(); +} + +std::shared_ptr<const vintf::CompatibilityMatrix> ListCommand::getDeviceMatrix() const { + return vintf::VintfObject::GetDeviceCompatibilityMatrix(); +} + +std::shared_ptr<const vintf::HalManifest> ListCommand::getFrameworkManifest() const { + return vintf::VintfObject::GetFrameworkHalManifest(); +} + +std::shared_ptr<const vintf::CompatibilityMatrix> ListCommand::getFrameworkMatrix() const { + return vintf::VintfObject::GetFrameworkCompatibilityMatrix(); +} + +VintfInfo ListCommand::getVintfInfo(const std::string& fqInstanceName, + vintf::TransportArch ta) const { + FqInstance fqInstance; + if (!fqInstance.setTo(fqInstanceName) && + // Ignore interface / instance for passthrough libs + !fqInstance.setTo(splitFirst(fqInstanceName, ':').first)) { + err() << "Warning: Cannot parse '" << fqInstanceName << "'; no VINTF info." << std::endl; + return VINTF_INFO_EMPTY; + } + + return lshal::getVintfInfo(getDeviceManifest(), fqInstance, ta, DEVICE_MANIFEST) | + lshal::getVintfInfo(getFrameworkManifest(), fqInstance, ta, FRAMEWORK_MANIFEST) | + lshal::getVintfInfo(getDeviceMatrix(), fqInstance, ta, DEVICE_MATRIX) | + lshal::getVintfInfo(getFrameworkMatrix(), fqInstance, ta, FRAMEWORK_MATRIX); +} + static bool scanBinderContext(pid_t pid, const std::string &contextName, std::function<void(const std::string&)> eachLine) { @@ -269,6 +330,7 @@ void ListCommand::postprocess() { } for (TableEntry& entry : table) { entry.partition = getPartition(entry.serverPid); + entry.vintfInfo = getVintfInfo(entry.interfaceName, {entry.transport, entry.arch}); } }); // use a double for loop here because lshal doesn't care about efficiency. @@ -777,6 +839,15 @@ void ListCommand::registerAllOptions() { }, "Emit debug info from\nIBase::debug with empty options. Cannot be used with --neat.\n" "Writes to specified file if 'arg' is provided, otherwise stdout."}); + mOptions.push_back({'V', "vintf", no_argument, v++, [](ListCommand* thiz, const char*) { + thiz->mSelectedColumns.push_back(TableColumnType::VINTF); + return OK; + }, "print VINTF info. This column contains a comma-separated list of:\n" + " - DM: device manifest\n" + " - DC: device compatibility matrix\n" + " - FM: framework manifest\n" + " - FC: framework compatibility matrix"}); + // long options without short alternatives mOptions.push_back({'\0', "init-vintf", no_argument, v++, [](ListCommand* thiz, const char* arg) { thiz->mVintf = true; diff --git a/cmds/lshal/ListCommand.h b/cmds/lshal/ListCommand.h index c35561d035..87d93b576a 100644 --- a/cmds/lshal/ListCommand.h +++ b/cmds/lshal/ListCommand.h @@ -28,6 +28,7 @@ #include <android/hidl/manager/1.0/IServiceManager.h> #include <hidl-util/FqInstance.h> #include <vintf/HalManifest.h> +#include <vintf/VintfObject.h> #include "Command.h" #include "NullableOStream.h" @@ -87,7 +88,9 @@ public: protected: Status parseArgs(const Arg &arg); + // Retrieve first-hand information Status fetch(); + // Retrieve derived information base on existing table virtual void postprocess(); Status dump(); void putEntry(TableEntrySource source, TableEntry &&entry); @@ -122,6 +125,13 @@ protected: virtual Partition getPartition(pid_t pid); Partition resolvePartition(Partition processPartition, const FqInstance &fqInstance) const; + VintfInfo getVintfInfo(const std::string &fqInstanceName, vintf::TransportArch ta) const; + // Allow to mock these functions for testing. + virtual std::shared_ptr<const vintf::HalManifest> getDeviceManifest() const; + virtual std::shared_ptr<const vintf::CompatibilityMatrix> getDeviceMatrix() const; + virtual std::shared_ptr<const vintf::HalManifest> getFrameworkManifest() const; + virtual std::shared_ptr<const vintf::CompatibilityMatrix> getFrameworkMatrix() const; + void forEachTable(const std::function<void(Table &)> &f); void forEachTable(const std::function<void(const Table &)> &f) const; diff --git a/cmds/lshal/TableEntry.cpp b/cmds/lshal/TableEntry.cpp index fbd17eec63..0154a2e431 100644 --- a/cmds/lshal/TableEntry.cpp +++ b/cmds/lshal/TableEntry.cpp @@ -16,6 +16,9 @@ #define LOG_TAG "lshal" #include <android-base/logging.h> +#include <map> + +#include <android-base/strings.h> #include <hidl-hash/Hash.h> #include <vintf/parse_string.h> @@ -58,6 +61,7 @@ static std::string getTitle(TableColumnType type) { case TableColumnType::THREADS: return "Thread Use"; case TableColumnType::RELEASED: return "R"; case TableColumnType::HASH: return "Hash"; + case TableColumnType::VINTF: return "VINTF"; default: LOG(FATAL) << __func__ << "Should not reach here. " << static_cast<int>(type); return ""; @@ -88,6 +92,8 @@ std::string TableEntry::getField(TableColumnType type) const { return isReleased(); case TableColumnType::HASH: return hash; + case TableColumnType::VINTF: + return getVintfInfo(); default: LOG(FATAL) << __func__ << "Should not reach here. " << static_cast<int>(type); return ""; @@ -106,6 +112,23 @@ std::string TableEntry::isReleased() const { return "Y"; // released } +std::string TableEntry::getVintfInfo() const { + static const std::map<VintfInfo, std::string> values{ + {DEVICE_MANIFEST, "DM"}, + {DEVICE_MATRIX, "DC"}, + {FRAMEWORK_MANIFEST, "FM"}, + {FRAMEWORK_MATRIX, "FC"}, + }; + std::vector<std::string> ret; + for (const auto& pair : values) { + if (vintfInfo & pair.first) { + ret.push_back(pair.second); + } + } + auto joined = base::Join(ret, ','); + return joined.empty() ? "X" : joined; +} + TextTable Table::createTextTable(bool neat, const std::function<std::string(const std::string&)>& emitDebugInfo) const { diff --git a/cmds/lshal/TableEntry.h b/cmds/lshal/TableEntry.h index 08a1303600..c9a6a23953 100644 --- a/cmds/lshal/TableEntry.h +++ b/cmds/lshal/TableEntry.h @@ -54,8 +54,18 @@ enum class TableColumnType : unsigned int { THREADS, RELEASED, HASH, + VINTF, }; +enum : unsigned int { + VINTF_INFO_EMPTY = 0, + DEVICE_MANIFEST = 1 << 0, + DEVICE_MATRIX = 1 << 1, + FRAMEWORK_MANIFEST = 1 << 2, + FRAMEWORK_MATRIX = 1 << 3, +}; +using VintfInfo = unsigned int; + enum { NO_PID = -1, NO_PTR = 0 @@ -75,6 +85,7 @@ struct TableEntry { // empty: unknown, all zeros: unreleased, otherwise: released std::string hash{}; Partition partition{Partition::UNKNOWN}; + VintfInfo vintfInfo{VINTF_INFO_EMPTY}; static bool sortByInterfaceName(const TableEntry &a, const TableEntry &b) { return a.interfaceName < b.interfaceName; @@ -93,6 +104,8 @@ struct TableEntry { std::string isReleased() const; + std::string getVintfInfo() const; + std::string getField(TableColumnType type) const; bool operator==(const TableEntry& other) const; diff --git a/cmds/lshal/test.cpp b/cmds/lshal/test.cpp index 2af01996d4..501c04de84 100644 --- a/cmds/lshal/test.cpp +++ b/cmds/lshal/test.cpp @@ -45,7 +45,12 @@ using ::android::hardware::hidl_handle; using ::android::hardware::hidl_string; using ::android::hardware::hidl_vec; using android::vintf::Arch; +using android::vintf::CompatibilityMatrix; +using android::vintf::gCompatibilityMatrixConverter; +using android::vintf::gHalManifestConverter; +using android::vintf::HalManifest; using android::vintf::Transport; +using android::vintf::VintfObject; using InstanceDebugInfo = IServiceManager::InstanceDebugInfo; @@ -209,6 +214,11 @@ public: MOCK_CONST_METHOD2(getPidInfo, bool(pid_t, PidInfo*)); MOCK_CONST_METHOD1(parseCmdline, std::string(pid_t)); MOCK_METHOD1(getPartition, Partition(pid_t)); + + MOCK_CONST_METHOD0(getDeviceManifest, std::shared_ptr<const vintf::HalManifest>()); + MOCK_CONST_METHOD0(getDeviceMatrix, std::shared_ptr<const vintf::CompatibilityMatrix>()); + MOCK_CONST_METHOD0(getFrameworkManifest, std::shared_ptr<const vintf::HalManifest>()); + MOCK_CONST_METHOD0(getFrameworkMatrix, std::shared_ptr<const vintf::CompatibilityMatrix>()); }; class ListParseArgsTest : public ::testing::Test { @@ -337,6 +347,15 @@ public: }); })); ON_CALL(*mockList, getPartition(_)).WillByDefault(Return(Partition::VENDOR)); + + ON_CALL(*mockList, getDeviceManifest()) + .WillByDefault(Return(VintfObject::GetDeviceHalManifest())); + ON_CALL(*mockList, getDeviceMatrix()) + .WillByDefault(Return(VintfObject::GetDeviceCompatibilityMatrix())); + ON_CALL(*mockList, getFrameworkManifest()) + .WillByDefault(Return(VintfObject::GetFrameworkHalManifest())); + ON_CALL(*mockList, getFrameworkMatrix()) + .WillByDefault(Return(VintfObject::GetFrameworkCompatibilityMatrix())); } void initMockServiceManager() { @@ -656,6 +675,87 @@ TEST_F(ListTest, UnknownHalType) { EXPECT_THAT(err.str(), HasSubstr("Unrecognized HAL type: a")); } +TEST_F(ListTest, Vintf) { + std::string deviceManifestXml = + "<manifest version=\"1.0\" type=\"device\">\n" + " <hal>\n" + " <name>a.h.foo1</name>\n" + " <transport>hwbinder</transport>\n" + " <fqname>@1.0::IFoo/1</fqname>\n" + " </hal>\n" + " <hal>\n" + " <name>a.h.foo3</name>\n" + " <transport arch=\"32+64\">passthrough</transport>\n" + " <fqname>@3.0::IFoo/3</fqname>\n" + " </hal>\n" + "</manifest>\n"; + std::string frameworkManifestXml = + "<manifest version=\"1.0\" type=\"framework\">\n" + " <hal>\n" + " <name>a.h.foo5</name>\n" + " <transport arch=\"32\">passthrough</transport>\n" + " <fqname>@5.0::IFoo/5</fqname>\n" + " </hal>\n" + "</manifest>\n"; + std::string deviceMatrixXml = + "<compatibility-matrix version=\"1.0\" type=\"device\">\n" + " <hal>\n" + " <name>a.h.foo5</name>\n" + " <version>5.0</version>\n" + " <interface>\n" + " <name>IFoo</name>\n" + " <instance>5</instance>\n" + " </interface>\n" + " </hal>\n" + "</compatibility-matrix>\n"; + std::string frameworkMatrixXml = + "<compatibility-matrix version=\"1.0\" type=\"framework\">\n" + " <hal>\n" + " <name>a.h.foo1</name>\n" + " <version>1.0</version>\n" + " <interface>\n" + " <name>IFoo</name>\n" + " <instance>1</instance>\n" + " </interface>\n" + " </hal>\n" + " <hal>\n" + " <name>a.h.foo3</name>\n" + " <version>3.0</version>\n" + " <interface>\n" + " <name>IFoo</name>\n" + " <instance>3</instance>\n" + " </interface>\n" + " </hal>\n" + "</compatibility-matrix>\n"; + + std::string expected = "DM,FC a.h.foo1@1.0::IFoo/1\n" + "X a.h.foo2@2.0::IFoo/2\n" + "DM,FC a.h.foo3@3.0::IFoo/3\n" + "X a.h.foo4@4.0::IFoo/4\n" + "DC,FM a.h.foo5@5.0::IFoo/5\n" + "X a.h.foo6@6.0::IFoo/6\n"; + + auto deviceManifest = std::make_shared<HalManifest>(); + auto frameworkManifest = std::make_shared<HalManifest>(); + auto deviceMatrix = std::make_shared<CompatibilityMatrix>(); + auto frameworkMatrix = std::make_shared<CompatibilityMatrix>(); + + ASSERT_TRUE(gHalManifestConverter(deviceManifest.get(), deviceManifestXml)); + ASSERT_TRUE(gHalManifestConverter(frameworkManifest.get(), frameworkManifestXml)); + ASSERT_TRUE(gCompatibilityMatrixConverter(deviceMatrix.get(), deviceMatrixXml)); + ASSERT_TRUE(gCompatibilityMatrixConverter(frameworkMatrix.get(), frameworkMatrixXml)); + + ON_CALL(*mockList, getDeviceManifest()).WillByDefault(Return(deviceManifest)); + ON_CALL(*mockList, getDeviceMatrix()).WillByDefault(Return(deviceMatrix)); + ON_CALL(*mockList, getFrameworkManifest()).WillByDefault(Return(frameworkManifest)); + ON_CALL(*mockList, getFrameworkMatrix()).WillByDefault(Return(frameworkMatrix)); + + optind = 1; // mimic Lshal::parseArg() + EXPECT_EQ(0u, mockList->main(createArg({"lshal", "-Vi", "--neat"}))); + EXPECT_THAT(out.str(), HasSubstr(expected)); + EXPECT_EQ("", err.str()); +} + class HelpTest : public ::testing::Test { public: void SetUp() override { |