diff --git a/boot/aidl/client/Android.bp b/boot/aidl/client/Android.bp new file mode 100644 index 0000000000..3e97e07276 --- /dev/null +++ b/boot/aidl/client/Android.bp @@ -0,0 +1,22 @@ + + +cc_library { + name: "libboot_control_client", + srcs: [ + "BootControlClient.cpp" + ], + export_include_dirs: ["include"], + export_shared_lib_headers: ["android.hardware.boot-V1-ndk"], + recovery_available: true, + shared_libs: [ + "android.hardware.boot-V1-ndk", + "android.hardware.boot@1.0", + "android.hardware.boot@1.1", + "android.hardware.boot@1.2", + "libhidlbase", + "libbinder_ndk", + "libbase", + "libcutils", + "libutils", + ], +} \ No newline at end of file diff --git a/boot/aidl/client/BootControlClient.cpp b/boot/aidl/client/BootControlClient.cpp new file mode 100644 index 0000000000..28070eb1de --- /dev/null +++ b/boot/aidl/client/BootControlClient.cpp @@ -0,0 +1,359 @@ +/* + * 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 (auto module = ::aidl::android::hardware::boot::IBootControl::fromBinder( + ndk::SpAIBinder(AServiceManager_waitForService(instance_name.c_str()))); + module != 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 diff --git a/boot/aidl/client/include/BootControlClient.h b/boot/aidl/client/include/BootControlClient.h new file mode 100644 index 0000000000..472e82e842 --- /dev/null +++ b/boot/aidl/client/include/BootControlClient.h @@ -0,0 +1,97 @@ +/* + * 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. + */ + +#ifndef __BOOT_CONTROL_CLIENT_H_ +#define __BOOT_CONTROL_CLIENT_H_ + +#include + +#include + +#include +#include + +namespace android::hal { + +struct CommandResult { + bool success; + std::string errMsg; + constexpr bool IsOk() const { return success; } +}; + +enum class BootControlVersion { BOOTCTL_V1_0, BOOTCTL_V1_1, BOOTCTL_V1_2, BOOTCTL_AIDL }; + +class BootControlClient { + public: + using MergeStatus = aidl::android::hardware::boot::MergeStatus; + virtual ~BootControlClient() = default; + virtual BootControlVersion GetVersion() const = 0; + // Return the number of update slots in the system. A system will normally + // have two slots, named "A" and "B" in the documentation, but sometimes + // images running from other media can have only one slot, like some USB + // image. Systems with only one slot won't be able to update. + [[nodiscard]] virtual int32_t GetNumSlots() const = 0; + + // Return the slot where we are running the system from. On success, the + // result is a number between 0 and GetNumSlots() - 1. Otherwise, log an error + // and return kInvalidSlot. + [[nodiscard]] virtual int32_t GetCurrentSlot() const = 0; + + // Return string suffix for input slot. Usually, for slot 0 the suffix is _a, and for slot 1 the + // suffix is _b. + [[nodiscard]] virtual std::string GetSuffix(int32_t slot) const = 0; + + // Returns whether the passed |slot| is marked as bootable. Returns false if + // the slot is invalid. + [[nodiscard]] virtual std::optional IsSlotBootable(int32_t slot) const = 0; + + // Mark the specified slot unbootable. No other slot flags are modified. + // Returns true on success. + [[nodiscard]] virtual CommandResult MarkSlotUnbootable(int32_t slot) = 0; + + // Set the passed |slot| as the preferred boot slot. Returns whether it + // succeeded setting the active slot. If succeeded, on next boot the + // bootloader will attempt to load the |slot| marked as active. Note that this + // method doesn't change the value of GetCurrentSlot() on the current boot. + // Return true if operation succeeded. + [[nodiscard]] virtual CommandResult SetActiveBootSlot(int32_t slot) = 0; + + // Check if |slot| is marked boot successfully. Return empty optional if the RPC call failed. + [[nodiscard]] virtual std::optional IsSlotMarkedSuccessful(int32_t slot) const = 0; + + // Mark boot as successful. Return an error message if operation failed. + [[nodiscard]] virtual CommandResult MarkBootSuccessful() = 0; + + // Added in IBootControl v1.1 + // Return the current merge status. + [[nodiscard]] virtual MergeStatus getSnapshotMergeStatus() const = 0; + + // Set snapshot merge status, return true if succeeded. + [[nodiscard]] virtual CommandResult SetSnapshotMergeStatus(MergeStatus status) = 0; + + // Added in IBootControl v1.2 + // Get the active slot. In other words, the slot which will be used on + // next system reboot. This should match the |slot| parameter of last + // successful call to |SetActiveBootSlot|. + // Return 0xFFFFFFFF if underlying HAL doesn't support this operation. + [[nodiscard]] virtual int32_t GetActiveBootSlot() const = 0; + + [[nodiscard]] static std::unique_ptr WaitForService(); +}; + +} // namespace android::hal + +#endif