/* * Copyright (C) 2022 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 #include #include #include #include #include #include #include "utils/StrongPointer.h" #define CONCAT(x, y) x##y #define LOG_NDK_STATUS(x) \ do { \ const auto CONCAT(status, __COUNTER__) = x; \ if (!CONCAT(status, __COUNTER__).isOk()) { \ LOG(ERROR) << #x << " failed " << CONCAT(status, __COUNTER__).getDescription(); \ } \ } while (0) using aidl::android::hardware::boot::MergeStatus; std::ostream& operator<<(std::ostream& os, MergeStatus status) { switch (status) { case MergeStatus::NONE: os << "MergeStatus::NONE"; break; case MergeStatus::UNKNOWN: os << "MergeStatus::UNKNOWN"; break; case MergeStatus::SNAPSHOTTED: os << "MergeStatus::SNAPSHOTTED"; break; case MergeStatus::MERGING: os << "MergeStatus::MERGING"; break; case MergeStatus::CANCELLED: os << "MergeStatus::CANCELLED"; break; default: os << static_cast(status); break; } return os; } namespace android::hal { class BootControlClientAidl final : public BootControlClient { using IBootControl = ::aidl::android::hardware::boot::IBootControl; public: BootControlClientAidl(std::shared_ptr module) : module_(module) {} BootControlVersion GetVersion() const override { return BootControlVersion::BOOTCTL_AIDL; } ~BootControlClientAidl() = default; virtual int32_t GetNumSlots() const { int32_t ret = -1; LOG_NDK_STATUS(module_->getNumberSlots(&ret)); return ret; } int32_t GetCurrentSlot() const { int32_t ret = -1; LOG_NDK_STATUS(module_->getCurrentSlot(&ret)); return ret; } MergeStatus getSnapshotMergeStatus() const { MergeStatus status = MergeStatus::UNKNOWN; LOG_NDK_STATUS(module_->getSnapshotMergeStatus(&status)); return status; } std::string GetSuffix(int32_t slot) const { std::string ret; const auto status = module_->getSuffix(slot, &ret); if (!status.isOk()) { LOG(ERROR) << __FUNCTION__ << "(" << slot << ")" << " failed " << status.getDescription(); return {}; } return ret; } std::optional IsSlotBootable(int32_t slot) const { bool ret = false; const auto status = module_->isSlotBootable(slot, &ret); if (!status.isOk()) { LOG(ERROR) << __FUNCTION__ << "(" << slot << ")" << " failed " << status.getDescription(); return {}; } return ret; } CommandResult MarkSlotUnbootable(int32_t slot) { const auto status = module_->setSlotAsUnbootable(slot); if (!status.isOk()) { LOG(ERROR) << __FUNCTION__ << "(" << slot << ")" << " failed " << status.getDescription(); } return {.success = status.isOk(), .errMsg = status.getDescription()}; } CommandResult SetActiveBootSlot(int slot) { const auto status = module_->setActiveBootSlot(slot); if (!status.isOk()) { LOG(ERROR) << __FUNCTION__ << "(" << slot << ")" << " failed " << status.getDescription(); } return {.success = status.isOk(), .errMsg = status.getDescription()}; } int GetActiveBootSlot() const { int ret = -1; LOG_NDK_STATUS(module_->getActiveBootSlot(&ret)); return ret; } // Check if |slot| is marked boot successfully. std::optional IsSlotMarkedSuccessful(int slot) const { bool ret = false; const auto status = module_->isSlotMarkedSuccessful(slot, &ret); if (!status.isOk()) { LOG(ERROR) << __FUNCTION__ << "(" << slot << ")" << " failed " << status.getDescription(); return {}; } return ret; } CommandResult MarkBootSuccessful() { const auto status = module_->markBootSuccessful(); if (!status.isOk()) { LOG(ERROR) << __FUNCTION__ << " failed " << status.getDescription(); } return {.success = status.isOk(), .errMsg = status.getDescription()}; } CommandResult SetSnapshotMergeStatus(aidl::android::hardware::boot::MergeStatus merge_status) { const auto status = module_->setSnapshotMergeStatus(merge_status); if (!status.isOk()) { LOG(ERROR) << __FUNCTION__ << "(" << merge_status << ")" << " failed " << status.getDescription(); } return {.success = status.isOk(), .errMsg = status.getDescription()}; } private: const std::shared_ptr module_; }; using namespace android::hardware::boot; class BootControlClientHIDL final : public BootControlClient { public: BootControlClientHIDL(android::sp module_v1, android::sp module_v1_1, android::sp module_v1_2) : module_v1_(module_v1), module_v1_1_(module_v1_1), module_v1_2_(module_v1_2) { CHECK(module_v1_ != nullptr); } BootControlVersion GetVersion() const override { if (module_v1_2_ != nullptr) { return BootControlVersion::BOOTCTL_V1_2; } else if (module_v1_1_ != nullptr) { return BootControlVersion::BOOTCTL_V1_1; } else { return BootControlVersion::BOOTCTL_V1_0; } } int32_t GetNumSlots() const { const auto ret = module_v1_->getNumberSlots(); if (!ret.isOk()) { LOG(ERROR) << __FUNCTION__ << " failed " << ret.description(); } return ret.withDefault(-1); } int32_t GetCurrentSlot() const { const auto ret = module_v1_->getCurrentSlot(); if (!ret.isOk()) { LOG(ERROR) << __FUNCTION__ << " failed " << ret.description(); } return ret.withDefault(-1); } std::string GetSuffix(int32_t slot) const { std::string suffix; const auto ret = module_v1_->getSuffix( slot, [&](const ::android::hardware::hidl_string& slotSuffix) { suffix = slotSuffix; }); if (!ret.isOk()) { LOG(ERROR) << __FUNCTION__ << "(" << slot << ")" << " failed " << ret.description(); } return suffix; } std::optional IsSlotBootable(int32_t slot) const { const auto ret = module_v1_->isSlotBootable(slot); if (!ret.isOk()) { LOG(ERROR) << __FUNCTION__ << "(" << slot << ")" << " failed " << ret.description(); return {}; } const auto bool_result = ret.withDefault(V1_0::BoolResult::INVALID_SLOT); if (bool_result == V1_0::BoolResult::INVALID_SLOT) { return {}; } return bool_result == V1_0::BoolResult::TRUE; } CommandResult MarkSlotUnbootable(int32_t slot) { CommandResult result; const auto ret = module_v1_->setSlotAsUnbootable(slot, [&](const V1_0::CommandResult& error) { result.success = error.success; result.errMsg = error.errMsg; }); if (!ret.isOk()) { LOG(ERROR) << __FUNCTION__ << "(" << slot << ")" << " failed " << ret.description(); } return result; } CommandResult SetActiveBootSlot(int32_t slot) { CommandResult result; const auto ret = module_v1_->setActiveBootSlot(slot, [&](const V1_0::CommandResult& error) { result.success = error.success; result.errMsg = error.errMsg; }); if (!ret.isOk()) { LOG(ERROR) << __FUNCTION__ << "(" << slot << ")" << " failed " << ret.description(); } return result; } CommandResult MarkBootSuccessful() { CommandResult result; const auto ret = module_v1_->markBootSuccessful([&](const V1_0::CommandResult& error) { result.success = error.success; result.errMsg = error.errMsg; }); if (!ret.isOk()) { LOG(ERROR) << __FUNCTION__ << " failed " << ret.description(); } return result; } std::optional IsSlotMarkedSuccessful(int32_t slot) const { const auto ret = module_v1_->isSlotMarkedSuccessful(slot); if (!ret.isOk()) { LOG(ERROR) << __FUNCTION__ << "(" << slot << ")" << " failed " << ret.description(); return {}; } const auto bool_result = ret.withDefault(V1_0::BoolResult::INVALID_SLOT); if (bool_result == V1_0::BoolResult::INVALID_SLOT) { return {}; } return bool_result == V1_0::BoolResult::TRUE; } MergeStatus getSnapshotMergeStatus() const { if (module_v1_1_ == nullptr) { LOG(ERROR) << __FUNCTION__ << " is unsupported, requires at least boot v1.1"; return MergeStatus::UNKNOWN; } const auto ret = module_v1_1_->getSnapshotMergeStatus(); if (!ret.isOk()) { LOG(ERROR) << __FUNCTION__ << " failed " << ret.description(); } return static_cast( ret.withDefault(static_cast(MergeStatus::UNKNOWN))); } CommandResult SetSnapshotMergeStatus(MergeStatus merge_status) { if (module_v1_1_ == nullptr) { return {.success = false, .errMsg = "setSnapshotMergeStatus is unsupported, requires at least boot v1.1"}; } const auto ret = module_v1_1_->setSnapshotMergeStatus(static_cast(merge_status)); if (!ret.isOk()) { LOG(ERROR) << __FUNCTION__ << "(" << merge_status << ")" << " failed " << ret.description(); } return {.success = ret.isOk(), .errMsg = ret.description()}; } int32_t GetActiveBootSlot() const { if (module_v1_2_ == nullptr) { LOG(ERROR) << __FUNCTION__ << " is unsupported, requires at least boot v1.2"; return -1; } const auto ret = module_v1_2_->getActiveBootSlot(); if (!ret.isOk()) { LOG(ERROR) << __FUNCTION__ << " failed " << ret.description(); } return ret.withDefault(-1); } private: android::sp module_v1_; android::sp module_v1_1_; android::sp module_v1_2_; }; std::unique_ptr BootControlClient::WaitForService() { const auto instance_name = std::string(::aidl::android::hardware::boot::IBootControl::descriptor) + "/default"; if (AServiceManager_isDeclared(instance_name.c_str())) { auto module = ::aidl::android::hardware::boot::IBootControl::fromBinder( ndk::SpAIBinder(AServiceManager_waitForService(instance_name.c_str()))); if (module == nullptr) { LOG(ERROR) << "AIDL " << instance_name << " is declared but waitForService returned nullptr."; return nullptr; } LOG(INFO) << "Using AIDL version of IBootControl"; return std::make_unique(module); } LOG(INFO) << "AIDL IBootControl not available, falling back to HIDL."; android::sp v1_0_module; android::sp v1_1_module; android::sp v1_2_module; v1_0_module = V1_0::IBootControl::getService(); if (v1_0_module == nullptr) { LOG(ERROR) << "Error getting bootctrl v1.0 module."; return nullptr; } v1_1_module = V1_1::IBootControl::castFrom(v1_0_module); v1_2_module = V1_2::IBootControl::castFrom(v1_0_module); if (v1_2_module != nullptr) { LOG(INFO) << "Using HIDL version 1.2 of IBootControl"; } else if (v1_1_module != nullptr) { LOG(INFO) << "Using HIDL version 1.1 of IBootControl"; } else { LOG(INFO) << "Using HIDL version 1.0 of IBootControl"; } return std::make_unique(v1_0_module, v1_1_module, v1_2_module); } } // namespace android::hal