| /* |
| * Copyright (C) 2017 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. |
| */ |
| #define LOG_TAG "vts_ibase_test" |
| |
| #include <algorithm> |
| #include <functional> |
| #include <map> |
| #include <string> |
| |
| #include <android-base/logging.h> |
| #include <android-base/properties.h> |
| #include <android-base/strings.h> |
| #include <android/hidl/base/1.0/IBase.h> |
| #include <android/hidl/manager/1.0/IServiceManager.h> |
| #include <gtest/gtest.h> |
| #include <hidl-util/FqInstance.h> |
| #include <hidl/HidlBinderSupport.h> |
| #include <hidl/ServiceManagement.h> |
| #include <init-test-utils/service_utils.h> |
| |
| using android::FqInstance; |
| using android::FQName; |
| using android::sp; |
| using android::wp; |
| using android::base::Result; |
| using android::hardware::hidl_array; |
| using android::hardware::hidl_death_recipient; |
| using android::hardware::hidl_handle; |
| using android::hardware::hidl_string; |
| using android::hardware::hidl_vec; |
| using android::hardware::IBinder; |
| using android::hardware::toBinder; |
| using android::hidl::base::V1_0::IBase; |
| using android::hidl::manager::V1_0::IServiceManager; |
| using android::init::ServiceInterfacesMap; |
| using PidInterfacesMap = std::map<pid_t, std::set<FqInstance>>; |
| |
| template <typename T> |
| static inline ::testing::AssertionResult isOk(const ::android::hardware::Return<T>& ret) { |
| return ret.isOk() ? (::testing::AssertionSuccess() << ret.description()) |
| : (::testing::AssertionFailure() << ret.description()); |
| } |
| #define ASSERT_OK(__ret__) ASSERT_TRUE(isOk(__ret__)) |
| #define EXPECT_OK(__ret__) EXPECT_TRUE(isOk(__ret__)) |
| |
| struct Hal { |
| sp<IBase> service; |
| std::string name; // space separated list of android.hidl.foo@1.0::IFoo/instance-name |
| }; |
| |
| std::string FqInstancesToString(const std::set<FqInstance>& instances) { |
| std::set<std::string> instance_strings; |
| for (const FqInstance& instance : instances) { |
| instance_strings.insert(instance.string()); |
| } |
| return android::base::Join(instance_strings, "\n"); |
| } |
| |
| pid_t GetServiceDebugPid(const std::string& service) { |
| return android::base::GetIntProperty("init.svc_debug_pid." + service, 0); |
| } |
| |
| template <typename T> |
| std::set<T> SetDifference(const std::set<T>& a, const std::set<T>& b) { |
| std::set<T> diff; |
| std::set_difference(a.begin(), a.end(), b.begin(), b.end(), std::inserter(diff, diff.begin())); |
| return diff; |
| } |
| |
| class VtsHalBaseV1_0TargetTest : public ::testing::Test { |
| public: |
| virtual void SetUp() override { |
| default_manager_ = ::android::hardware::defaultServiceManager(); |
| |
| ASSERT_NE(default_manager_, nullptr) |
| << "Failed to get default service manager." << std::endl; |
| |
| ASSERT_OK(default_manager_->list([&](const auto& list) { |
| for (const auto& name : list) { |
| const std::string strName = name; |
| auto loc = strName.find_first_of('/'); |
| if (loc == std::string::npos) { |
| ADD_FAILURE() << "Invalid FQName: " << strName; |
| continue; |
| } |
| const std::string fqName = strName.substr(0, loc); |
| const std::string instance = strName.substr(loc + 1); |
| |
| sp<IBase> service = default_manager_->get(fqName, instance); |
| if (service == nullptr) { |
| ADD_FAILURE() << "Null service for " << name << " " << fqName << " " |
| << instance; |
| continue; |
| } |
| |
| sp<IBinder> binder = toBinder(service); |
| if (binder == nullptr) { |
| ADD_FAILURE() << "Null binder for " << name; |
| continue; |
| } |
| |
| auto iter = all_hals_.find(binder); |
| if (iter != all_hals_.end()) { |
| // include all the names this is registered as for error messages |
| iter->second.name += " " + strName; |
| } else { |
| all_hals_.insert(iter, {binder, Hal{service, strName}}); |
| } |
| } |
| })); |
| |
| ASSERT_FALSE(all_hals_.empty()); // sanity |
| } |
| |
| void EachHal(const std::function<void(const Hal&)>& check) { |
| for (auto iter = all_hals_.begin(); iter != all_hals_.end(); ++iter) { |
| check(iter->second); |
| } |
| } |
| |
| PidInterfacesMap GetPidInterfacesMap() { |
| PidInterfacesMap result; |
| EXPECT_OK(default_manager_->debugDump([&result](const auto& list) { |
| for (const auto& debug_info : list) { |
| if (debug_info.pid != static_cast<int32_t>(IServiceManager::PidConstant::NO_PID)) { |
| FQName fqName; |
| ASSERT_TRUE(fqName.setTo(debug_info.interfaceName.c_str())) |
| << "Unable to parse interface: '" << debug_info.interfaceName.c_str(); |
| FqInstance fqInstance; |
| ASSERT_TRUE(fqInstance.setTo(fqName, debug_info.instanceName.c_str())); |
| if (fqInstance.getFqName() != android::gIBaseFqName) { |
| result[debug_info.pid].insert(fqInstance); |
| } |
| } |
| } |
| })); |
| return result; |
| } |
| |
| // default service manager |
| sp<IServiceManager> default_manager_; |
| |
| // map from underlying instance to actual instance |
| // |
| // this prevents calling the same service twice since the same service |
| // will get registered multiple times for its entire inheritance |
| // hierarchy (or perhaps as different instance names) |
| std::map<sp<IBinder>, Hal> all_hals_; |
| }; |
| |
| TEST_F(VtsHalBaseV1_0TargetTest, CanPing) { |
| EachHal( |
| [&](const Hal& base) { EXPECT_OK(base.service->ping()) << "Cannot ping " << base.name; }); |
| } |
| |
| TEST_F(VtsHalBaseV1_0TargetTest, InterfaceChain) { |
| EachHal([&](const Hal& base) { |
| EXPECT_OK(base.service->interfaceChain([&](const auto& interfaceChain) { |
| // must include IBase + subclasses |
| EXPECT_GT(interfaceChain.size(), 1u) << "Invalid instance name " << base.name; |
| })) << base.name; |
| }); |
| } |
| |
| TEST_F(VtsHalBaseV1_0TargetTest, Descriptor) { |
| EachHal([&](const Hal& base) { |
| EXPECT_OK(base.service->interfaceDescriptor([&](const auto& descriptor) { |
| // must include IBase + subclasses |
| EXPECT_GT(descriptor.size(), 0u) << base.name; |
| EXPECT_NE(IBase::descriptor, descriptor) << base.name; |
| })) << base.name; |
| }); |
| } |
| |
| TEST_F(VtsHalBaseV1_0TargetTest, Death) { |
| struct HidlDeathRecipient : hidl_death_recipient { |
| virtual void serviceDied(uint64_t /* cookie */, const wp<IBase>& /* who */){}; |
| }; |
| sp<hidl_death_recipient> recipient = new HidlDeathRecipient; |
| |
| EachHal([&](const Hal& base) { |
| EXPECT_OK(base.service->linkToDeath(recipient, 0 /* cookie */)) |
| << "Register death recipient " << base.name; |
| EXPECT_OK(base.service->unlinkToDeath(recipient)) << "Unlink death recipient " << base.name; |
| }); |
| } |
| |
| TEST_F(VtsHalBaseV1_0TargetTest, Debug) { |
| EachHal([&](const Hal& base) { |
| // normally one is passed, but this is tested by dumpstate |
| EXPECT_OK(base.service->debug(hidl_handle(), {})) |
| << "Handle empty debug handle " << base.name; |
| }); |
| } |
| |
| TEST_F(VtsHalBaseV1_0TargetTest, HashChain) { |
| EachHal([&](const Hal& base) { |
| EXPECT_OK(base.service->getHashChain([&](const auto& hashChain) { |
| // must include IBase + subclasses |
| EXPECT_NE(0u, hashChain.size()) << "Invalid hash chain " << base.name; |
| })) << base.name; |
| }); |
| } |
| |
| TEST_F(VtsHalBaseV1_0TargetTest, ServiceProvidesAndDeclaresTheSameInterfaces) { |
| Result<ServiceInterfacesMap> service_interfaces_map = |
| android::init::GetOnDeviceServiceInterfacesMap(); |
| ASSERT_TRUE(service_interfaces_map) << service_interfaces_map.error(); |
| PidInterfacesMap pid_interfaces_map = GetPidInterfacesMap(); |
| |
| for (auto [service, declared_interfaces] : *service_interfaces_map) { |
| if (declared_interfaces.empty()) { |
| LOG(INFO) << "Service '" << service << "' does not declare any interfaces." |
| << std::endl; |
| continue; |
| } |
| for (auto it = declared_interfaces.begin(); it != declared_interfaces.end();) { |
| if (it->getFqName() == android::gIBaseFqName) { |
| it = declared_interfaces.erase(it); |
| } else { |
| ++it; |
| } |
| } |
| pid_t pid = GetServiceDebugPid(service); |
| // TODO(b/138114550): Check lazy services that are not currently running |
| // (when pid == 0). |
| if (pid != 0) { |
| std::set<FqInstance> served_interfaces = pid_interfaces_map[pid]; |
| std::set<FqInstance> diff = SetDifference(served_interfaces, declared_interfaces); |
| EXPECT_TRUE(diff.empty()) |
| << "Service '" << service << "' serves interfaces that it does not declare." |
| << std::endl |
| << " Served:" << std::endl |
| << FqInstancesToString(served_interfaces) << std::endl |
| << " Declared: " << std::endl |
| << FqInstancesToString(declared_interfaces) << std::endl |
| << " Difference: " << std::endl |
| << FqInstancesToString(diff); |
| diff = SetDifference(declared_interfaces, served_interfaces); |
| EXPECT_TRUE(diff.empty()) |
| << "Service '" << service << "' declares interfaces that it does not serve." |
| << std::endl |
| << " Declared: " << std::endl |
| << FqInstancesToString(declared_interfaces) << std::endl |
| << " Served:" << std::endl |
| << FqInstancesToString(served_interfaces) << std::endl |
| << " Difference: " << std::endl |
| << FqInstancesToString(diff); |
| } |
| } |
| } |
| |
| int main(int argc, char** argv) { |
| ::testing::InitGoogleTest(&argc, argv); |
| return RUN_ALL_TESTS(); |
| } |