Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b66d2a08 authored by Kelvin Zhang's avatar Kelvin Zhang
Browse files

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
parent 309f5404
Loading
Loading
Loading
Loading
+22 −0
Original line number Diff line number Diff line


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
+359 −0
Original line number Diff line number Diff line
/*
 * 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 −0
Original line number Diff line number Diff line
/*
 * 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