Add API support for GPU work duration report in ADPF.

Previously we introduced the reportActualWorkDuration API without
specifying the work duration for each components, this patch introduces
a separate API that allows clients to send work duration with each
component to allow fine grained scheduling strategy.

Bug: b/284324521
Test: atest PerformanceHintNativeTestCases
Test: atest PerformanceHintManagerTest
Test: atest HintManagerServiceTest
Change-Id: Id7261b9b5779cf618d1a611e66240602c36e06d0
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index b0af09c..f4be33c7 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -335,6 +335,13 @@
     APerformanceHint_closeSession; # introduced=Tiramisu
     APerformanceHint_setThreads; # introduced=UpsideDownCake
     APerformanceHint_setPreferPowerEfficiency; # introduced=VanillaIceCream
+    APerformanceHint_reportActualWorkDuration2; # introduced=VanillaIceCream
+    AWorkDuration_create; # introduced=VanillaIceCream
+    AWorkDuration_release; # introduced=VanillaIceCream
+    AWorkDuration_setWorkPeriodStartTimestampNanos; # introduced=VanillaIceCream
+    AWorkDuration_setActualTotalDurationNanos; # introduced=VanillaIceCream
+    AWorkDuration_setActualCpuDurationNanos; # introduced=VanillaIceCream
+    AWorkDuration_setActualGpuDurationNanos; # introduced=VanillaIceCream
   local:
     *;
 };
diff --git a/native/android/performance_hint.cpp b/native/android/performance_hint.cpp
index c25df6e..c4c8128 100644
--- a/native/android/performance_hint.cpp
+++ b/native/android/performance_hint.cpp
@@ -18,12 +18,14 @@
 
 #include <aidl/android/hardware/power/SessionHint.h>
 #include <aidl/android/hardware/power/SessionMode.h>
+#include <android/WorkDuration.h>
 #include <android/os/IHintManager.h>
 #include <android/os/IHintSession.h>
 #include <android/performance_hint.h>
 #include <binder/Binder.h>
 #include <binder/IBinder.h>
 #include <binder/IServiceManager.h>
+#include <inttypes.h>
 #include <performance_hint_private.h>
 #include <utils/SystemClock.h>
 
@@ -75,10 +77,13 @@
     int setThreads(const int32_t* threadIds, size_t size);
     int getThreadIds(int32_t* const threadIds, size_t* size);
     int setPreferPowerEfficiency(bool enabled);
+    int reportActualWorkDuration(AWorkDuration* workDuration);
 
 private:
     friend struct APerformanceHintManager;
 
+    int reportActualWorkDurationInternal(WorkDuration* workDuration);
+
     sp<IHintManager> mHintManager;
     sp<IHintSession> mHintSession;
     // HAL preferred update rate
@@ -92,8 +97,7 @@
     // 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;
+    std::vector<WorkDuration> mActualWorkDurations;
 };
 
 static IHintManager* gIHintManagerForTesting = nullptr;
@@ -195,8 +199,7 @@
      * Most of the workload is target_duration dependent, so now clear the cached samples
      * as they are most likely obsolete.
      */
-    mActualDurationsNanos.clear();
-    mTimestampsNanos.clear();
+    mActualWorkDurations.clear();
     mFirstTargetMetTimestamp = 0;
     mLastTargetMetTimestamp = 0;
     return 0;
@@ -207,43 +210,10 @@
         ALOGE("%s: actualDurationNanos must be positive", __FUNCTION__);
         return EINVAL;
     }
-    int64_t now = elapsedRealtimeNano();
-    mActualDurationsNanos.push_back(actualDurationNanos);
-    mTimestampsNanos.push_back(now);
 
