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;