| /* |
| * 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 && !IsAndroidPlatformInterface(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 (!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 = 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 (!IsAndroidPlatformInterface(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 && |
| (!IsAndroidPlatformInterface(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 (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; |
| } |
| } |
| }; |
| |
| ForEachHalInstance(GetParam(), is_released); |
| } |
| |
| } // namespace testing |
| } // namespace vintf |
| } // namespace android |