-    if (actualDurationNanos >= mTargetDurationNanos) {
-        // Reset timestamps if we are equal or over the target.
-        mFirstTargetMetTimestamp = 0;
-    } else {
-        // Set mFirstTargetMetTimestamp for first time meeting target.
-        if (!mFirstTargetMetTimestamp || !mLastTargetMetTimestamp ||
-            (now - mLastTargetMetTimestamp > 2 * mPreferredRateNanos)) {
-            mFirstTargetMetTimestamp = now;
-        }
-        /**
-         * Rate limit the change if the update is over mPreferredRateNanos since first
-         * meeting target and less than mPreferredRateNanos since last meeting target.
-         */
-        if (now - mFirstTargetMetTimestamp > mPreferredRateNanos &&
-            now - mLastTargetMetTimestamp <= mPreferredRateNanos) {
-            return 0;
-        }
-        mLastTargetMetTimestamp = now;
-    }
+    WorkDuration workDuration(0, actualDurationNanos, actualDurationNanos, 0);
 
-    binder::Status ret =
-            mHintSession->reportActualWorkDuration(mActualDurationsNanos, mTimestampsNanos);
-    if (!ret.isOk()) {
-        ALOGE("%s: HintSession reportActualWorkDuration failed: %s", __FUNCTION__,
-              ret.exceptionMessage().c_str());
-        mFirstTargetMetTimestamp = 0;
-        mLastTargetMetTimestamp = 0;
-        return EPIPE;
-    }
-    mActualDurationsNanos.clear();
-    mTimestampsNanos.clear();
-
-    return 0;
+    return reportActualWorkDurationInternal(&workDuration);
 }
 
 int APerformanceHintSession::sendHint(SessionHint hint) {
@@ -322,6 +292,67 @@
     return OK;
 }
 
+int APerformanceHintSession::reportActualWorkDuration(AWorkDuration* aWorkDuration) {
+    WorkDuration* workDuration = static_cast<WorkDuration*>(aWorkDuration);
+    if (workDuration->workPeriodStartTimestampNanos <= 0) {
+        ALOGE("%s: workPeriodStartTimestampNanos must be positive", __FUNCTION__);
+        return EINVAL;
+    }
+    if (workDuration->actualTotalDurationNanos <= 0) {
+        ALOGE("%s: actualDurationNanos must be positive", __FUNCTION__);
+        return EINVAL;
+    }
+    if (workDuration->actualCpuDurationNanos <= 0) {
+        ALOGE("%s: cpuDurationNanos must be positive", __FUNCTION__);
+        return EINVAL;
+    }
+    if (workDuration->actualGpuDurationNanos < 0) {
+        ALOGE("%s: gpuDurationNanos must be non negative", __FUNCTION__);
+        return EINVAL;
+    }
+
+    return reportActualWorkDurationInternal(workDuration);
+}
+
+int APerformanceHintSession::reportActualWorkDurationInternal(WorkDuration* workDuration) {
+    int64_t actualTotalDurationNanos = workDuration->actualTotalDurationNanos;
+    int64_t now = uptimeNanos();
+    workDuration->timestampNanos = now;
+    mActualWorkDurations.push_back(std::move(*workDuration));
+
+    if (actualTotalDurationNanos >= mTargetDurationNanos) {
+        // Reset timestamps if we are equal or over the target.
+        mFirstTargetMetTimestamp = 0;
+    } else {
+        // Set mFirstTargetMetTimestamp for first time meeting target.
+        if (!mFirstTargetMetTimestamp || !mLastTargetMetTimestamp ||
+            (now - mLastTargetMetTimestamp > 2 * mPreferredRateNanos)) {
+            mFirstTargetMetTimestamp = now;
+        }
+        /**
+         * Rate limit the change if the update is over mPreferredRateNanos since first
+         * meeting target and less than mPreferredRateNanos since last meeting target.
+         */
+        if (now - mFirstTargetMetTimestamp > mPreferredRateNanos &&
+            now - mLastTargetMetTimestamp <= mPreferredRateNanos) {
+            return 0;
+        }
+        mLastTargetMetTimestamp = now;
+    }
+
+    binder::Status ret = mHintSession->reportActualWorkDuration2(mActualWorkDurations);
+    if (!ret.isOk()) {
+        ALOGE("%s: HintSession reportActualWorkDuration failed: %s", __FUNCTION__,
+              ret.exceptionMessage().c_str());
+        mFirstTargetMetTimestamp = 0;
+        mLastTargetMetTimestamp = 0;
+        return ret.exceptionCode() == binder::Status::EX_ILLEGAL_ARGUMENT ? EINVAL : EPIPE;
+    }
+    mActualWorkDurations.clear();
+
+    return 0;
+}
+
 // ===================================== C API
 APerformanceHintManager* APerformanceHint_getManager() {
     return APerformanceHintManager::getInstance();
@@ -376,6 +407,64 @@
     return session->setPreferPowerEfficiency(enabled);
 }
 
