/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "SingleManifestTest.h"

#include <aidl/metadata.h>
#include <android-base/file.h>
#include <android-base/hex.h>
#include <android-base/properties.h>
#include <android-base/strings.h>
#include <android/apex/ApexInfo.h>
#include <android/apex/IApexService.h>
#include <android/vintf/IServiceInfoFetcher.h>
#include <android/vintf/ServiceInfo.h>
#include <binder/IServiceManager.h>
#include <binder/Parcel.h>
#include <binder/RpcSession.h>
#include <binder/Stability.h>
#include <binder/Status.h>
#include <dirent.h>
#include <dlfcn.h>
#include <gmock/gmock.h>
#include <hidl-util/FqInstance.h>
#include <hidl/HidlTransportUtils.h>
#include <linux/vm_sockets.h>
#include <stdio.h>
#include <vintf/constants.h>
#include <vintf/parse_string.h>

#include <algorithm>

#include "utils.h"

using ::testing::AnyOf;
using ::testing::Contains;
using ::testing::StartsWith;

namespace android {
namespace vintf {
namespace testing {

namespace {

constexpr int kAndroidApi202404 = 202404;
constexpr int kAndroidApi202504 = 202504;
constexpr unsigned int kTrustyTestVmVintfTaPort = 10;

}  // namespace
using android::FqInstance;
using android::base::unique_fd;
using android::vintf::IServiceInfoFetcher;
using android::vintf::ServiceInfo;
using android::vintf::toFQNameString;

// For devices that launched <= Android O-MR1, systems/hals/implementations
// were delivered to companies which either don't start up on device boot.
bool LegacyAndExempt(const FQName &fq_name) {
  return GetVendorApiLevel() <= 27 && !IsAndroidPlatformInterface(fq_name);
}

void FailureHalMissing(const FQName &fq_name, const std::string &instance) {
  if (LegacyAndExempt(fq_name)) {
    cout << "[  WARNING ] " << fq_name.string() << "/" << instance
         << " not available but is exempted because it is legacy. It is still "
            "recommended to fix this."
         << endl;
  } else {
    ADD_FAILURE() << fq_name.string() << "/" << instance << " not available.";
  }
}

void FailureHashMissing(const FQName &fq_name) {
  if (LegacyAndExempt(fq_name)) {
    cout << "[  WARNING ] " << fq_name.string()
         << " has an empty hash but is exempted because it is legacy. It is "
            "still recommended to fix this. This is because it was compiled "
            "without being frozen in a corresponding current.txt file."
         << endl;
  } else if (base::GetProperty("ro.build.version.codename", "") != "REL") {
    cout << "[  WARNING ] " << fq_name.string()
         << " has an empty hash but is exempted because it is not a release "
            "build"
         << endl;
  } else {
    ADD_FAILURE()
        << fq_name.string()
        << " has an empty hash. This is because it was compiled "
           "without being frozen in a corresponding current.txt file.";
  }
}

static inline std::string ServiceName(const AidlInstance &aidl_instance) {
  return aidl_instance.package() + "." + aidl_instance.interface() + "/" +
         aidl_instance.instance();
}

static inline std::string ServiceName(const ServiceInfo &hal_info) {
  return hal_info.type + "/" + hal_info.instance;
}

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> GetPassthroughServiceExact(const FqInstance &fq_instance,
                                            bool expect_interface_chain_valid) {
  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::GetHidlService(
        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 && expect_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;
}

GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(SingleHwbinderHalTest);
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(SingleHidlTest);

// Given vendor.foo.bar@x.y::IFoo/default, also look up all declared passthrough
// HAL implementations on the device that implements this interface.
sp<IBase> SingleHidlTest::GetPassthroughService(const FqInstance &fq_instance) {
  sp<IBase> hal_service = GetPassthroughServiceExact(
      fq_instance, true /* expect_interface_chain_valid */);
  if (hal_service != nullptr) {
    return hal_service;
  }

  // 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. Attempt to find it.
  cout
      << "Can't find passthrough service " << fq_instance.string()
      << ". It might be a vendor extension. Searching all passthrough services "
         "on the device for a match."
      << endl;

  const auto &[_, manifest] = GetParam();
  auto all_declared_passthrough_instances = GetHidlInstances(manifest);
  for (const HidlInstance &other_hidl_instance :
       all_declared_passthrough_instances) {
    if (other_hidl_instance.transport() != Transport::PASSTHROUGH) {
      continue;
    }
    if (other_hidl_instance.instance_name() != fq_instance.getInstance()) {
      cout << "Skipping " << other_hidl_instance.fq_name().string() << "/"
           << other_hidl_instance.instance_name()
           << " because instance name is not " << fq_instance.getInstance();
      continue;
    }
    auto other_fq_instance = FqInstance::from(
        other_hidl_instance.fq_name(), other_hidl_instance.instance_name());
    if (!other_fq_instance) {
      cout << other_hidl_instance.fq_name().string() << "/"
           << other_hidl_instance.instance_name()
           << " is not a valid FqInstance, skipping." << endl;
      continue;
    }
    auto other_service = GetPassthroughServiceExact(
        *other_fq_instance, false /* expect_interface_chain_valid */);
    if (other_service == nullptr) {
      cout << "Cannot retrieve " << other_fq_instance->string() << ", skipping."
           << endl;
      continue;
    }
    bool match = false;
    auto other_interface_chain_ret =
        other_service->interfaceChain([&](const auto &chain) {
          for (const auto &intf : chain) {
            auto other_fq_instance_in_chain = FqInstance::from(
                std::string(intf) + "/" + other_fq_instance->getInstance());
            if (other_fq_instance_in_chain == fq_instance) {
              match = true;
              break;
            }
          }
        });
    if (!other_interface_chain_ret.isOk()) {
      cout << "Cannot call interfaceChain on " << other_fq_instance->string()
           << ", skipping." << endl;
      continue;
    }
    if (match) {
      cout << "The implementation of " << other_fq_instance->string()
           << " also implements " << fq_instance.string()
           << ", using it to check if passthrough is allowed for "
           << fq_instance.string() << endl;
      return other_service;
    }
  }
  cout << "Can't find any other passthrough service implementing "
       << fq_instance.string() << endl;
  return nullptr;
}

// returns true only if the specified apex is updated
static bool IsApexUpdated(const std::string &apex_name) {
  using namespace ::android::apex;
  auto binder =
      defaultServiceManager()->waitForService(String16("apexservice"));
  if (binder != nullptr) {
    auto apex_service = interface_cast<IApexService>(binder);
    std::vector<ApexInfo> list;
    auto status = apex_service->getActivePackages(&list);
    EXPECT_TRUE(status.isOk())
        << "Failed to getActivePackages():" << status.exceptionMessage();
    for (const ApexInfo &apex_info : list) {
      if (apex_info.moduleName == apex_name) {
        return !apex_info.isFactory;
      }
    }
  }
  return false;
}

// Tests that no HAL outside of the allowed set is specified as passthrough in
// VINTF.
// @VsrTest = VSR-3.2-014
TEST_P(SingleHidlTest, HalIsBinderized) {
  const auto &[hidl_instance, manifest] = GetParam();
  const FQName &fq_name = hidl_instance.fq_name();
  auto opt_fq_instance =
      FqInstance::from(fq_name, hidl_instance.instance_name());
  ASSERT_TRUE(opt_fq_instance);
  const FqInstance &fq_instance = *opt_fq_instance;

  EXPECT_THAT(hidl_instance.transport(),
              AnyOf(Transport::HWBINDER, Transport::PASSTHROUGH))
      << "HIDL HAL has unknown transport specified in VINTF ("
      << hidl_instance.transport() << ": " << fq_instance.string();

  if (hidl_instance.transport() == Transport::HWBINDER) {
    return;
  }

  set<FqInstance> passthrough_allowed;
  auto hal_service = GetPassthroughService(fq_instance);
  if (hal_service == nullptr) {
    cout << "Skip calling interfaceChain on " << fq_instance.string()
         << " because it can't be retrieved directly." << endl;
  } else {
    // 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, fq_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);
        }
      }
    });
  }

