Add a wrapper for callers of BootControl HAL
We are switching BootControl from HIDL to AIDL. All clients must be
compatible with both implementations. To ease implementation, create a
wrapper that will use AIDL if possible, and fallback to HIDL otherwise.
Test: th
Bug: 227536004
Change-Id: Ia40cb384058a0052f7c5c39766534c23a095ca59
diff --git a/boot/aidl/client/BootControlClient.cpp b/boot/aidl/client/BootControlClient.cpp
new file mode 100644
index 0000000..28070eb
--- /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 <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 (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<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