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

Commit 6c18e6da authored by Matt Buckley's avatar Matt Buckley
Browse files

Update PowerHAL wrapper support checking behavior

- Updates support checks to check status for UNKNOWN_TRANSACTION
- Adds PowerHintSessionWrapper class to check support on session methods
- Ensures that wrapper methods check the HAL version number for support
- Adds macros to cache returned wrapper call support status

Bug: 324255931
Test: atest libpowermanager_test
Test: atest libsurfaceflinger_unittest:PowerAdvisorTest
Change-Id: I4b329e6b55c53198bb064a34e792be6336e66e27
parent 415c59e4
Loading
Loading
Loading
Loading
+165 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.
 */

#pragma once

#include <android/binder_auto_utils.h>
#include <android/binder_status.h>
#include <android/hardware/power/1.0/IPower.h>
#include <binder/Status.h>
#include <hidl/HidlSupport.h>
#include <string>

namespace android::power {

static bool checkUnsupported(const ndk::ScopedAStatus& ndkStatus) {
    return ndkStatus.getExceptionCode() == EX_UNSUPPORTED_OPERATION ||
            ndkStatus.getStatus() == STATUS_UNKNOWN_TRANSACTION;
}

static bool checkUnsupported(const binder::Status& status) {
    return status.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION ||
            status.transactionError() == UNKNOWN_TRANSACTION;
}

// Result of a call to the Power HAL wrapper, holding data if successful.
template <typename T>
class HalResult {
public:
    static HalResult<T> ok(T&& value) { return HalResult(std::forward<T>(value)); }
    static HalResult<T> ok(T& value) { return HalResult<T>::ok(T{value}); }
    static HalResult<T> failed(std::string msg) { return HalResult(msg, /* unsupported= */ false); }
    static HalResult<T> unsupported() { return HalResult("", /* unsupported= */ true); }

    static HalResult<T> fromStatus(const binder::Status& status, T&& data) {
        if (checkUnsupported(status)) {
            return HalResult<T>::unsupported();
        }
        if (status.isOk()) {
            return HalResult<T>::ok(std::forward<T>(data));
        }
        return HalResult<T>::failed(std::string(status.toString8().c_str()));
    }

    static HalResult<T> fromStatus(const binder::Status& status, T& data) {
        return HalResult<T>::fromStatus(status, T{data});
    }

    static HalResult<T> fromStatus(const ndk::ScopedAStatus& ndkStatus, T&& data) {
        if (checkUnsupported(ndkStatus)) {
            return HalResult<T>::unsupported();
        }
        if (ndkStatus.isOk()) {
            return HalResult<T>::ok(std::forward<T>(data));
        }
        return HalResult<T>::failed(std::string(ndkStatus.getDescription()));
    }

    static HalResult<T> fromStatus(const ndk::ScopedAStatus& ndkStatus, T& data) {
        return HalResult<T>::fromStatus(ndkStatus, T{data});
    }

    template <typename R>
    static HalResult<T> fromReturn(hardware::Return<R>& ret, T&& data) {
        return ret.isOk() ? HalResult<T>::ok(std::forward<T>(data))
                          : HalResult<T>::failed(ret.description());
    }

    template <typename R>
    static HalResult<T> fromReturn(hardware::Return<R>& ret, T& data) {
        return HalResult<T>::fromReturn(ret, T{data});
    }

    template <typename R>
    static HalResult<T> fromReturn(hardware::Return<R>& ret, hardware::power::V1_0::Status status,
                                   T&& data) {
        return ret.isOk() ? HalResult<T>::fromStatus(status, std::forward<T>(data))
                          : HalResult<T>::failed(ret.description());
    }

    template <typename R>
    static HalResult<T> fromReturn(hardware::Return<R>& ret, hardware::power::V1_0::Status status,
                                   T& data) {
        return HalResult<T>::fromReturn(ret, status, T{data});
    }