  EXPECT_THAT(passthrough_allowed, Contains(fq_instance))
      << "HIDL HAL can't be passthrough under Treble rules (or they "
         "can't be retrieved): "
      << fq_instance.string();
}

// Tests that all HALs specified in the VINTF are available through service
// manager.
// This tests (HAL in manifest) => (HAL is served)
// @VsrTest = VSR-3.2-014
TEST_P(SingleHidlTest, HalIsServed) {
  // Verifies that HAL is available through service manager and is served from a
  // specific set of partitions.

  const auto &[hidl_instance, manifest] = GetParam();

  Partition expected_partition = PartitionOfType(manifest->type());
  const FQName &fq_name = hidl_instance.fq_name();
  const string &instance_name = hidl_instance.instance_name();
  Transport transport = hidl_instance.transport();

  sp<IBase> hal_service;

  if (transport == Transport::PASSTHROUGH) {
    using android::hardware::details::canCastInterface;

    // Passthrough services all start with minor version 0.
    // there are only three of them listed above. They are looked
    // up based on their binary location. For instance,
    // V1_0::IFoo::getService() might correspond to looking up
    // android.hardware.foo@1.0-impl for the symbol
    // HIDL_FETCH_IFoo. For @1.1::IFoo to continue to work with
    // 1.0 clients, it must also be present in a library that is
    // called the 1.0 name. Clients can say:
    //     mFoo1_0 = V1_0::IFoo::getService();
    //     mFoo1_1 = V1_1::IFoo::castFrom(mFoo1_0);
    // This is the standard pattern for making a service work
    // for both versions (mFoo1_1 != nullptr => you have 1.1)
    // and a 1.0 client still works with the 1.1 interface.

    if (!IsAndroidPlatformInterface(fq_name)) {
      // This isn't the case for extensions of core Google interfaces.
      return;
    }

    const FQName lowest_name =
        fq_name.withVersion(fq_name.getPackageMajorVersion(), 0);
    hal_service = GetHidlService(lowest_name, instance_name, transport);
    EXPECT_TRUE(canCastInterface(hal_service.get(), fq_name.string().c_str()))
        << fq_name.string() << " is not on the device.";
  } else {
    hal_service = GetHidlService(fq_name, instance_name, transport);
  }

  if (hal_service == nullptr) {
    FailureHalMissing(fq_name, instance_name);
    return;
  }

  EXPECT_EQ(transport == Transport::HWBINDER, hal_service->isRemote())
      << "transport is " << transport << "but HAL service is "
      << (hal_service->isRemote() ? "" : "not") << " remote.";
  EXPECT_EQ(transport == Transport::PASSTHROUGH, !hal_service->isRemote())
      << "transport is " << transport << "but HAL service is "
      << (hal_service->isRemote() ? "" : "not") << " remote.";

  if (!hal_service->isRemote()) return;

  Partition partition = GetPartition(hal_service);
  if (partition == Partition::UNKNOWN) return;
  EXPECT_EQ(expected_partition, partition)
      << fq_name.string() << "/" << instance_name << " is in partition "
      << partition << " but is expected to be in " << expected_partition;
}

