| /* |
| * 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 <BootControlClient.h> |
| |
| #include <aidl/android/hardware/boot/IBootControl.h> |
| #include <android-base/logging.h> |
| #include <android/binder_manager.h> |
| #include <android/hardware/boot/1.0/IBootControl.h> |
| #include <android/hardware/boot/1.1/IBootControl.h> |
| #include <android/hardware/boot/1.2/IBootControl.h> |
| #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<int>(status); |
| break; |
| } |
| return os; |
| } |
| |
| namespace android::hal { |
| class BootControlClientAidl final : public BootControlClient { |
| using IBootControl = ::aidl::android::hardware::boot::IBootControl; |
| |
| public: |
| BootControlClientAidl(std::shared_ptr<IBootControl> 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<bool> 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<bool> 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<IBootControl> module_; |
| }; |
| |
| using namespace android::hardware::boot; |
| |
| class BootControlClientHIDL final : public BootControlClient { |
| public: |
| BootControlClientHIDL(android::sp<V1_0::IBootControl> module_v1, |
| android::sp<V1_1::IBootControl> module_v1_1, |
| android::sp<V1_2::IBootControl> 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<bool> 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<bool> 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<MergeStatus>( |
| ret.withDefault(static_cast<V1_1::MergeStatus>(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<V1_1::MergeStatus>(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<V1_0::IBootControl> module_v1_; |
| android::sp<V1_1::IBootControl> module_v1_1_; |
| android::sp<V1_2::IBootControl> module_v1_2_; |
| }; |
| |
| std::unique_ptr<BootControlClient> 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<BootControlClientAidl>(module); |
| } |
| LOG(INFO) << "AIDL IBootControl not available, falling back to HIDL."; |
| |
| android::sp<V1_0::IBootControl> v1_0_module; |
| android::sp<V1_1::IBootControl> v1_1_module; |
| android::sp<V1_2::IBootControl> 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<BootControlClientHIDL>(v1_0_module, v1_1_module, v1_2_module); |
| } |
| |
| } // namespace android::hal |