    // This will throw std::bad_optional_access if this result is not ok.
    const T& value() const { return mValue.value(); }
    bool isOk() const { return !mUnsupported && mValue.has_value(); }
    bool isFailed() const { return !mUnsupported && !mValue.has_value(); }
    bool isUnsupported() const { return mUnsupported; }
    const char* errorMessage() const { return mErrorMessage.c_str(); }

private:
    std::optional<T> mValue;
    std::string mErrorMessage;
    bool mUnsupported;

    explicit HalResult(T&& value)
          : mValue{std::move(value)}, mErrorMessage(), mUnsupported(false) {}
    explicit HalResult(std::string errorMessage, bool unsupported)
          : mValue(), mErrorMessage(std::move(errorMessage)), mUnsupported(unsupported) {}
};

// Empty result
template <>
class HalResult<void> {
public:
    static HalResult<void> ok() { return HalResult(); }
    static HalResult<void> failed(std::string msg) { return HalResult(std::move(msg)); }
    static HalResult<void> unsupported() { return HalResult(/* unsupported= */ true); }

    static HalResult<void> fromStatus(const binder::Status& status) {
        if (checkUnsupported(status)) {
            return HalResult<void>::unsupported();
        }
        if (status.isOk()) {
            return HalResult<void>::ok();
        }
        return HalResult<void>::failed(std::string(status.toString8().c_str()));
    }

    static HalResult<void> fromStatus(const ndk::ScopedAStatus& ndkStatus) {
        if (ndkStatus.isOk()) {
            return HalResult<void>::ok();
        }
        if (checkUnsupported(ndkStatus)) {
            return HalResult<void>::unsupported();
        }
        return HalResult<void>::failed(ndkStatus.getDescription());
    }

    template <typename R>
    static HalResult<void> fromReturn(hardware::Return<R>& ret) {
        return ret.isOk() ? HalResult<void>::ok() : HalResult<void>::failed(ret.description());
    }

    bool isOk() const { return !mUnsupported && !mFailed; }
    bool isFailed() const { return !mUnsupported && mFailed; }
    bool isUnsupported() const { return mUnsupported; }
    const char* errorMessage() const { return mErrorMessage.c_str(); }

private:
    std::string mErrorMessage;
    bool mFailed;
    bool mUnsupported;

    explicit HalResult(bool unsupported = false)
          : mErrorMessage(), mFailed(false), mUnsupported(unsupported) {}
    explicit HalResult(std::string errorMessage)
          : mErrorMessage(std::move(errorMessage)), mFailed(true), mUnsupported(false) {}
};
} // namespace android::power
+9 −8
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@
#include <aidl/android/hardware/power/Mode.h>
#include <android-base/thread_annotations.h>
#include <powermanager/PowerHalWrapper.h>
#include <powermanager/PowerHintSessionWrapper.h>

namespace android {

@@ -38,6 +39,7 @@ public:

    virtual std::unique_ptr<HalWrapper> connect();
    virtual void reset();
    virtual int32_t getAidlVersion();
};