// Tests that all HALs which are served are specified in the VINTF
// This tests (HAL is served) => (HAL in manifest)
// @VsrTest = VSR-3.2-014
TEST_P(SingleHwbinderHalTest, ServedHwbinderHalIsInManifest) {
  const auto &[fq_instance_name, manifest] = GetParam();

  if (fq_instance_name.find(IBase::descriptor) == 0) {
    GTEST_SKIP() << "Ignore IBase: " << fq_instance_name;
  }

  auto expected_partition = PartitionOfType(manifest->type());
  std::set<std::string> manifest_hwbinder_hals =
      GetDeclaredHidlHalsOfTransport(manifest, Transport::HWBINDER);

  auto opt_fq_instance = FqInstance::from(fq_instance_name);
  ASSERT_TRUE(opt_fq_instance);
  const FqInstance &fq_instance = *opt_fq_instance;

  auto service = GetHidlService(
      toFQNameString(fq_instance.getPackage(), fq_instance.getVersion(),
                     fq_instance.getInterface()),
      fq_instance.getInstance(), Transport::HWBINDER);
  ASSERT_NE(service, nullptr);

  Partition partition = GetPartition(service);
  if (partition == Partition::UNKNOWN) {
    // Caught by SystemVendorTest.ServedHwbinderHalIsInManifest
    // if that test is run.
    GTEST_SKIP() << "Unable to determine partition. "
                    "Refer to SystemVendorTest.ServedHwbinderHalIsInManifest "
                    "or SingleHwbinderHalTest.ServedHwbinderHalIsInManifest "
                    "for the other manifest for correct result: "
                 << fq_instance_name;
  }
  if (partition != expected_partition) {
    GTEST_SKIP() << "Skipping because this test only test "
                 << expected_partition << " partition on the "
                 << manifest->type()
                 << " side of Treble boundary. "
                    "Refer to SystemVendorTest.ServedHwbinderHalIsInManifest "
                    "or SingleHwbinderHalTest.ServedHwbinderHalIsInManifest "
                    "for the other manifest for correct result: "
                 << fq_instance_name;
  }
  EXPECT_NE(manifest_hwbinder_hals.find(fq_instance_name),
            manifest_hwbinder_hals.end())
      << fq_instance_name << " is being served, but it is not in a manifest.";
}

std::string SingleHwbinderHalTest::GetTestCaseSuffix(
    const ::testing::TestParamInfo<ParamType> &info) {
  const auto &[fq_instance_name, manifest] = info.param;
  return SanitizeTestCaseName(fq_instance_name) + "_" +
         std::to_string(info.index);
}

// Tests that all HALs which are served are specified in the VINTF
// This tests (HAL is served) => (HAL in manifest) for passthrough HALs
// @VsrTest = VSR-3.2-014
TEST_P(SingleHidlTest, ServedPassthroughHalIsInManifest) {
  const auto &[hidl_instance, manifest] = GetParam();
  const FQName &fq_name = hidl_instance.fq_name();
  const string &instance_name = hidl_instance.instance_name();
  Transport transport = hidl_instance.transport();
  std::set<std::string> manifest_passthrough_hals =
      GetDeclaredHidlHalsOfTransport(manifest, Transport::PASSTHROUGH);

  if (transport != Transport::PASSTHROUGH) {
    GTEST_SKIP() << "Not passthrough: " << fq_name.string() << "/"
                 << instance_name;
  }

  // See HalIsServed. These are always retrieved through the base interface
  // and if it is not a google defined interface, it must be an extension of
  // one.
  if (!IsAndroidPlatformInterface(fq_name)) {
    GTEST_SKIP() << "Not Android Platform Interface: " << fq_name.string()
                 << "/" << instance_name;
  }

  const FQName lowest_name =
      fq_name.withVersion(fq_name.getPackageMajorVersion(), 0);
  sp<IBase> hal_service = GetHidlService(lowest_name, instance_name, transport);
  ASSERT_NE(nullptr, hal_service)
      << "Could not get service " << fq_name.string() << "/" << instance_name;

  Return<void> ret = hal_service->interfaceChain(
      [&manifest_passthrough_hals, &instance_name](const auto &interfaces) {
        for (const auto &interface : interfaces) {
          if (std::string(interface) == IBase::descriptor) continue;

          const std::string instance =
              std::string(interface) + "/" + instance_name;
          EXPECT_NE(manifest_passthrough_hals.find(instance),
                    manifest_passthrough_hals.end())
              << "Instance missing from manifest: " << instance;
        }
      });
  EXPECT_TRUE(ret.isOk());
}

