Files
hardware_interfaces/boot/aidl/client/BootControlClient.cpp
Daniel Zheng 01d5a1d2f1 bootctl: fix reconnect logic
Instead of just logging an error message, let's try reonnecting to the
bootctl service if our death recipient is invoked. We should also
reconnect our death recipient here.

If binder is killed, our death recipient is automatically unlinked.

Bug: 369289491
Test: kill bootctl service on CVD. Apply OTA
Change-Id: I914643baaf1fa6fe1e192517a2e43e07ee749b70
2024-10-21 11:39:00 -07:00

400 lines
15 KiB
C++

/*
* 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_ibinder.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:
explicit BootControlClientAidl(std::shared_ptr<IBootControl> module)
: module_(module),
boot_control_death_recipient(AIBinder_DeathRecipient_new(onBootControlServiceDied)) {
binder_status_t status =
AIBinder_linkToDeath(module->asBinder().get(), boot_control_death_recipient, this);
if (status != STATUS_OK) {
LOG(ERROR) << "Could not link to binder death";
return;
}
}
BootControlVersion GetVersion() const override { return BootControlVersion::BOOTCTL_AIDL; }
void onBootControlServiceDied() {
LOG(ERROR) << "boot control service AIDL died. Attempting to reconnect...";
const auto instance_name =
std::string(::aidl::android::hardware::boot::IBootControl::descriptor) + "/default";
if (AServiceManager_isDeclared(instance_name.c_str())) {
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 when trying to "
"reconnect boot control service";
return;
}
LOG(INFO) << "Reconnected to AIDL version of IBootControl";
binder_status_t status = AIBinder_linkToDeath(module_->asBinder().get(),
boot_control_death_recipient, this);
if (status != STATUS_OK) {
LOG(ERROR) << "Could not link to binder death";
return;
}
} else {
LOG(ERROR) << "Failed to get service manager for: " << instance_name;
}
}
int32_t GetNumSlots() const override {
int32_t ret = -1;
LOG_NDK_STATUS(module_->getNumberSlots(&ret));
return ret;
}
int32_t GetCurrentSlot() const override {
int32_t ret = -1;
LOG_NDK_STATUS(module_->getCurrentSlot(&ret));
return ret;
}
MergeStatus getSnapshotMergeStatus() const override {
MergeStatus status = MergeStatus::UNKNOWN;
LOG_NDK_STATUS(module_->getSnapshotMergeStatus(&status));
return status;
}
std::string GetSuffix(int32_t slot) const override {
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 override {
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) override {
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) override {
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 override {
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() override {
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) override {
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:
std::shared_ptr<IBootControl> module_;
AIBinder_DeathRecipient* boot_control_death_recipient;
static void onBootControlServiceDied(void* client) {
BootControlClientAidl* self = static_cast<BootControlClientAidl*>(client);
self->onBootControlServiceDied();
}
};
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 override {
const auto ret = module_v1_->getNumberSlots();
if (!ret.isOk()) {
LOG(ERROR) << __FUNCTION__ << " failed " << ret.description();
}
return ret.withDefault(-1);
}
int32_t GetCurrentSlot() const override {
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 override {
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 override {
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) override {
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) override {
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() override {
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 override {
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 override {
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) override {
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 override {
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