mirror of
https://github.com/Evolution-X/hardware_interfaces
synced 2026-02-01 11:36:00 +00:00
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
This commit is contained in:
22
boot/aidl/client/Android.bp
Normal file
22
boot/aidl/client/Android.bp
Normal file
@@ -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",
|
||||
],
|
||||
}
|
||||
359
boot/aidl/client/BootControlClient.cpp
Normal file
359
boot/aidl/client/BootControlClient.cpp
Normal file
@@ -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
|
||||
97
boot/aidl/client/include/BootControlClient.h
Normal file
97
boot/aidl/client/include/BootControlClient.h
Normal file
@@ -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 <aidl/android/hardware/boot/MergeStatus.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
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<bool> 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<bool> 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<BootControlClient> WaitForService();
|
||||
};
|
||||
|
||||
} // namespace android::hal
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user