// Tests that HAL interfaces are officially released.
// @VsrTest = VSR-3.2-014
TEST_P(SingleHidlTest, InterfaceIsReleased) {
  const auto &[hidl_instance, manifest] = GetParam();

  const FQName &fq_name = hidl_instance.fq_name();
  const string &instance_name = hidl_instance.instance_name();
  Transport transport = hidl_instance.transport();

  // See HalIsServed. These are always retrieved through the base interface
  // and if it is not a google defined interface, it must be an extension of
  // one.
  if (transport == Transport::PASSTHROUGH &&
      (!IsAndroidPlatformInterface(fq_name) ||
       fq_name.getPackageMinorVersion() != 0)) {
    return;
  }

  sp<IBase> hal_service = GetHidlService(fq_name, instance_name, transport);

  if (hal_service == nullptr) {
    FailureHalMissing(fq_name, instance_name);
    return;
  }

  vector<string> iface_chain = GetInterfaceChain(hal_service);

  vector<string> hash_chain{};
  hal_service->getHashChain([&hash_chain](
                                const hidl_vec<HashCharArray> &chain) {
    for (const HashCharArray &hash : chain) {
      hash_chain.push_back(android::base::HexString(hash.data(), hash.size()));
    }
  });

  ASSERT_EQ(iface_chain.size(), hash_chain.size());
  for (size_t i = 0; i < iface_chain.size(); ++i) {
    FQName fq_iface_name;
    if (!FQName::parse(iface_chain[i], &fq_iface_name)) {
      ADD_FAILURE() << "Could not parse iface name " << iface_chain[i]
                    << " from interface chain of " << fq_name.string();
      return;
    }
    string hash = hash_chain[i];
    if (hash == android::base::HexString(Hash::kEmptyHash.data(),
                                         Hash::kEmptyHash.size())) {
      FailureHashMissing(fq_iface_name);
    } else if (IsAndroidPlatformInterface(fq_iface_name)) {
      set<string> released_hashes = ReleasedHashes(fq_iface_name);
      EXPECT_NE(released_hashes.find(hash), released_hashes.end())
          << "Hash not found. This interface was not released." << endl
          << "Interface name: " << fq_iface_name.string() << endl
          << "Hash: " << hash << endl;
    }
  }
}

static std::optional<AidlInterfaceMetadata> metadataForInterface(
    const std::string &name) {
  for (const auto &module : AidlInterfaceMetadata::all()) {
    if (std::find(module.types.begin(), module.types.end(), name) !=
        module.types.end()) {
      return module;
    }
  }
  return std::nullopt;
}

#ifdef TRUSTED_HAL_TEST
sp<IServiceInfoFetcher> GetTrustedHalInfoFetcher() {
  int test_vm_cid = base::GetIntProperty("trusty.test_vm.vm_cid", -1);
  if (test_vm_cid == -1) {
    cout << "no test VM is running";
    return nullptr;
  }

  auto session = RpcSession::make();
  auto request = [=] {
    int s = socket(AF_VSOCK, SOCK_STREAM, 0);
    if (s < 0) {
      cout << "failed to get vsock; errno:" << errno;
      return unique_fd{};
    }
    struct timeval connect_timeout = {.tv_sec = 60, .tv_usec = 0};
    int res = setsockopt(s, AF_VSOCK, SO_VM_SOCKETS_CONNECT_TIMEOUT,
                         &connect_timeout, sizeof(connect_timeout));
    if (res) {
      cout << "failed to set timeout; errno:" << errno;
    }
    struct sockaddr_vm addr = {
        .svm_family = AF_VSOCK,
        .svm_port = kTrustyTestVmVintfTaPort,
        .svm_cid = static_cast<unsigned int>(test_vm_cid),
    };
    res =
        TEMP_FAILURE_RETRY(connect(s, (struct sockaddr *)&addr, sizeof(addr)));
    if (res != 0) {
      cout << "failed to connect to VM. Error code:" << res;
      return unique_fd{};
    } else {
      cout << "vsock connection successful\n";
      return unique_fd(s);
    }
  };
  auto status = session->setupPreconnectedClient(unique_fd{}, request);
  if (status != android::OK) {
    cout << "unable to set up vsock client";
    return nullptr;
  }
  sp<IBinder> root = session->getRootObject();
  if (root == nullptr) {
    cout << "failed to get root object for IServiceInfoFetcher";
    return nullptr;
  }
  return IServiceInfoFetcher::asInterface(root);
}