+int APerformanceHint_reportActualWorkDuration2(APerformanceHintSession* session,
+                                               AWorkDuration* workDuration) {
+    if (session == nullptr || workDuration == nullptr) {
+        ALOGE("Invalid value: (session %p, workDuration %p)", session, workDuration);
+        return EINVAL;
+    }
+    return session->reportActualWorkDuration(workDuration);
+}
+
+AWorkDuration* AWorkDuration_create() {
+    WorkDuration* workDuration = new WorkDuration();
+    return static_cast<AWorkDuration*>(workDuration);
+}
+
+void AWorkDuration_release(AWorkDuration* aWorkDuration) {
+    if (aWorkDuration == nullptr) {
+        ALOGE("%s: aWorkDuration is nullptr", __FUNCTION__);
+    }
+    delete aWorkDuration;
+}
+
+void AWorkDuration_setWorkPeriodStartTimestampNanos(AWorkDuration* aWorkDuration,
+                                                    int64_t workPeriodStartTimestampNanos) {
+    if (aWorkDuration == nullptr || workPeriodStartTimestampNanos <= 0) {
+        ALOGE("%s: Invalid value. (AWorkDuration: %p, workPeriodStartTimestampNanos: %" PRIi64 ")",
+              __FUNCTION__, aWorkDuration, workPeriodStartTimestampNanos);
+    }
+    static_cast<WorkDuration*>(aWorkDuration)->workPeriodStartTimestampNanos =
+            workPeriodStartTimestampNanos;
+}
+
+void AWorkDuration_setActualTotalDurationNanos(AWorkDuration* aWorkDuration,
+                                               int64_t actualTotalDurationNanos) {
+    if (aWorkDuration == nullptr || actualTotalDurationNanos <= 0) {
+        ALOGE("%s: Invalid value. (AWorkDuration: %p, actualTotalDurationNanos: %" PRIi64 ")",
+              __FUNCTION__, aWorkDuration, actualTotalDurationNanos);
+    }
+    static_cast<WorkDuration*>(aWorkDuration)->actualTotalDurationNanos = actualTotalDurationNanos;
+}
+
+void AWorkDuration_setActualCpuDurationNanos(AWorkDuration* aWorkDuration,
+                                             int64_t actualCpuDurationNanos) {
+    if (aWorkDuration == nullptr || actualCpuDurationNanos <= 0) {
+        ALOGE("%s: Invalid value. (AWorkDuration: %p, actualCpuDurationNanos: %" PRIi64 ")",
+              __FUNCTION__, aWorkDuration, actualCpuDurationNanos);
+    }
+    static_cast<WorkDuration*>(aWorkDuration)->actualCpuDurationNanos = actualCpuDurationNanos;
+}
+
+void AWorkDuration_setActualGpuDurationNanos(AWorkDuration* aWorkDuration,
+                                             int64_t actualGpuDurationNanos) {
+    if (aWorkDuration == nullptr || actualGpuDurationNanos < 0) {
+        ALOGE("%s: Invalid value. (AWorkDuration: %p, actualGpuDurationNanos: %" PRIi64 ")",
+              __FUNCTION__, aWorkDuration, actualGpuDurationNanos);
+    }
+    static_cast<WorkDuration*>(aWorkDuration)->actualGpuDurationNanos = actualGpuDurationNanos;
+}
+
 void APerformanceHint_setIHintManagerForTesting(void* iManager) {
     delete gHintManagerForTesting;
     gHintManagerForTesting = nullptr;
diff --git a/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp b/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp
index 22d33b1..4553b49 100644
--- a/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp
+++ b/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp
@@ -16,6 +16,7 @@
 
 #define LOG_TAG "PerformanceHintNativeTest"
 
+#include <android/WorkDuration.h>
 #include <android/os/IHintManager.h>
 #include <android/os/IHintSession.h>
 #include <android/performance_hint.h>
@@ -60,6 +61,8 @@
     MOCK_METHOD(Status, setMode, (int32_t mode, bool enabled), (override));
     MOCK_METHOD(Status, close, (), (override));
     MOCK_METHOD(IBinder*, onAsBinder, (), (override));
+    MOCK_METHOD(Status, reportActualWorkDuration2,
+                (const ::std::vector<android::os::WorkDuration>& workDurations), (override));
 };
 
 class PerformanceHintTest : public Test {
@@ -120,6 +123,7 @@
     std::vector<int64_t> actualDurations;
     actualDurations.push_back(20);
     EXPECT_CALL(*iSession, reportActualWorkDuration(Eq(actualDurations), _)).Times(Exactly(1));
+    EXPECT_CALL(*iSession, reportActualWorkDuration2(_)).Times(Exactly(1));
     result = APerformanceHint_reportActualWorkDuration(session, actualDurationNanos);
     EXPECT_EQ(0, result);
 
@@ -238,4 +242,125 @@
     APerformanceHintSession* session =
             APerformanceHint_createSession(manager, tids.data(), tids.size(), targetDuration);
     ASSERT_TRUE(session);
-}
\ No newline at end of file
+}
+
+MATCHER_P(WorkDurationEq, expected, "") {
+    if (arg.size() != expected.size()) {
+        *result_listener << "WorkDuration vectors are different sizes. Expected: "
+                         << expected.size() << ", Actual: " << arg.size();
+        return false;
+    }
+    for (int i = 0; i < expected.size(); ++i) {
+        android::os::WorkDuration expectedWorkDuration = expected[i];
+        android::os::WorkDuration actualWorkDuration = arg[i];
+        if (!expectedWorkDuration.equalsWithoutTimestamp(actualWorkDuration)) {
+            *result_listener << "WorkDuration at [" << i << "] is different: "
+                             << "Expected: " << expectedWorkDuration
+                             << ", Actual: " << actualWorkDuration;
+            return false;
+        }
+    }
+    return true;
+}
+
+TEST_F(PerformanceHintTest, TestAPerformanceHint_reportActualWorkDuration2) {
+    APerformanceHintManager* manager = createManager();
+
+    std::vector<int32_t> tids;
+    tids.push_back(1);
+    tids.push_back(2);
+    int64_t targetDuration = 56789L;
+
+    StrictMock<MockIHintSession>* iSession = new StrictMock<MockIHintSession>();
+    sp<IHintSession> session_sp(iSession);
+
+    EXPECT_CALL(*mMockIHintManager, createHintSession(_, Eq(tids), Eq(targetDuration), _))
+            .Times(Exactly(1))
+            .WillRepeatedly(DoAll(SetArgPointee<3>(std::move(session_sp)), Return(Status())));
+
+    APerformanceHintSession* session =
+            APerformanceHint_createSession(manager, tids.data(), tids.size(), targetDuration);
+    ASSERT_TRUE(session);
+
+    int64_t targetDurationNanos = 10;
+    EXPECT_CALL(*iSession, updateTargetWorkDuration(Eq(targetDurationNanos))).Times(Exactly(1));
+    int result = APerformanceHint_updateTargetWorkDuration(session, targetDurationNanos);
+    EXPECT_EQ(0, result);
+
+    usleep(2); // Sleep for longer than preferredUpdateRateNanos.
+    {
+        std::vector<android::os::WorkDuration> actualWorkDurations;
+        android::os::WorkDuration workDuration(1, 20, 13, 8);
+        actualWorkDurations.push_back(workDuration);
+
+        EXPECT_CALL(*iSession, reportActualWorkDuration2(WorkDurationEq(actualWorkDurations)))
+                .Times(Exactly(1));
+        result = APerformanceHint_reportActualWorkDuration2(session,
+                                                            static_cast<AWorkDuration*>(
+                                                                    &workDuration));
+        EXPECT_EQ(0, result);
+    }
+
+    {
+        std::vector<android::os::WorkDuration> actualWorkDurations;
+        android::os::WorkDuration workDuration(-1, 20, 13, 8);
+        actualWorkDurations.push_back(workDuration);
+
+        EXPECT_CALL(*iSession, reportActualWorkDuration2(WorkDurationEq(actualWorkDurations)))
+                .Times(Exactly(1));
+        result = APerformanceHint_reportActualWorkDuration2(session,
+                                                            static_cast<AWorkDuration*>(
+                                                                    &workDuration));
+        EXPECT_EQ(22, result);
+    }
+    {
+        std::vector<android::os::WorkDuration> actualWorkDurations;
+        android::os::WorkDuration workDuration(1, -20, 13, 8);
+        actualWorkDurations.push_back(workDuration);
+
+        EXPECT_CALL(*iSession, reportActualWorkDuration2(WorkDurationEq(actualWorkDurations)))
+                .Times(Exactly(1));
+        result = APerformanceHint_reportActualWorkDuration2(session,
+                                                            static_cast<AWorkDuration*>(
+                                                                    &workDuration));
+        EXPECT_EQ(22, result);
+    }
+    {
+        std::vector<android::os::WorkDuration> actualWorkDurations;
+        android::os::WorkDuration workDuration(1, 20, -13, 8);
+        actualWorkDurations.push_back(workDuration);
+
+        EXPECT_CALL(*iSession, reportActualWorkDuration2(WorkDurationEq(actualWorkDurations)))
+                .Times(Exactly(1));
+        result = APerformanceHint_reportActualWorkDuration2(session,
+                                                            static_cast<AWorkDuration*>(
+                                                                    &workDuration));
+        EXPECT_EQ(EINVAL, result);
+    }
+    {
+        std::vector<android::os::WorkDuration> actualWorkDurations;
+        android::os::WorkDuration workDuration(1, 20, 13, -8);
+        actualWorkDurations.push_back(workDuration);
+
+        EXPECT_CALL(*iSession, reportActualWorkDuration2(WorkDurationEq(actualWorkDurations)))
+                .Times(Exactly(1));
+        result = APerformanceHint_reportActualWorkDuration2(session,
+                                                            static_cast<AWorkDuration*>(
+                                                                    &workDuration));
+        EXPECT_EQ(EINVAL, result);
+    }
+
+    EXPECT_CALL(*iSession, close()).Times(Exactly(1));
+    APerformanceHint_closeSession(session);
+}
+
+TEST_F(PerformanceHintTest, TestAWorkDuration) {
+    AWorkDuration* aWorkDuration = AWorkDuration_create();
+    ASSERT_NE(aWorkDuration, nullptr);
+
+    AWorkDuration_setWorkPeriodStartTimestampNanos(aWorkDuration, 1);
+    AWorkDuration_setActualTotalDurationNanos(aWorkDuration, 20);
+    AWorkDuration_setActualCpuDurationNanos(aWorkDuration, 13);
+    AWorkDuration_setActualGpuDurationNanos(aWorkDuration, 8);
+    AWorkDuration_release(aWorkDuration);
+}