// -------------------------------------------------------------------------------------------------
@@ -59,12 +61,11 @@ public:
                                     int32_t durationMs) override;
    virtual HalResult<void> setMode(aidl::android::hardware::power::Mode mode,
                                    bool enabled) override;
    virtual HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>>
    createHintSession(int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
    virtual HalResult<std::shared_ptr<PowerHintSessionWrapper>> createHintSession(
            int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
            int64_t durationNanos) override;
    virtual HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>>
    createHintSessionWithConfig(int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
                                int64_t durationNanos,
    virtual HalResult<std::shared_ptr<PowerHintSessionWrapper>> createHintSessionWithConfig(
            int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, int64_t durationNanos,
            aidl::android::hardware::power::SessionTag tag,
            aidl::android::hardware::power::SessionConfig* config) override;
    virtual HalResult<int64_t> getHintSessionPreferredRate() override;
+4 −0
Original line number Diff line number Diff line
@@ -36,6 +36,8 @@ public:
    static sp<hardware::power::V1_1::IPower> loadHidlV1_1();
    static sp<hardware::power::V1_2::IPower> loadHidlV1_2();
    static sp<hardware::power::V1_3::IPower> loadHidlV1_3();
    // Returns aidl interface version, or 0 if AIDL is not used
    static int32_t getAidlVersion();

private:
    static std::mutex gHalMutex;
@@ -48,6 +50,8 @@ private:
    static sp<hardware::power::V1_0::IPower> loadHidlV1_0Locked()
            EXCLUSIVE_LOCKS_REQUIRED(gHalMutex);

    static int32_t gAidlInterfaceVersion;

    PowerHalLoader() = delete;
    ~PowerHalLoader() = delete;
};
+25 −155
Original line number Diff line number Diff line
@@ -26,6 +26,9 @@
#include <android/hardware/power/1.1/IPower.h>
#include <android/hardware/power/1.2/IPower.h>
#include <android/hardware/power/1.3/IPower.h>
#include <powermanager/HalResult.h>
#include <powermanager/PowerHintSessionWrapper.h>

#include <binder/Status.h>

#include <utility>
@@ -41,134 +44,6 @@ enum class HalSupport {
    OFF = 2,
};

// Result of a call to the Power HAL wrapper, holding data if successful.
template <typename T>
class HalResult {
public:
    static HalResult<T> ok(T&& value) { return HalResult(std::forward<T>(value)); }
    static HalResult<T> ok(T& value) { return HalResult<T>::ok(T{value}); }
    static HalResult<T> failed(std::string msg) { return HalResult(msg, /* unsupported= */ false); }
    static HalResult<T> unsupported() { return HalResult("", /* unsupported= */ true); }

    static HalResult<T> fromStatus(const binder::Status& status, T&& data) {
        if (status.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
            return HalResult<T>::unsupported();
        }
        if (status.isOk()) {
            return HalResult<T>::ok(std::forward<T>(data));
        }
        return HalResult<T>::failed(std::string(status.toString8().c_str()));
    }

    static HalResult<T> fromStatus(const binder::Status& status, T& data) {
        return HalResult<T>::fromStatus(status, T{data});
    }

    static HalResult<T> fromStatus(const ndk::ScopedAStatus& status, T&& data) {
        if (status.getExceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
            return HalResult<T>::unsupported();
        }
        if (status.isOk()) {
            return HalResult<T>::ok(std::forward<T>(data));
        }
        return HalResult<T>::failed(std::string(status.getDescription()));
    }

    static HalResult<T> fromStatus(const ndk::ScopedAStatus& status, T& data) {
        return HalResult<T>::fromStatus(status, T{data});
    }

    template <typename R>
    static HalResult<T> fromReturn(hardware::Return<R>& ret, T&& data) {
        return ret.isOk() ? HalResult<T>::ok(std::forward<T>(data))
                          : HalResult<T>::failed(ret.description());
    }

    template <typename R>
    static HalResult<T> fromReturn(hardware::Return<R>& ret, T& data) {
        return HalResult<T>::fromReturn(ret, T{data});
    }

    template <typename R>
    static HalResult<T> fromReturn(hardware::Return<R>& ret, hardware::power::V1_0::Status status,
                                   T&& data) {
        return ret.isOk() ? HalResult<T>::fromStatus(status, std::forward<T>(data))
                          : HalResult<T>::failed(ret.description());
    }

    template <typename R>
    static HalResult<T> fromReturn(hardware::Return<R>& ret, hardware::power::V1_0::Status status,
                                   T& data) {
        return HalResult<T>::fromReturn(ret, status, T{data});
    }

    // This will throw std::bad_optional_access if this result is not ok.
    const T& value() const { return mValue.value(); }
    bool isOk() const { return !mUnsupported && mValue.has_value(); }
    bool isFailed() const { return !mUnsupported && !mValue.has_value(); }
    bool isUnsupported() const { return mUnsupported; }
    const char* errorMessage() const { return mErrorMessage.c_str(); }

private:
    std::optional<T> mValue;
    std::string mErrorMessage;
    bool mUnsupported;

    explicit HalResult(T&& value)
          : mValue{std::move(value)}, mErrorMessage(), mUnsupported(false) {}
    explicit HalResult(std::string errorMessage, bool unsupported)
          : mValue(), mErrorMessage(std::move(errorMessage)), mUnsupported(unsupported) {}
};

// Empty result of a call to the Power HAL wrapper.
template <>
class HalResult<void> {
public:
    static HalResult<void> ok() { return HalResult(); }
    static HalResult<void> failed(std::string msg) { return HalResult(std::move(msg)); }
    static HalResult<void> unsupported() { return HalResult(/* unsupported= */ true); }

    static HalResult<void> fromStatus(const binder::Status& status) {
        if (status.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
            return HalResult<void>::unsupported();
        }
        if (status.isOk()) {
            return HalResult<void>::ok();
        }
        return HalResult<void>::failed(std::string(status.toString8().c_str()));
    }

    static HalResult<void> fromStatus(const ndk::ScopedAStatus& status) {
        if (status.getExceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
            return HalResult<void>::unsupported();
        }
        if (status.isOk()) {
            return HalResult<void>::ok();
        }
        return HalResult<void>::failed(std::string(status.getDescription()));
    }

    template <typename R>
    static HalResult<void> fromReturn(hardware::Return<R>& ret) {
        return ret.isOk() ? HalResult<void>::ok() : HalResult<void>::failed(ret.description());
    }

    bool isOk() const { return !mUnsupported && !mFailed; }
    bool isFailed() const { return !mUnsupported && mFailed; }
    bool isUnsupported() const { return mUnsupported; }
    const char* errorMessage() const { return mErrorMessage.c_str(); }

private:
    std::string mErrorMessage;
    bool mFailed;
    bool mUnsupported;

    explicit HalResult(bool unsupported = false)
          : mErrorMessage(), mFailed(false), mUnsupported(unsupported) {}
    explicit HalResult(std::string errorMessage)
          : mErrorMessage(std::move(errorMessage)), mFailed(true), mUnsupported(false) {}
};

// Wrapper for Power HAL handlers.
class HalWrapper {
public:
@@ -177,12 +52,11 @@ public:
    virtual HalResult<void> setBoost(aidl::android::hardware::power::Boost boost,
                                     int32_t durationMs) = 0;
    virtual HalResult<void> setMode(aidl::android::hardware::power::Mode mode, bool enabled) = 0;
    virtual HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>>
    createHintSession(int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
    virtual HalResult<std::shared_ptr<PowerHintSessionWrapper>> createHintSession(
            int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
            int64_t durationNanos) = 0;
    virtual HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>>
    createHintSessionWithConfig(int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
                                int64_t durationNanos,
    virtual HalResult<std::shared_ptr<PowerHintSessionWrapper>> createHintSessionWithConfig(
            int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, int64_t durationNanos,
            aidl::android::hardware::power::SessionTag tag,
            aidl::android::hardware::power::SessionConfig* config) = 0;
    virtual HalResult<int64_t> getHintSessionPreferredRate() = 0;
@@ -200,12 +74,11 @@ public:
    HalResult<void> setBoost(aidl::android::hardware::power::Boost boost,
                             int32_t durationMs) override;
    HalResult<void> setMode(aidl::android::hardware::power::Mode mode, bool enabled) override;
    HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>> createHintSession(
    HalResult<std::shared_ptr<PowerHintSessionWrapper>> createHintSession(
            int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
            int64_t durationNanos) override;
    HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>>
    createHintSessionWithConfig(int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
                                int64_t durationNanos,
    HalResult<std::shared_ptr<PowerHintSessionWrapper>> createHintSessionWithConfig(
            int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, int64_t durationNanos,
            aidl::android::hardware::power::SessionTag tag,
            aidl::android::hardware::power::SessionConfig* config) override;
    HalResult<int64_t> getHintSessionPreferredRate() override;
@@ -285,12 +158,11 @@ public:
    HalResult<void> setBoost(aidl::android::hardware::power::Boost boost,
                             int32_t durationMs) override;
    HalResult<void> setMode(aidl::android::hardware::power::Mode mode, bool enabled) override;
    HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>> createHintSession(
    HalResult<std::shared_ptr<PowerHintSessionWrapper>> createHintSession(
            int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
            int64_t durationNanos) override;
    HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>>
    createHintSessionWithConfig(int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
                                int64_t durationNanos,
    HalResult<std::shared_ptr<PowerHintSessionWrapper>> createHintSessionWithConfig(
            int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, int64_t durationNanos,
            aidl::android::hardware::power::SessionTag tag,
            aidl::android::hardware::power::SessionConfig* config) override;

@@ -307,14 +179,12 @@ private:
    std::mutex mBoostMutex;
    std::mutex mModeMutex;
    std::shared_ptr<aidl::android::hardware::power::IPower> mHandle;
    // Android framework only sends boost upto DISPLAY_UPDATE_IMMINENT.
    // Need to increase the array size if more boost supported.
    std::array<
            std::atomic<HalSupport>,
            static_cast<int32_t>(aidl::android::hardware::power::Boost::DISPLAY_UPDATE_IMMINENT) +
    std::array<HalSupport,
               static_cast<int32_t>(
                       *(ndk::enum_range<aidl::android::hardware::power::Boost>().end() - 1)) +
                       1>
            mBoostSupportedArray GUARDED_BY(mBoostMutex) = {HalSupport::UNKNOWN};
    std::array<std::atomic<HalSupport>,
    std::array<HalSupport,
               static_cast<int32_t>(
                       *(ndk::enum_range<aidl::android::hardware::power::Mode>().end() - 1)) +
                       1>
+54 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.
 */

#pragma once

#include <aidl/android/hardware/power/Boost.h>
#include <aidl/android/hardware/power/ChannelConfig.h>
#include <aidl/android/hardware/power/IPower.h>
#include <aidl/android/hardware/power/IPowerHintSession.h>
#include <aidl/android/hardware/power/Mode.h>
#include <aidl/android/hardware/power/SessionConfig.h>
#include <android-base/thread_annotations.h>
#include "HalResult.h"

namespace android::power {

// Wrapper for power hint sessions, which allows for better mocking,
// support checking, and failure handling than using hint sessions directly
class PowerHintSessionWrapper {
public:
    virtual ~PowerHintSessionWrapper() = default;
    PowerHintSessionWrapper(
            std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>&& session);
    virtual HalResult<void> updateTargetWorkDuration(int64_t in_targetDurationNanos);
    virtual HalResult<void> reportActualWorkDuration(
            const std::vector<::aidl::android::hardware::power::WorkDuration>& in_durations);
    virtual HalResult<void> pause();
    virtual HalResult<void> resume();
    virtual HalResult<void> close();
    virtual HalResult<void> sendHint(::aidl::android::hardware::power::SessionHint in_hint);
    virtual HalResult<void> setThreads(const std::vector<int32_t>& in_threadIds);
    virtual HalResult<void> setMode(::aidl::android::hardware::power::SessionMode in_type,
                                    bool in_enabled);
    virtual HalResult<aidl::android::hardware::power::SessionConfig> getSessionConfig();

private:
    std::shared_ptr<aidl::android::hardware::power::IPowerHintSession> mSession;
    int32_t mInterfaceVersion;
};

} // namespace android::power
 No newline at end of file
Loading