TEST(TrustedHalDeclaredTest, InstalledTrustedHalsAreDeclared) {
  auto trusted_hal_info_fetcher = GetTrustedHalInfoFetcher();
  ASSERT_NE(trusted_hal_info_fetcher, nullptr)
      << "failed to get IServiceInfoFetcher";

  // Retrieve the list of actually installed Trusted HALs.
  std::vector<std::string> actual_trusted_hal_list;
  ASSERT_TRUE(
      trusted_hal_info_fetcher->listAllServices(&actual_trusted_hal_list)
          .isOk())
      << "failed to list all services";
  std::set<std::string> actual_instances(actual_trusted_hal_list.begin(),
                                         actual_trusted_hal_list.end());
  for (const auto &aidl_instance : VtsTrebleVintfTestBase::GetAidlInstances(
           VintfObject::GetDeviceHalManifest())) {
    auto name = ServiceName(aidl_instance);
    if (aidl_instance.exclusiveTo() == ExclusiveTo::VM) {
      ASSERT_TRUE(actual_instances.contains(name))
          << "Declared Trusted HAL instance (exclusive to VM) is not "
             "installed: "
          << name;
      actual_instances.erase(name);
    } else if (aidl_instance.exclusiveTo() == ExclusiveTo::EMPTY) {
      actual_instances.erase(name);
    }
  }
  ASSERT_TRUE(actual_instances.empty())
      << "Installed Trusted HAL instances are not declared: "
      << actual_instances.size();
}

#else   // TRUSTED_HAL_TEST
// TODO(b/150155678): using standard code to do this
static int32_t getInterfaceVersion(const sp<IBinder> &binder) {
  Parcel data;
  Parcel reply;
  const auto &descriptor = binder->getInterfaceDescriptor();
  data.writeInterfaceToken(descriptor);
  status_t err = binder->transact(IBinder::LAST_CALL_TRANSACTION, data, &reply);
  // On upgrading devices, the HAL may not implement this transaction. libvintf
  // treats missing <version> as version 1, so we do the same here.
  if (err == UNKNOWN_TRANSACTION) {
    std::cout << "INFO: " << descriptor
              << " does not have an interface version, using default value "
              << android::vintf::kDefaultAidlMinorVersion << std::endl;
    return android::vintf::kDefaultAidlMinorVersion;
  }
  EXPECT_EQ(OK, err);
  binder::Status status;
  EXPECT_EQ(OK, status.readFromParcel(reply));
  EXPECT_TRUE(status.isOk()) << status.toString8().c_str();
  auto version = reply.readInt32();
  return version;
}

// TODO(b/150155678): using standard code to do this
static std::string getInterfaceHash(const sp<IBinder> &binder) {
  Parcel data;
  Parcel reply;
  data.writeInterfaceToken(binder->getInterfaceDescriptor());
  status_t err =
      binder->transact(IBinder::LAST_CALL_TRANSACTION - 1, data, &reply, 0);
  if (err == UNKNOWN_TRANSACTION) {
    return "";
  }
  EXPECT_EQ(OK, err);
  binder::Status status;
  EXPECT_EQ(OK, status.readFromParcel(reply));
  EXPECT_TRUE(status.isOk()) << status.toString8().c_str();
  std::string str;
  EXPECT_EQ(OK, reply.readUtf8FromUtf16(&str));
  return str;
}

std::vector<ServiceInfo> getExtensionInfos(const sp<IBinder> &binder) {
  // if you end up here because of a stack overflow when running this
  // test... you have a cycle in your interface extensions. Break that
  // cycle to continue.
  std::vector<ServiceInfo> extensions = {};
  sp<IBinder> parent = binder;
  sp<IBinder> extension;
  while (parent) {
    status_t status = parent->getExtension(&extension);
    if (status != OK || !extension) {
      break;
    }
    ServiceInfo info;
    info.type =
        std::string(String8(extension->getInterfaceDescriptor()).c_str());
    info.requireVintfDeclaration =
        android::internal::Stability::requiresVintfDeclaration(extension);
    info.hash = getInterfaceHash(parent);
    extensions.push_back(info);
    parent = extension;
  }
  return extensions;
}
#endif  // TRUSTED_HAL_TEST

