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

Commit 56093a77 authored by Matt Buckley's avatar Matt Buckley
Browse files

Add rate limiter to sendHint API call

Adds a rate limiter to the HintSession.sendHint API for safety, and
update tests to reflect this.

Bug: b/243973548
Test: atest PerformanceHintNativeTestCases
Test: atest FrameworksCoreTests:android.os.PerformanceHintManagerTest
Change-Id: Ic68683bacf7df3e11efc3d59689b5470c3fa4274
parent 7c5817e8
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -117,7 +117,8 @@ public class PerformanceHintManagerTest {
    public void testSendHint() {
        Session s = createSession();
        assumeNotNull(s);
        s.sendHint(Session.CPU_LOAD_UP);
        s.sendHint(Session.CPU_LOAD_RESET);
        // ensure we can also send within the rate limit without exception
        s.sendHint(Session.CPU_LOAD_RESET);
    }

+1 −0
Original line number Diff line number Diff line
@@ -95,6 +95,7 @@ cc_library_shared {
        "libpowermanager",
        "android.hardware.configstore@1.0",
        "android.hardware.configstore-utils",
        "android.hardware.power-V4-ndk",
        "libnativedisplay",
    ],

+25 −3
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

#define LOG_TAG "perf_hint"

#include <aidl/android/hardware/power/SessionHint.h>
#include <android/os/IHintManager.h>
#include <android/os/IHintSession.h>
#include <android/performance_hint.h>
@@ -25,14 +26,21 @@
#include <performance_hint_private.h>
#include <utils/SystemClock.h>

#include <chrono>
#include <utility>
#include <vector>

using namespace android;
using namespace android::os;

using namespace std::chrono_literals;

using AidlSessionHint = aidl::android::hardware::power::SessionHint;

struct APerformanceHintSession;

constexpr int64_t SEND_HINT_TIMEOUT = std::chrono::nanoseconds(100ms).count();

struct APerformanceHintManager {
public:
    static APerformanceHintManager* getInstance();
@@ -75,6 +83,8 @@ private:
    int64_t mFirstTargetMetTimestamp;
    // Last target hit timestamp
    int64_t mLastTargetMetTimestamp;
    // Last hint reported from sendHint indexed by hint value
    std::vector<int64_t> mLastHintSentTimestamp;
    // Cached samples
    std::vector<int64_t> mActualDurationsNanos;
    std::vector<int64_t> mTimestampsNanos;
@@ -147,7 +157,12 @@ APerformanceHintSession::APerformanceHintSession(sp<IHintSession> session,
        mPreferredRateNanos(preferredRateNanos),
        mTargetDurationNanos(targetDurationNanos),
        mFirstTargetMetTimestamp(0),
        mLastTargetMetTimestamp(0) {}
        mLastTargetMetTimestamp(0) {
    const std::vector<AidlSessionHint> sessionHintRange{ndk::enum_range<AidlSessionHint>().begin(),
                                                        ndk::enum_range<AidlSessionHint>().end()};

    mLastHintSentTimestamp = std::vector<int64_t>(sessionHintRange.size(), 0);
}

APerformanceHintSession::~APerformanceHintSession() {
    binder::Status ret = mHintSession->close();
@@ -224,10 +239,16 @@ int APerformanceHintSession::reportActualWorkDuration(int64_t actualDurationNano
}

int APerformanceHintSession::sendHint(int32_t hint) {
    if (hint < 0) {
        ALOGE("%s: session hint value must be greater than zero", __FUNCTION__);
    if (hint < 0 || hint >= static_cast<int32_t>(mLastHintSentTimestamp.size())) {
        ALOGE("%s: invalid session hint %d", __FUNCTION__, hint);
        return EINVAL;
    }
    int64_t now = elapsedRealtimeNano();

    // Limit sendHint to a pre-detemined rate for safety
    if (now < (mLastHintSentTimestamp[hint] + SEND_HINT_TIMEOUT)) {
        return 0;
    }

    binder::Status ret = mHintSession->sendHint(hint);

@@ -235,6 +256,7 @@ int APerformanceHintSession::sendHint(int32_t hint) {
        ALOGE("%s: HintSession sendHint failed: %s", __FUNCTION__, ret.exceptionMessage().c_str());
        return EPIPE;
    }
    mLastHintSentTimestamp[hint] = now;
    return 0;
}

+9 −2
Original line number Diff line number Diff line
@@ -122,9 +122,16 @@ TEST_F(PerformanceHintTest, TestSession) {
    result = APerformanceHint_reportActualWorkDuration(session, -1L);
    EXPECT_EQ(EINVAL, result);

    // Send both valid and invalid session hints
    int hintId = 2;
    EXPECT_CALL(*iSession, sendHint(Eq(2))).Times(Exactly(1));
    EXPECT_CALL(*iSession, sendHint(Eq(hintId))).Times(Exactly(1));
    result = APerformanceHint_sendHint(session, hintId);
    EXPECT_EQ(0, result);
    usleep(110000); // Sleep for longer than the update timeout.
    EXPECT_CALL(*iSession, sendHint(Eq(hintId))).Times(Exactly(1));
    result = APerformanceHint_sendHint(session, hintId);
    EXPECT_EQ(0, result);
    // Expect to get rate limited if we try to send faster than the limiter allows
    EXPECT_CALL(*iSession, sendHint(Eq(hintId))).Times(Exactly(0));
    result = APerformanceHint_sendHint(session, hintId);
    EXPECT_EQ(0, result);