Merge "Allow vendor extension of passthrough HALs."
diff --git a/treble/vintf/Android.bp b/treble/vintf/Android.bp
index 428f93e..50a0888 100644
--- a/treble/vintf/Android.bp
+++ b/treble/vintf/Android.bp
@@ -29,6 +29,7 @@
"libz",
],
static_libs: [
+ "libgmock",
"libhidl-gen-hash",
"libhidl-gen-utils",
"libprocpartition",
diff --git a/treble/vintf/SingleManifestTest.cpp b/treble/vintf/SingleManifestTest.cpp
index 42acfc2..17ae647 100644
--- a/treble/vintf/SingleManifestTest.cpp
+++ b/treble/vintf/SingleManifestTest.cpp
@@ -16,12 +16,18 @@
#include "SingleManifestTest.h"
+#include <android-base/strings.h>
+#include <gmock/gmock.h>
#include <hidl-util/FqInstance.h>
#include <hidl/HidlTransportUtils.h>
#include <vintf/parse_string.h>
+#include <algorithm>
+
#include "utils.h"
+using ::testing::AnyOf;
+
namespace android {
namespace vintf {
namespace testing {
@@ -61,29 +67,179 @@
}
}
+template <typename It>
+static string RangeInstancesToString(const std::pair<It, It> &range) {
+ std::stringstream ss;
+ for (auto it = range.first; it != range.second; ++it) {
+ if (it != range.first) ss << ", ";
+ ss << it->second.string();
+ }
+ return ss.str();
+}
+
+template <typename Container>
+static string InstancesToString(const Container &container) {
+ std::stringstream ss;
+ for (auto it = container.begin(); it != container.end(); ++it) {
+ if (it != container.begin()) ss << ", ";
+ ss << *it;
+ }
+ return ss.str();
+}
+
+static FqInstance ToFqInstance(const string &interface,
+ const string &instance) {
+ FqInstance fq_interface;
+ FqInstance ret;
+
+ if (!fq_interface.setTo(interface)) {
+ ADD_FAILURE() << interface << " is not a valid FQName";
+ return ret;
+ }
+ if (!ret.setTo(fq_interface.getPackage(), fq_interface.getMajorVersion(),
+ fq_interface.getMinorVersion(), fq_interface.getInterface(),
+ instance)) {
+ ADD_FAILURE() << "Cannot convert to FqInstance: " << interface << "/"
+ << instance;
+ }
+ return ret;
+}
+
+// Given android.foo.bar@x.y::IFoo/default, attempt to get
+// android.foo.bar@x.y::IFoo/default, android.foo.bar@x.(y-1)::IFoo/default,
+// ... android.foo.bar@x.0::IFoo/default until the passthrough HAL is retrieved.
+static sp<IBase> GetPassthroughService(const FqInstance &fq_instance) {
+ for (size_t minor_version = fq_instance.getMinorVersion();; --minor_version) {
+ // String out instance name from fq_instance.
+ FqInstance interface;
+ if (!interface.setTo(fq_instance.getPackage(),
+ fq_instance.getMajorVersion(), minor_version,
+ fq_instance.getInterface())) {
+ ADD_FAILURE() << fq_instance.string()
+ << " doesn't contain a valid FQName";
+ return nullptr;
+ }
+
+ auto hal_service = VtsTrebleVintfTestBase::GetHalService(
+ interface.string(), fq_instance.getInstance(), Transport::PASSTHROUGH);
+
+ if (hal_service != nullptr) {
+ bool interface_chain_valid = false;
+ hal_service->interfaceChain([&](const auto &chain) {
+ for (const auto &intf : chain) {
+ if (intf == interface.string()) {
+ interface_chain_valid = true;
+ return;
+ }
+ }
+ });
+ if (!interface_chain_valid) {
+ ADD_FAILURE() << "Retrieved " << interface.string() << "/"
+ << fq_instance.getInstance() << " as "
+ << fq_instance.string()
+ << " but interfaceChain() doesn't contain "
+ << fq_instance.string();
+ return nullptr;
+ }
+ cout << "Retrieved " << interface.string() << "/"
+ << fq_instance.getInstance() << " as " << fq_instance.string()
+ << endl;
+ return hal_service;
+ }
+
+ if (minor_version == 0) {
+ return nullptr;
+ }
+ }
+ ADD_FAILURE() << "Should not reach here";
+ return nullptr;
+}
+
// Tests that no HAL outside of the allowed set is specified as passthrough in
// VINTF.
TEST_P(SingleManifestTest, HalsAreBinderized) {
- // Verifies that HAL is binderized unless it's allowed to be passthrough.
- HalVerifyFn is_binderized = [](const FQName &fq_name,
- const string & /* instance_name */,
- Transport transport) {
- cout << "Verifying transport method of: " << fq_name.string() << endl;
- string hal_name = fq_name.package();
- Version version{fq_name.getPackageMajorVersion(),
- fq_name.getPackageMinorVersion()};
- string iface_name = fq_name.name();
+ multimap<Transport, FqInstance> instances;
+ ForEachHalInstance(GetParam(), [&instances](const FQName &fq_name,
+ const string &instance_name,
+ Transport transport) {
+ FqInstance fqInstance;
+ ASSERT_TRUE(fqInstance.setTo(
+ fq_name.package(), fq_name.getPackageMajorVersion(),
+ fq_name.getPackageMinorVersion(), fq_name.name(), instance_name));
+ instances.emplace(transport, std::move(fqInstance));
+ });
- EXPECT_NE(transport, Transport::EMPTY)
- << hal_name << " has no transport specified in VINTF.";
+ for (auto it = instances.begin(); it != instances.end();
+ it = instances.upper_bound(it->first)) {
+ EXPECT_THAT(it->first, AnyOf(Transport::HWBINDER, Transport::PASSTHROUGH))
+ << "The following HALs has unknown transport specified in VINTF ("
+ << it->first << ", ordinal "
+ << static_cast<std::underlying_type_t<Transport>>(it->first) << ")"
+ << RangeInstancesToString(instances.equal_range(it->first));
+ }
- if (transport == Transport::PASSTHROUGH) {
- EXPECT_NE(kPassthroughHals.find(hal_name), kPassthroughHals.end())
- << hal_name << " can't be passthrough under Treble rules.";
+ auto passthrough_declared_range =
+ instances.equal_range(Transport::PASSTHROUGH);
+ set<FqInstance> passthrough_declared;
+ std::transform(
+ passthrough_declared_range.first, passthrough_declared_range.second,
+ std::inserter(passthrough_declared, passthrough_declared.begin()),
+ [](const auto &pair) { return pair.second; });
+
+ set<FqInstance> passthrough_allowed;
+ for (const auto &declared_instance : passthrough_declared) {
+ auto hal_service = GetPassthroughService(declared_instance);
+
+ // For vendor extensions, hal_service may be null because we don't know
+ // its interfaceChain()[1] to call getService(). However, the base interface
+ // should be declared in the manifest, so other iterations of this for-loop
+ // verify that vendor extension.
+ if (hal_service == nullptr) {
+ cout << "Skip calling interfaceChain on " << declared_instance.string()
+ << " because it can't be retrieved directly." << endl;
+ continue;
}
- };
- ForEachHalInstance(GetParam(), is_binderized);
+ // For example, given the following interfaceChain when
+ // hal_service is "android.hardware.mapper@2.0::IMapper/default":
+ // ["vendor.foo.mapper@1.0::IMapper",
+ // "android.hardware.mapper@2.1::IMapper",
+ // "android.hardware.mapper@2.0::IMapper",
+ // "android.hidl.base@1.0::IBase"],
+ // Allow the following:
+ // ["vendor.foo.mapper@1.0::IMapper/default",
+ // "android.hardware.mapper@2.1::IMapper/default",
+ // "android.hardware.mapper@2.0::IMapper/default"]
+ hal_service->interfaceChain([&](const auto &chain) {
+ vector<FqInstance> fq_instances;
+ std::transform(
+ chain.begin(), chain.end(), std::back_inserter(fq_instances),
+ [&](const auto &interface) {
+ return ToFqInstance(interface, declared_instance.getInstance());
+ });
+
+ bool allowing = false;
+ for (auto it = fq_instances.rbegin(); it != fq_instances.rend(); ++it) {
+ if (kPassthroughHals.find(it->getPackage()) != kPassthroughHals.end()) {
+ allowing = true;
+ }
+ if (allowing) {
+ cout << it->string() << " is allowed to be passthrough" << endl;
+ passthrough_allowed.insert(*it);
+ }
+ }
+ });
+ }
+
+ set<FqInstance> passthrough_not_allowed;
+ std::set_difference(
+ passthrough_declared.begin(), passthrough_declared.end(),
+ passthrough_allowed.begin(), passthrough_allowed.end(),
+ std::inserter(passthrough_not_allowed, passthrough_not_allowed.begin()));
+
+ EXPECT_TRUE(passthrough_not_allowed.empty())
+ << "The following HALs can't be passthrough under Treble rules: ["
+ << InstancesToString(passthrough_not_allowed) << "].";
}
// Tests that all HALs specified in the VINTF are available through service
diff --git a/treble/vintf/utils.h b/treble/vintf/utils.h
index 2ad580e..024ca22 100644
--- a/treble/vintf/utils.h
+++ b/treble/vintf/utils.h
@@ -56,6 +56,7 @@
using std::cout;
using std::endl;
using std::map;
+using std::multimap;
using std::set;
using std::string;