static bool CheckAidlVersionMatchesDeclared(
    const AidlInstance &declared_instance, const ServiceInfo &actual_hal_info) {
  const auto name = ServiceName(actual_hal_info);
  const auto actual_version = actual_hal_info.version;
  if (actual_version < 1) {
    ADD_FAILURE() << "For " << name << ", version should be >= 1 but it is "
                  << actual_version << ".";
    return false;
  }

  uint64_t declared_version = declared_instance.version();
  if (declared_version == actual_version) {
    std::cout << "For " << name << ", version " << actual_version
              << " matches declared value." << std::endl;
    return true;
  }

  const optional<string> &updatable_via_apex =
      declared_instance.updatable_via_apex();
  // allow upgrade if updatable HAL's declared APEX is actually updated.
  // or if the HAL is updatable via system.
  const bool allow_upgrade = (updatable_via_apex.has_value() &&
                              IsApexUpdated(updatable_via_apex.value())) ||
                             declared_instance.updatable_via_system();
  if (allow_upgrade && actual_version > declared_version) {
    std::cout << "For " << name << ", upgraded version " << actual_version
              << " is okay. (declared value = " << declared_version << ".)"
              << std::endl;
    return true;
  }

  // Android R VINTF did not support AIDL version in the manifest.
  Level shipping_fcm_version = VintfObject::GetDeviceHalManifest()->level();
  if (shipping_fcm_version != Level::UNSPECIFIED &&
      shipping_fcm_version <= Level::R) {
    std::cout << "For " << name << ", manifest declares version "
              << declared_version << ", but the actual version is "
              << actual_version << ". Exempted for shipping FCM version "
              << shipping_fcm_version << ". (b/178458001, b/199190514)"
              << std::endl;
    return true;
  }

  ADD_FAILURE()
      << "For " << name << ", manifest (targeting FCM:" << shipping_fcm_version
      << ") declares version " << declared_version
      << ", but the actual version is " << actual_version << std::endl
      << "Either the VINTF manifest <hal> entry needs to be updated with a "
         "version tag for the actual version, or the implementation should be "
         "changed to use the declared version";
  return false;
}

static inline void checkHash(
    const ServiceInfo &hal_info, bool ignore_rel,
    const std::optional<const std::string> &parent_interface) {
  const std::string &interface = hal_info.type;
  const std::string &hash = hal_info.hash;
  const std::optional<AidlInterfaceMetadata> metadata =
      metadataForInterface(interface);
  const std::string parent_log =
      parent_interface
          ? "\nThis interface is set as an extension via setExtension on " +
                *parent_interface
          : "";

  const bool is_aosp = base::StartsWith(interface, "android.");
  ASSERT_TRUE(!is_aosp || metadata)
      << "AOSP interface must have metadata: "
      << interface << ". Do not use the "
      << "'android.' prefix for non-AOSP HALs" << parent_log;
  const bool is_release =
      base::GetProperty("ro.build.version.codename", "") == "REL";

  const std::vector<std::string> hashes =
      metadata ? metadata->hashes : std::vector<std::string>();
  const bool found_hash =
      std::find(hashes.begin(), hashes.end(), hash) != hashes.end();

  if (is_aosp) {
    if (!found_hash) {
      if (is_release || ignore_rel) {
        ADD_FAILURE() << "Interface "
                      << interface << " has an unrecognized hash: '" << hash
                      << "'. The following hashes are known:\n"
                      << base::Join(hashes, '\n')
                      << "\nHAL interfaces must be released and unchanged."
                      << parent_log;
      } else {
        std::cout << "INFO: using unfrozen hash '" << hash << "' for "
                  << interface << ". This will become an error upon release."
                  << parent_log << std::endl;
      }
    }
  } else {
    // is partner-owned
    //
    // we only require that these are frozen, but we cannot check them for
    // accuracy
    if (hash.empty() ||
        (GetVendorApiLevel() >= kAndroidApi202504 && hash == "notfrozen")) {
      if (is_release) {
        ADD_FAILURE()
            << "Interface "
            << interface << " is used but not frozen (cannot find hash for it)."
            << parent_log;
      } else {
        std::cout << "INFO: missing hash for "
                  << interface << ". This will become an error upon release."
                  << parent_log << std::endl;
      }
    }
  }
}

// This checks to make sure all vintf extensions are frozen.
// We do not check for known hashes because the Android framework does not
// support these extensions without out-of-tree changes from partners.
// @param binder - the parent binder to check all of its extensions
void checkVintfExtensionInterfaces(const ServiceInfo &info) {
  // if you end up here because of a stack overflow when running this
  // test... you have a cycle in your interface extensions. Break that
  // cycle to continue.
  for (const auto &extension : info.extensions) {
    if (extension.requireVintfDeclaration) {
      checkHash(extension, false, info.type);
    }
    checkVintfExtensionInterfaces(extension);
  }
}

// This checks if @updatable-via-apex in VINTF is correct.
void checkVintfUpdatableViaApex(const std::string &exe,
                                const std::string &apex_name) {
  // HAL service should start from the apex
  ASSERT_THAT(exe, StartsWith("/apex/" + apex_name + "/"));
}

#ifndef TRUSTED_HAL_TEST
static std::vector<std::string> halsUpdatableViaSystem() {
  std::vector<std::string> hals = {};
  // The KeyMint HALs connecting to the Trusty VM in the system image are
  // supposed to be enabled in vendor init when the system property
  // |trusty.security_vm.keymint.enabled| is set to true in W.
  if (base::GetBoolProperty("trusty.security_vm.keymint.enabled", false)) {
    hals.push_back("android.hardware.security.keymint.IKeyMintDevice/default");
    hals.push_back(
        "android.hardware.security.keymint.IRemotelyProvisionedComponent/"
        "default");
    hals.push_back(
        "android.hardware.security.sharedsecret.ISharedSecret/default");
    hals.push_back(
        "android.hardware.security.secureclock.ISecureClock/default");
  }
  return hals;
}

