C++程序  |  305行  |  11.57 KB

/*
 * 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 <hidl-util/FqInstance.h>
#include <hidl/HidlTransportUtils.h>
#include <vintf/parse_string.h>

#include "utils.h"

namespace android {
namespace vintf {
namespace testing {

using android::FqInstance;
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 GetShippingApiLevel() <= 27 && !IsGoogleDefinedIface(fq_name);
}

void FailureHalMissing(const FQName &fq_name) {
  if (LegacyAndExempt(fq_name)) {
    cout << "[  WARNING ] " << fq_name.string()
         << " not available but is exempted because it is legacy. It is still "
            "recommended to fix this."
         << endl;
  } else {
    ADD_FAILURE() << fq_name.string() << " 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 {
    ADD_FAILURE()
        << fq_name.string()
        << " has an empty hash. This is because it was compiled "
           "without being frozen in a corresponding current.txt file.";
  }
}

// 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();

    EXPECT_NE(transport, Transport::EMPTY)
        << hal_name << " has no transport specified in VINTF.";

    if (transport == Transport::PASSTHROUGH) {
      EXPECT_NE(kPassthroughHals.find(hal_name), kPassthroughHals.end())
          << hal_name << " can't be passthrough under Treble rules.";
    }
  };

  ForEachHalInstance(GetParam(), is_binderized);
}

// Tests that all HALs specified in the VINTF are available through service
// manager.
// This tests (HAL in manifest) => (HAL is served)
TEST_P(SingleManifestTest, HalsAreServed) {
  // Returns a function that verifies that HAL is available through service
  // manager and is served from a specific set of partitions.
  auto is_available_from = [this](Partition expected_partition) -> HalVerifyFn {
    return [this, expected_partition](const FQName &fq_name,
                                      const string &instance_name,
                                      Transport 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 (!IsGoogleDefinedIface(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 = GetHalService(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 = GetHalService(fq_name, instance_name, transport);
      }

      if (hal_service == nullptr) {
        FailureHalMissing(fq_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() << " is in partition " << partition
          << " but is expected to be in " << expected_partition;
    };
  };

  auto manifest = GetParam();
  ForEachHalInstance(manifest,
                     is_available_from(PartitionOfType(manifest->type())));
}

// Tests that all HALs which are served are specified in the VINTF
// This tests (HAL is served) => (HAL in manifest)
TEST_P(SingleManifestTest, ServedHwbinderHalsAreInManifest) {
  auto manifest = GetParam();
  auto expected_partition = PartitionOfType(manifest->type());
  std::set<std::string> manifest_hwbinder_hals_ = GetHwbinderHals(manifest);

  Return<void> ret = default_manager_->list([&](const auto &list) {
    for (const auto &name : list) {
      if (std::string(name).find(IBase::descriptor) == 0) continue;

      FqInstance fqInstanceName;
      EXPECT_TRUE(fqInstanceName.setTo(name));

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

      Partition partition = GetPartition(service);
      if (partition == Partition::UNKNOWN) {
        // Caught by SystemVendorTest.ServedHwbinderHalsAreInManifest
        // if that test is run.
        return;
      }
      if (partition == expected_partition) {
        EXPECT_NE(manifest_hwbinder_hals_.find(name),
                  manifest_hwbinder_hals_.end())
            << name << " is being served, but it is not in a manifest.";
      }
    }
  });
  EXPECT_TRUE(ret.isOk());
}

TEST_P(SingleManifestTest, ServedPassthroughHalsAreInManifest) {
  auto manifest = GetParam();
  std::set<std::string> manifest_passthrough_hals_ =
      GetPassthroughHals(manifest);

  auto passthrough_interfaces_declared = [&manifest_passthrough_hals_](
                                             const FQName &fq_name,
                                             const string &instance_name,
                                             Transport transport) {
    if (transport != Transport::PASSTHROUGH) return;

    // See HalsAreServed. 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 (!IsGoogleDefinedIface(fq_name)) return;

    const FQName lowest_name =
        fq_name.withVersion(fq_name.getPackageMajorVersion(), 0);
    sp<IBase> hal_service =
        GetHalService(lowest_name, instance_name, transport);
    if (hal_service == nullptr) {
      ADD_FAILURE() << "Could not get service " << fq_name.string() << "/"
                    << instance_name;
      return;
    }

    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());
  };
  ForEachHalInstance(manifest, passthrough_interfaces_declared);
}

// Tests that HAL interfaces are officially released.
TEST_P(SingleManifestTest, InterfacesAreReleased) {
  // Verifies that HAL are released by fetching the hash of the interface and
  // comparing it to the set of known hashes of released interfaces.
  HalVerifyFn is_released = [](const FQName &fq_name,
                               const string &instance_name,
                               Transport transport) {
    // See HalsAreServed. 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 &&
        (!IsGoogleDefinedIface(fq_name) ||
         fq_name.getPackageMinorVersion() != 0)) {
      return;
    }

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

    if (hal_service == nullptr) {
      FailureHalMissing(fq_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_array : chain) {
            vector<uint8_t> hash{hash_array.data(),
                                 hash_array.data() + hash_array.size()};
            hash_chain.push_back(Hash::hexString(hash));
          }
        });

    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 == Hash::hexString(Hash::kEmptyHash)) {
        FailureHashMissing(fq_iface_name);
      }

      if (IsGoogleDefinedIface(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;
      }
    }
  };

  ForEachHalInstance(GetParam(), is_released);
}

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