TEST_P(SingleAidlTest, ExpectedUpdatableViaSystemHals) {
  const auto &[aidl_instance, _] = GetParam();
  const std::string name = ServiceName(aidl_instance);

  const auto hals = halsUpdatableViaSystem();
  if (std::find(hals.begin(), hals.end(), name) != hals.end()) {
    ASSERT_TRUE(aidl_instance.updatable_via_system())
        << "HAL " << name << " has system dependency but not declared with "
        << "updatable-via-system in the VINTF manifest.";
  } else {
    ASSERT_FALSE(aidl_instance.updatable_via_system())
        << "HAL " << name << " is declared with updatable-via-system in the "
        << "VINTF manifest but it does not have system dependency.";
  }
}
#endif  // TRUSTED_HAL_TEST

// An AIDL HAL with VINTF stability can only be registered if it is in the
// manifest. However, we still must manually check that every declared HAL is
// actually present on the device.
// @VsrTest = VSR-3.2-014
TEST_P(SingleAidlTest, HalIsServed) {
  const auto &[aidl_instance, manifest] = GetParam();
  const string &package = aidl_instance.package();
  uint64_t version = aidl_instance.version();
  const string &interface = aidl_instance.interface();
  const string &instance = aidl_instance.instance();
  const optional<string> &updatable_via_apex =
      aidl_instance.updatable_via_apex();

  const std::string type = package + "." + interface;
  const std::string name = type + "/" + instance;

  ServiceInfo actual_hal_info;
  Partition actual_partition;

#ifdef TRUSTED_HAL_TEST
  if (aidl_instance.exclusiveTo() != ExclusiveTo::VM) {
    GTEST_SKIP() << name
                 << " is not check in this test as it is not exclusive to VM";
  }
  auto trusted_hal_info_fetcher = GetTrustedHalInfoFetcher();
  ASSERT_NE(trusted_hal_info_fetcher, nullptr)
      << "failed to get IServiceInfoFetcher";

  ASSERT_TRUE(
      trusted_hal_info_fetcher->getServiceInfo(name, &actual_hal_info).isOk())
      << "failed to get service info for HAL exclusive to VM: " << name;

  actual_partition = Partition::VENDOR;
#else   // TRUSTED_HAL_TEST
  if (aidl_instance.exclusiveTo() == ExclusiveTo::VM) {
    GTEST_SKIP() << name
                 << " is not check in this test as it is exclusive to VM";
  }
  sp<IBinder> binder = GetAidlService(name);
  ASSERT_NE(binder, nullptr) << "Failed to get " << name;

  actual_hal_info.type = type;
  actual_hal_info.instance = instance;
  actual_hal_info.requireVintfDeclaration =
      android::internal::Stability::requiresVintfDeclaration(binder);
  actual_hal_info.version = getInterfaceVersion(binder);
  actual_hal_info.hash = getInterfaceHash(binder);

  pid_t pid;
  ASSERT_EQ(OK, binder->getDebugPid(&pid));
  actual_partition = PartitionOfProcess(pid);

  ASSERT_TRUE(base::Readlink("/proc/" + std::to_string(pid) + "/exe",
                             &actual_hal_info.exe));

  actual_hal_info.extensions = getExtensionInfos(binder);
#endif  // TRUSTED_HAL_TEST

  ASSERT_EQ(name, ServiceName(actual_hal_info));
  if (GetVendorApiLevel() >= kAndroidApi202404 &&
      !actual_hal_info.requireVintfDeclaration) {
    ADD_FAILURE() << "Interface " << name
                  << " is declared in the VINTF manifest "
                  << "but it does not have \"vintf\" stability. "
                  << "Add 'stability: \"vintf\" to the aidl_interface module, "
                  << "or remove it from the VINTF manifest.";
  }
  // If we know this version is frozen, even on non-REL builds we should throw
  // an error if this is an AOSP interfaces with a hash that we don't know
  // about.
  const bool reliable_version =
      CheckAidlVersionMatchesDeclared(aidl_instance, actual_hal_info);
  const std::optional<AidlInterfaceMetadata> metadata =
      metadataForInterface(type);
  const bool is_existing =
      metadata ? std::find(metadata->versions.begin(), metadata->versions.end(),
                           version) != metadata->versions.end()
               : false;
  const bool ignore_rel_for_aosp = reliable_version && is_existing;

  checkHash(actual_hal_info, ignore_rel_for_aosp, std::nullopt);

  if (GetVendorApiLevel() >= kAndroidApi202504) {
    checkVintfExtensionInterfaces(actual_hal_info);
  }

  // TODO(b/388106311): always be able to determine where this code comes from
  const bool ableToDeterminePartition = actual_partition != Partition::UNKNOWN;
  if (GetVendorApiLevel() >= kAndroidApi202504 && ableToDeterminePartition) {
    Partition expected_partition = PartitionOfType(manifest->type());
    EXPECT_EQ(expected_partition, actual_partition);
  }

  if (updatable_via_apex.has_value()) {
    checkVintfUpdatableViaApex(actual_hal_info.exe, updatable_via_apex.value());
  }
}

TEST_P(SingleAidlTest, NoExclusiveToVmHalExistIfTrustedVmDisabled) {
  const auto &[aidl_instance, _] = GetParam();
  const std::string name = ServiceName(aidl_instance);

  const bool trustyVmEnabled =
      base::GetBoolProperty("trusty.security_vm.enabled", false) ||
      base::GetBoolProperty("trusty.widevine_vm.enabled", false);
  if (!trustyVmEnabled) {
    ASSERT_NE(ExclusiveTo::VM, aidl_instance.exclusiveTo())
        << "HAL " << name << " is exclusive to VM but the device does not "
        << "support any Trusty VM.";
  }
}

// We don't want to add more same process HALs in Android. We have some 3rd
// party ones such as openGL and Vulkan. In the future, we should verify those
// here as well. However we want to strictly limit other HALs because a
// same-process HAL confuses the client and server SELinux permissions. In
// Android, we prefer upstream Linux support, then secondary to that, we prefer
// having hardware use in a process isolated from the Android framework.
struct NativePackage {
  std::string name;
  int32_t majorVersion;
};

ostream &operator<<(ostream &os, const NativePackage &pkg) {
  os << pkg.name << "-v" << pkg.majorVersion;
  return os;
}

static const std::array<NativePackage, 1> kKnownNativePackages = {
    NativePackage{"mapper", 5},
};
static const std::vector<std::string> kNativeHalPaths = {
    "/vendor/lib/hw/",
    "/vendor/lib64/hw/",
};

static std::optional<NativePackage> findKnownNativePackage(
    std::string_view package) {
  for (const auto &it : kKnownNativePackages) {
    if (it.name == package) {
      return it;
    }
  }
  return std::nullopt;
}

// using device manifest test for access to GetNativeInstances
TEST(NativeDeclaredTest, NativeDeclaredIfExists) {
  SKIP_TEST_IN_TRUSTED_HAL_VTS();
  std::set<std::string> names;  // e.g. 'mapper.instance_name'

  // read all the native HALs installed on disk
  bool found_a_dir = false;
  for (const std::string &dir : kNativeHalPaths) {
    DIR *dp = opendir(dir.c_str());
    if (dp == nullptr) continue;
    found_a_dir = true;

    dirent *entry;
    while ((entry = readdir(dp))) {
      std::string name = entry->d_name;
      size_t dot_one = name.find('.');
      if (dot_one == std::string::npos) continue;
      size_t dot_end = name.rfind('.');
      if (dot_end == std::string::npos || dot_one == dot_end) continue;
      ASSERT_LT(dot_one, dot_end);
      if (name.substr(dot_end) != ".so") continue;

      std::string package = name.substr(0, dot_one);
      if (!findKnownNativePackage(package).has_value()) continue;

      names.insert(name.substr(0, dot_end));
    }
    closedir(dp);
  }
  ASSERT_TRUE(found_a_dir);

  // ignore HALs which are declared, because they'll be checked in
  // SingleNativeTest ExistsIfDeclared
  for (const auto &hal : VtsTrebleVintfTestBase::GetNativeInstances(
           VintfObject::GetDeviceHalManifest())) {
    std::string this_name = hal.package() + "." + hal.instance();
    names.erase(this_name);
  }

  for (const std::string &name : names) {
    ADD_FAILURE() << name
                  << " is installed on the device, but it's not declared in "
                     "the VINTF manifest";
  }
}

GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(SingleNativeTest);
TEST_P(SingleNativeTest, ExistsIfDeclared) {
  const auto &[native_instance, manifest] = GetParam();

  // Currently only support rev'ing the major version
  EXPECT_EQ(native_instance.minor_version(), 0);

  auto knownPackageInfo = findKnownNativePackage(native_instance.package());
  ASSERT_TRUE(knownPackageInfo.has_value())
      << "Unsupported package: " << native_instance.package()
      << " must be one of: " << base::Join(kKnownNativePackages, ", ");
  EXPECT_EQ(native_instance.major_version(), knownPackageInfo->majorVersion);
  EXPECT_TRUE(native_instance.interface() == "I" ||
              native_instance.interface() == "")
      << "Interface must be 'I' or '' for native HAL: "
      << native_instance.interface();

  void *so = openDeclaredPassthroughHal(
      String16(native_instance.package().c_str()),
      String16(native_instance.instance().c_str()), RTLD_LAZY | RTLD_LOCAL);
  ASSERT_NE(so, nullptr) << "Failed to load " << native_instance << dlerror();

  std::string upperPackage = native_instance.package();
  std::transform(upperPackage.begin(), upperPackage.end(), upperPackage.begin(),
                 ::toupper);
  std::string versionSymbol = "ANDROID_HAL_" + upperPackage + "_VERSION";
  int32_t *halVersion = (int32_t *)dlsym(so, versionSymbol.c_str());
  ASSERT_NE(halVersion, nullptr) << "Failed to find symbol " << versionSymbol;
  EXPECT_EQ(native_instance.major_version(), *halVersion);

  dlclose(so);
}

}  // namespace testing
}  // namespace vintf
}  // namespace android
