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/core/api/current.txt b/core/api/current.txt
index 3f4a34b..e7ca0c5 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -33137,6 +33137,7 @@
public static class PerformanceHintManager.Session implements java.io.Closeable {
method public void close();
method public void reportActualWorkDuration(long);
+ method @FlaggedApi("android.os.adpf_gpu_report_actual_work_duration") public void reportActualWorkDuration(@NonNull android.os.WorkDuration);
method @FlaggedApi("android.os.adpf_prefer_power_efficiency") public void setPreferPowerEfficiency(boolean);
method public void setThreads(@NonNull int[]);
method public void updateTargetWorkDuration(long);
@@ -33478,6 +33479,7 @@
method public static boolean setCurrentTimeMillis(long);
method public static void sleep(long);
method public static long uptimeMillis();
+ method @FlaggedApi("android.os.adpf_gpu_report_actual_work_duration") public static long uptimeNanos();
}
public class TestLooperManager {
@@ -33743,6 +33745,22 @@
method @RequiresPermission(android.Manifest.permission.VIBRATE) public final void vibrate(@NonNull android.os.CombinedVibration, @Nullable android.os.VibrationAttributes);
}
+ @FlaggedApi("android.os.adpf_gpu_report_actual_work_duration") public final class WorkDuration implements android.os.Parcelable {
+ ctor public WorkDuration();
+ ctor public WorkDuration(long, long, long, long);
+ method public int describeContents();
+ method public long getActualCpuDurationNanos();
+ method public long getActualGpuDurationNanos();
+ method public long getActualTotalDurationNanos();
+ method public long getWorkPeriodStartTimestampNanos();
+ method public void setActualCpuDurationNanos(long);
+ method public void setActualGpuDurationNanos(long);
+ method public void setActualTotalDurationNanos(long);
+ method public void setWorkPeriodStartTimestampNanos(long);
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.os.WorkDuration> CREATOR;
+ }
+
public class WorkSource implements android.os.Parcelable {
ctor public WorkSource();
ctor public WorkSource(android.os.WorkSource);
diff --git a/core/java/android/os/IHintSession.aidl b/core/java/android/os/IHintSession.aidl
index 6b43e73..fe85da2 100644
--- a/core/java/android/os/IHintSession.aidl
+++ b/core/java/android/os/IHintSession.aidl
@@ -17,6 +17,8 @@
package android.os;
+import android.os.WorkDuration;
+
/** {@hide} */
oneway interface IHintSession {
void updateTargetWorkDuration(long targetDurationNanos);
@@ -24,4 +26,5 @@
void close();
void sendHint(int hint);
void setMode(int mode, boolean enabled);
+ void reportActualWorkDuration2(in WorkDuration[] workDurations);
}
diff --git a/core/java/android/os/PerformanceHintManager.java b/core/java/android/os/PerformanceHintManager.java
index 11084b8..e005910 100644
--- a/core/java/android/os/PerformanceHintManager.java
+++ b/core/java/android/os/PerformanceHintManager.java
@@ -103,7 +103,7 @@
* Any call in this class will change its internal data, so you must do your own thread
* safety to protect from racing.
*
- * All timings should be in {@link SystemClock#elapsedRealtimeNanos()}.
+ * All timings should be in {@link SystemClock#uptimeNanos()}.
*/
public static class Session implements Closeable {
private long mNativeSessionPtr;
@@ -269,6 +269,40 @@
public @Nullable int[] getThreadIds() {
return nativeGetThreadIds(mNativeSessionPtr);
}
+
+ /**
+ * Reports the work duration for the last cycle of work.
+ *
+ * The system will attempt to adjust the core placement of the threads within the thread
+ * group and/or the frequency of the core on which they are run to bring the actual duration
+ * close to the target duration.
+ *
+ * @param workDuration the work duration of each component.
+ * @throws IllegalArgumentException if work period start timestamp is not positive, or
+ * actual total duration is not positive, or actual CPU duration is not positive,
+ * or actual GPU duration is negative.
+ */
+ @FlaggedApi(Flags.FLAG_ADPF_GPU_REPORT_ACTUAL_WORK_DURATION)
+ public void reportActualWorkDuration(@NonNull WorkDuration workDuration) {
+ if (workDuration.mWorkPeriodStartTimestampNanos <= 0) {
+ throw new IllegalArgumentException(
+ "the work period start timestamp should be positive.");
+ }
+ if (workDuration.mActualTotalDurationNanos <= 0) {
+ throw new IllegalArgumentException("the actual total duration should be positive.");
+ }
+ if (workDuration.mActualCpuDurationNanos <= 0) {
+ throw new IllegalArgumentException("the actual CPU duration should be positive.");
+ }
+ if (workDuration.mActualGpuDurationNanos < 0) {
+ throw new IllegalArgumentException(
+ "the actual GPU duration should be non negative.");
+ }
+ nativeReportActualWorkDuration(mNativeSessionPtr,
+ workDuration.mWorkPeriodStartTimestampNanos,
+ workDuration.mActualTotalDurationNanos,
+ workDuration.mActualCpuDurationNanos, workDuration.mActualGpuDurationNanos);
+ }
}
private static native long nativeAcquireManager();
@@ -285,4 +319,7 @@
private static native void nativeSetThreads(long nativeSessionPtr, int[] tids);
private static native void nativeSetPreferPowerEfficiency(long nativeSessionPtr,
boolean enabled);
+ private static native void nativeReportActualWorkDuration(long nativeSessionPtr,
+ long workPeriodStartTimestampNanos, long actualTotalDurationNanos,
+ long actualCpuDurationNanos, long actualGpuDurationNanos);
}
diff --git a/core/java/android/os/SystemClock.java b/core/java/android/os/SystemClock.java
index 831ca86..fb67cc0 100644
--- a/core/java/android/os/SystemClock.java
+++ b/core/java/android/os/SystemClock.java
@@ -16,6 +16,7 @@
package android.os;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.app.IAlarmManager;
import android.app.time.UnixEpochTime;
@@ -192,8 +193,8 @@
* Returns nanoseconds since boot, not counting time spent in deep sleep.
*
* @return nanoseconds of non-sleep uptime since boot.
- * @hide
*/
+ @FlaggedApi(Flags.FLAG_ADPF_GPU_REPORT_ACTUAL_WORK_DURATION)
@CriticalNative
public static native long uptimeNanos();
diff --git a/core/java/android/os/WorkDuration.aidl b/core/java/android/os/WorkDuration.aidl
new file mode 100644
index 0000000..0f61204
--- /dev/null
+++ b/core/java/android/os/WorkDuration.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2023 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.
+ */
+
+package android.os;
+
+parcelable WorkDuration cpp_header "android/WorkDuration.h";
\ No newline at end of file
diff --git a/core/java/android/os/WorkDuration.java b/core/java/android/os/WorkDuration.java
new file mode 100644
index 0000000..4fdc34f
--- /dev/null
+++ b/core/java/android/os/WorkDuration.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2023 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.
+ */
+
+package android.os;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+
+import java.util.Objects;
+
+/**
+ * WorkDuration contains the measured time in nano seconds of the workload
+ * in each component, see
+ * {@link PerformanceHintManager.Session#reportActualWorkDuration(WorkDuration)}.
+ *
+ * All timings should be in {@link SystemClock#elapsedRealtimeNanos()}.
+ */
+@FlaggedApi(Flags.FLAG_ADPF_GPU_REPORT_ACTUAL_WORK_DURATION)
+public final class WorkDuration implements Parcelable {
+ long mWorkPeriodStartTimestampNanos = 0;
+ long mActualTotalDurationNanos = 0;
+ long mActualCpuDurationNanos = 0;
+ long mActualGpuDurationNanos = 0;
+ long mTimestampNanos = 0;
+
+ public static final @NonNull Creator<WorkDuration> CREATOR = new Creator<>() {
+ @Override
+ public WorkDuration createFromParcel(Parcel in) {
+ return new WorkDuration(in);
+ }
+
+ @Override
+ public WorkDuration[] newArray(int size) {
+ return new WorkDuration[size];
+ }
+ };
+
+ public WorkDuration() {}
+
+ public WorkDuration(long workPeriodStartTimestampNanos,
+ long actualTotalDurationNanos,
+ long actualCpuDurationNanos,
+ long actualGpuDurationNanos) {
+ mWorkPeriodStartTimestampNanos = workPeriodStartTimestampNanos;
+ mActualTotalDurationNanos = actualTotalDurationNanos;
+ mActualCpuDurationNanos = actualCpuDurationNanos;
+ mActualGpuDurationNanos = actualGpuDurationNanos;
+ }
+
+ /**
+ * @hide
+ */
+ public WorkDuration(long workPeriodStartTimestampNanos,
+ long actualTotalDurationNanos,
+ long actualCpuDurationNanos,
+ long actualGpuDurationNanos,
+ long timestampNanos) {
+ mWorkPeriodStartTimestampNanos = workPeriodStartTimestampNanos;
+ mActualTotalDurationNanos = actualTotalDurationNanos;
+ mActualCpuDurationNanos = actualCpuDurationNanos;
+ mActualGpuDurationNanos = actualGpuDurationNanos;
+ mTimestampNanos = timestampNanos;
+ }
+
+ WorkDuration(@NonNull Parcel in) {
+ mWorkPeriodStartTimestampNanos = in.readLong();
+ mActualTotalDurationNanos = in.readLong();
+ mActualCpuDurationNanos = in.readLong();
+ mActualGpuDurationNanos = in.readLong();
+ mTimestampNanos = in.readLong();
+ }
+
+ /**
+ * Sets the work period start timestamp in nanoseconds.
+ *
+ * All timings should be in {@link SystemClock#elapsedRealtimeNanos()}.
+ */
+ public void setWorkPeriodStartTimestampNanos(long workPeriodStartTimestampNanos) {
+ if (workPeriodStartTimestampNanos <= 0) {
+ throw new IllegalArgumentException(
+ "the work period start timestamp should be positive.");
+ }
+ mWorkPeriodStartTimestampNanos = workPeriodStartTimestampNanos;
+ }
+
+ /**
+ * Sets the actual total duration in nanoseconds.
+ *
+ * All timings should be in {@link SystemClock#elapsedRealtimeNanos()}.
+ */
+ public void setActualTotalDurationNanos(long actualTotalDurationNanos) {
+ if (actualTotalDurationNanos <= 0) {
+ throw new IllegalArgumentException("the actual total duration should be positive.");
+ }
+ mActualTotalDurationNanos = actualTotalDurationNanos;
+ }
+
+ /**
+ * Sets the actual CPU duration in nanoseconds.
+ *
+ * All timings should be in {@link SystemClock#elapsedRealtimeNanos()}.
+ */
+ public void setActualCpuDurationNanos(long actualCpuDurationNanos) {
+ if (actualCpuDurationNanos <= 0) {
+ throw new IllegalArgumentException("the actual CPU duration should be positive.");
+ }
+ mActualCpuDurationNanos = actualCpuDurationNanos;
+ }
+
+ /**
+ * Sets the actual GPU duration in nanoseconds.
+ *
+ * All timings should be in {@link SystemClock#elapsedRealtimeNanos()}.
+ */
+ public void setActualGpuDurationNanos(long actualGpuDurationNanos) {
+ if (actualGpuDurationNanos < 0) {
+ throw new IllegalArgumentException("the actual GPU duration should be non negative.");
+ }
+ mActualGpuDurationNanos = actualGpuDurationNanos;
+ }
+
+ /**
+ * Returns the work period start timestamp based in nanoseconds.
+ *
+ * All timings should be in {@link SystemClock#elapsedRealtimeNanos()}.
+ */
+ public long getWorkPeriodStartTimestampNanos() {
+ return mWorkPeriodStartTimestampNanos;
+ }
+
+ /**
+ * Returns the actual total duration in nanoseconds.
+ *
+ * All timings should be in {@link SystemClock#elapsedRealtimeNanos()}.
+ */
+ public long getActualTotalDurationNanos() {
+ return mActualTotalDurationNanos;
+ }
+
+ /**
+ * Returns the actual CPU duration in nanoseconds.
+ *
+ * All timings should be in {@link SystemClock#elapsedRealtimeNanos()}.
+ */
+ public long getActualCpuDurationNanos() {
+ return mActualCpuDurationNanos;
+ }
+
+ /**
+ * Returns the actual GPU duration in nanoseconds.
+ *
+ * All timings should be in {@link SystemClock#elapsedRealtimeNanos()}.
+ */
+ public long getActualGpuDurationNanos() {
+ return mActualGpuDurationNanos;
+ }
+
+ /**
+ * @hide
+ */
+ public long getTimestampNanos() {
+ return mTimestampNanos;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeLong(mWorkPeriodStartTimestampNanos);
+ dest.writeLong(mActualTotalDurationNanos);
+ dest.writeLong(mActualCpuDurationNanos);
+ dest.writeLong(mActualGpuDurationNanos);
+ dest.writeLong(mTimestampNanos);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ }
+ if (!(obj instanceof WorkDuration)) {
+ return false;
+ }
+ WorkDuration workDuration = (WorkDuration) obj;
+ return workDuration.mTimestampNanos == this.mTimestampNanos
+ && workDuration.mWorkPeriodStartTimestampNanos == this.mWorkPeriodStartTimestampNanos
+ && workDuration.mActualTotalDurationNanos == this.mActualTotalDurationNanos
+ && workDuration.mActualCpuDurationNanos == this.mActualCpuDurationNanos
+ && workDuration.mActualGpuDurationNanos == this.mActualGpuDurationNanos;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mWorkPeriodStartTimestampNanos, mActualTotalDurationNanos,
+ mActualCpuDurationNanos, mActualGpuDurationNanos, mTimestampNanos);
+ }
+}
diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig
index 940ddf2..0809b3b 100644
--- a/core/java/android/os/flags.aconfig
+++ b/core/java/android/os/flags.aconfig
@@ -54,4 +54,11 @@
namespace: "backstage_power"
description: "Guards a new API in PowerManager to check if battery saver is supported or not."
bug: "305067031"
-}
\ No newline at end of file
+}
+
+flag {
+ name: "adpf_gpu_report_actual_work_duration"
+ namespace: "game"
+ description: "Guards the ADPF GPU APIs."
+ bug: "284324521"
+}
diff --git a/core/jni/android_os_PerformanceHintManager.cpp b/core/jni/android_os_PerformanceHintManager.cpp
index 95bf49f..aebe7ea 100644
--- a/core/jni/android_os_PerformanceHintManager.cpp
+++ b/core/jni/android_os_PerformanceHintManager.cpp
@@ -16,15 +16,16 @@
#define LOG_TAG "PerfHint-jni"
-#include "jni.h"
-
+#include <android/performance_hint.h>
#include <dlfcn.h>
#include <nativehelper/JNIHelp.h>
#include <nativehelper/ScopedPrimitiveArray.h>
#include <utils/Log.h>
+
#include <vector>
#include "core_jni_helpers.h"
+#include "jni.h"
namespace android {
@@ -44,6 +45,11 @@
typedef int (*APH_setThreads)(APerformanceHintSession*, const pid_t*, size_t);
typedef void (*APH_getThreadIds)(APerformanceHintSession*, int32_t* const, size_t* const);
typedef void (*APH_setPreferPowerEfficiency)(APerformanceHintSession*, bool);
+typedef void (*APH_reportActualWorkDuration2)(APerformanceHintSession*, AWorkDuration*);
+
+typedef AWorkDuration* (*AWD_create)();
+typedef void (*AWD_setTimeNanos)(AWorkDuration*, int64_t);
+typedef void (*AWD_release)(AWorkDuration*);
bool gAPerformanceHintBindingInitialized = false;
APH_getManager gAPH_getManagerFn = nullptr;
@@ -56,6 +62,14 @@
APH_setThreads gAPH_setThreadsFn = nullptr;
APH_getThreadIds gAPH_getThreadIdsFn = nullptr;
APH_setPreferPowerEfficiency gAPH_setPreferPowerEfficiencyFn = nullptr;
+APH_reportActualWorkDuration2 gAPH_reportActualWorkDuration2Fn = nullptr;
+
+AWD_create gAWD_createFn = nullptr;
+AWD_setTimeNanos gAWD_setWorkPeriodStartTimestampNanosFn = nullptr;
+AWD_setTimeNanos gAWD_setActualTotalDurationNanosFn = nullptr;
+AWD_setTimeNanos gAWD_setActualCpuDurationNanosFn = nullptr;
+AWD_setTimeNanos gAWD_setActualGpuDurationNanosFn = nullptr;
+AWD_release gAWD_releaseFn = nullptr;
void ensureAPerformanceHintBindingInitialized() {
if (gAPerformanceHintBindingInitialized) return;
@@ -112,9 +126,46 @@
(APH_setPreferPowerEfficiency)dlsym(handle_,
"APerformanceHint_setPreferPowerEfficiency");
LOG_ALWAYS_FATAL_IF(gAPH_setPreferPowerEfficiencyFn == nullptr,
- "Failed to find required symbol"
+ "Failed to find required symbol "
"APerformanceHint_setPreferPowerEfficiency!");
+ gAPH_reportActualWorkDuration2Fn =
+ (APH_reportActualWorkDuration2)dlsym(handle_,
+ "APerformanceHint_reportActualWorkDuration2");
+ LOG_ALWAYS_FATAL_IF(gAPH_reportActualWorkDuration2Fn == nullptr,
+ "Failed to find required symbol "
+ "APerformanceHint_reportActualWorkDuration2!");
+
+ gAWD_createFn = (AWD_create)dlsym(handle_, "AWorkDuration_create");
+ LOG_ALWAYS_FATAL_IF(gAWD_createFn == nullptr,
+ "Failed to find required symbol AWorkDuration_create!");
+
+ gAWD_setWorkPeriodStartTimestampNanosFn =
+ (AWD_setTimeNanos)dlsym(handle_, "AWorkDuration_setWorkPeriodStartTimestampNanos");
+ LOG_ALWAYS_FATAL_IF(gAWD_setWorkPeriodStartTimestampNanosFn == nullptr,
+ "Failed to find required symbol "
+ "AWorkDuration_setWorkPeriodStartTimestampNanos!");
+
+ gAWD_setActualTotalDurationNanosFn =
+ (AWD_setTimeNanos)dlsym(handle_, "AWorkDuration_setActualTotalDurationNanos");
+ LOG_ALWAYS_FATAL_IF(gAWD_setActualTotalDurationNanosFn == nullptr,
+ "Failed to find required symbol "
+ "AWorkDuration_setActualTotalDurationNanos!");
+
+ gAWD_setActualCpuDurationNanosFn =
+ (AWD_setTimeNanos)dlsym(handle_, "AWorkDuration_setActualCpuDurationNanos");
+ LOG_ALWAYS_FATAL_IF(gAWD_setActualCpuDurationNanosFn == nullptr,
+ "Failed to find required symbol AWorkDuration_setActualCpuDurationNanos!");
+
+ gAWD_setActualGpuDurationNanosFn =
+ (AWD_setTimeNanos)dlsym(handle_, "AWorkDuration_setActualGpuDurationNanos");
+ LOG_ALWAYS_FATAL_IF(gAWD_setActualGpuDurationNanosFn == nullptr,
+ "Failed to find required symbol AWorkDuration_setActualGpuDurationNanos!");
+
+ gAWD_releaseFn = (AWD_release)dlsym(handle_, "AWorkDuration_release");
+ LOG_ALWAYS_FATAL_IF(gAWD_releaseFn == nullptr,
+ "Failed to find required symbol AWorkDuration_release!");
+
gAPerformanceHintBindingInitialized = true;
}
@@ -238,6 +289,25 @@
enabled);
}
+static void nativeReportActualWorkDuration2(JNIEnv* env, jclass clazz, jlong nativeSessionPtr,
+ jlong workPeriodStartTimestampNanos,
+ jlong actualTotalDurationNanos,
+ jlong actualCpuDurationNanos,
+ jlong actualGpuDurationNanos) {
+ ensureAPerformanceHintBindingInitialized();
+
+ AWorkDuration* workDuration = gAWD_createFn();
+ gAWD_setWorkPeriodStartTimestampNanosFn(workDuration, workPeriodStartTimestampNanos);
+ gAWD_setActualTotalDurationNanosFn(workDuration, actualTotalDurationNanos);
+ gAWD_setActualCpuDurationNanosFn(workDuration, actualCpuDurationNanos);
+ gAWD_setActualGpuDurationNanosFn(workDuration, actualGpuDurationNanos);
+
+ gAPH_reportActualWorkDuration2Fn(reinterpret_cast<APerformanceHintSession*>(nativeSessionPtr),
+ workDuration);
+
+ gAWD_releaseFn(workDuration);
+}
+
static const JNINativeMethod gPerformanceHintMethods[] = {
{"nativeAcquireManager", "()J", (void*)nativeAcquireManager},
{"nativeGetPreferredUpdateRateNanos", "(J)J", (void*)nativeGetPreferredUpdateRateNanos},
@@ -249,6 +319,7 @@
{"nativeSetThreads", "(J[I)V", (void*)nativeSetThreads},
{"nativeGetThreadIds", "(J)[I", (void*)nativeGetThreadIds},
{"nativeSetPreferPowerEfficiency", "(JZ)V", (void*)nativeSetPreferPowerEfficiency},
+ {"nativeReportActualWorkDuration", "(JJJJJ)V", (void*)nativeReportActualWorkDuration2},
};
int register_android_os_PerformanceHintManager(JNIEnv* env) {
diff --git a/core/tests/coretests/src/android/os/PerformanceHintManagerTest.java b/core/tests/coretests/src/android/os/PerformanceHintManagerTest.java
index 20ba427..9b4dec4 100644
--- a/core/tests/coretests/src/android/os/PerformanceHintManagerTest.java
+++ b/core/tests/coretests/src/android/os/PerformanceHintManagerTest.java
@@ -182,4 +182,42 @@
s.setPreferPowerEfficiency(true);
s.setPreferPowerEfficiency(true);
}
+
+ @Test
+ public void testReportActualWorkDurationWithWorkDurationClass() {
+ Session s = createSession();
+ assumeNotNull(s);
+ s.updateTargetWorkDuration(16);
+ s.reportActualWorkDuration(new WorkDuration(1, 12, 8, 6));
+ s.reportActualWorkDuration(new WorkDuration(1, 33, 14, 20));
+ s.reportActualWorkDuration(new WorkDuration(1, 14, 10, 6));
+ }
+
+ @Test
+ public void testReportActualWorkDurationWithWorkDurationClass_IllegalArgument() {
+ Session s = createSession();
+ assumeNotNull(s);
+ s.updateTargetWorkDuration(16);
+ assertThrows(IllegalArgumentException.class, () -> {
+ s.reportActualWorkDuration(new WorkDuration(-1, 12, 8, 6));
+ });
+ assertThrows(IllegalArgumentException.class, () -> {
+ s.reportActualWorkDuration(new WorkDuration(0, 12, 8, 6));
+ });
+ assertThrows(IllegalArgumentException.class, () -> {
+ s.reportActualWorkDuration(new WorkDuration(1, -1, 8, 6));
+ });
+ assertThrows(IllegalArgumentException.class, () -> {
+ s.reportActualWorkDuration(new WorkDuration(1, 0, 8, 6));
+ });
+ assertThrows(IllegalArgumentException.class, () -> {
+ s.reportActualWorkDuration(new WorkDuration(1, 12, -1, 6));
+ });
+ assertThrows(IllegalArgumentException.class, () -> {
+ s.reportActualWorkDuration(new WorkDuration(1, 12, 0, 6));
+ });
+ assertThrows(IllegalArgumentException.class, () -> {
+ s.reportActualWorkDuration(new WorkDuration(1, 12, 8, -1));
+ });
+ }
}
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);
+}
diff --git a/services/core/java/com/android/server/power/hint/HintManagerService.java b/services/core/java/com/android/server/power/hint/HintManagerService.java
index dd39fb0..ee3b746 100644
--- a/services/core/java/com/android/server/power/hint/HintManagerService.java
+++ b/services/core/java/com/android/server/power/hint/HintManagerService.java
@@ -32,6 +32,8 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemProperties;
+import android.os.WorkDuration;
+import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.SparseIntArray;
@@ -195,6 +197,9 @@
private static native void nativeSetMode(long halPtr, int mode, boolean enabled);
+ private static native void nativeReportActualWorkDuration(long halPtr,
+ WorkDuration[] workDurations);
+
/** Wrapper for HintManager.nativeInit */
public void halInit() {
nativeInit();
@@ -252,6 +257,10 @@
nativeSetMode(halPtr, mode, enabled);
}
+ /** Wrapper for HintManager.nativeReportActualWorkDuration */
+ public void halReportActualWorkDuration(long halPtr, WorkDuration[] workDurations) {
+ nativeReportActualWorkDuration(halPtr, workDurations);
+ }
}
@VisibleForTesting
@@ -624,6 +633,52 @@
}
}
+ @Override
+ public void reportActualWorkDuration2(WorkDuration[] workDurations) {
+ synchronized (this) {
+ if (mHalSessionPtr == 0 || !mUpdateAllowed) {
+ return;
+ }
+ Preconditions.checkArgument(workDurations.length != 0, "the count"
+ + " of work durations shouldn't be 0.");
+ for (WorkDuration workDuration : workDurations) {
+ validateWorkDuration(workDuration);
+ }
+ mNativeWrapper.halReportActualWorkDuration(mHalSessionPtr, workDurations);
+ }
+ }
+
+ void validateWorkDuration(WorkDuration workDuration) {
+ if (DEBUG) {
+ Slogf.d(TAG, "WorkDuration(" + workDuration.getTimestampNanos() + ", "
+ + workDuration.getWorkPeriodStartTimestampNanos() + ", "
+ + workDuration.getActualTotalDurationNanos() + ", "
+ + workDuration.getActualCpuDurationNanos() + ", "
+ + workDuration.getActualGpuDurationNanos() + ")");
+ }
+ if (workDuration.getWorkPeriodStartTimestampNanos() <= 0) {
+ throw new IllegalArgumentException(
+ TextUtils.formatSimple(
+ "Work period start timestamp (%d) should be greater than 0",
+ workDuration.getWorkPeriodStartTimestampNanos()));
+ }
+ if (workDuration.getActualTotalDurationNanos() <= 0) {
+ throw new IllegalArgumentException(
+ TextUtils.formatSimple("Actual total duration (%d) should be greater than 0",
+ workDuration.getActualTotalDurationNanos()));
+ }
+ if (workDuration.getActualCpuDurationNanos() <= 0) {
+ throw new IllegalArgumentException(
+ TextUtils.formatSimple("Actual CPU duration (%d) should be greater than 0",
+ workDuration.getActualCpuDurationNanos()));
+ }
+ if (workDuration.getActualGpuDurationNanos() < 0) {
+ throw new IllegalArgumentException(
+ TextUtils.formatSimple("Actual GPU duration (%d) should be non negative",
+ workDuration.getActualGpuDurationNanos()));
+ }
+ }
+
private void onProcStateChanged(boolean updateAllowed) {
updateHintAllowed(updateAllowed);
}
diff --git a/services/core/jni/com_android_server_hint_HintManagerService.cpp b/services/core/jni/com_android_server_hint_HintManagerService.cpp
index 7edf445..ccd9bd0 100644
--- a/services/core/jni/com_android_server_hint_HintManagerService.cpp
+++ b/services/core/jni/com_android_server_hint_HintManagerService.cpp
@@ -20,6 +20,7 @@
#include <aidl/android/hardware/power/IPower.h>
#include <android-base/stringprintf.h>
+#include <inttypes.h>
#include <nativehelper/JNIHelp.h>
#include <nativehelper/ScopedPrimitiveArray.h>
#include <powermanager/PowerHalController.h>
@@ -38,6 +39,15 @@
namespace android {
+static struct {
+ jclass clazz{};
+ jfieldID workPeriodStartTimestampNanos{};
+ jfieldID actualTotalDurationNanos{};
+ jfieldID actualCpuDurationNanos{};
+ jfieldID actualGpuDurationNanos{};
+ jfieldID timestampNanos{};
+} gWorkDurationInfo;
+
static power::PowerHalController gPowerHalController;
static std::unordered_map<jlong, std::shared_ptr<IPowerHintSession>> gSessionMap;
static std::mutex gSessionMapLock;
@@ -180,6 +190,26 @@
setMode(session_ptr, static_cast<SessionMode>(mode), enabled);
}
+static void nativeReportActualWorkDuration2(JNIEnv* env, jclass /* clazz */, jlong session_ptr,
+ jobjectArray jWorkDurations) {
+ int size = env->GetArrayLength(jWorkDurations);
+ std::vector<WorkDuration> workDurations(size);
+ for (int i = 0; i < size; i++) {
+ jobject workDuration = env->GetObjectArrayElement(jWorkDurations, i);
+ workDurations[i].workPeriodStartTimestampNanos =
+ env->GetLongField(workDuration, gWorkDurationInfo.workPeriodStartTimestampNanos);
+ workDurations[i].durationNanos =
+ env->GetLongField(workDuration, gWorkDurationInfo.actualTotalDurationNanos);
+ workDurations[i].cpuDurationNanos =
+ env->GetLongField(workDuration, gWorkDurationInfo.actualCpuDurationNanos);
+ workDurations[i].gpuDurationNanos =
+ env->GetLongField(workDuration, gWorkDurationInfo.actualGpuDurationNanos);
+ workDurations[i].timeStampNanos =
+ env->GetLongField(workDuration, gWorkDurationInfo.timestampNanos);
+ }
+ reportActualWorkDuration(session_ptr, workDurations);
+}
+
// ----------------------------------------------------------------------------
static const JNINativeMethod sHintManagerServiceMethods[] = {
/* name, signature, funcPtr */
@@ -194,9 +224,23 @@
{"nativeSendHint", "(JI)V", (void*)nativeSendHint},
{"nativeSetThreads", "(J[I)V", (void*)nativeSetThreads},
{"nativeSetMode", "(JIZ)V", (void*)nativeSetMode},
+ {"nativeReportActualWorkDuration", "(J[Landroid/os/WorkDuration;)V",
+ (void*)nativeReportActualWorkDuration2},
};
int register_android_server_HintManagerService(JNIEnv* env) {
+ gWorkDurationInfo.clazz = env->FindClass("android/os/WorkDuration");
+ gWorkDurationInfo.workPeriodStartTimestampNanos =
+ env->GetFieldID(gWorkDurationInfo.clazz, "mWorkPeriodStartTimestampNanos", "J");
+ gWorkDurationInfo.actualTotalDurationNanos =
+ env->GetFieldID(gWorkDurationInfo.clazz, "mActualTotalDurationNanos", "J");
+ gWorkDurationInfo.actualCpuDurationNanos =
+ env->GetFieldID(gWorkDurationInfo.clazz, "mActualCpuDurationNanos", "J");
+ gWorkDurationInfo.actualGpuDurationNanos =
+ env->GetFieldID(gWorkDurationInfo.clazz, "mActualGpuDurationNanos", "J");
+ gWorkDurationInfo.timestampNanos =
+ env->GetFieldID(gWorkDurationInfo.clazz, "mTimestampNanos", "J");
+
return jniRegisterNativeMethods(env,
"com/android/server/power/hint/"
"HintManagerService$NativeWrapper",
diff --git a/services/tests/servicestests/src/com/android/server/power/hint/HintManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/hint/HintManagerServiceTest.java
index d09aa89..3748527 100644
--- a/services/tests/servicestests/src/com/android/server/power/hint/HintManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/hint/HintManagerServiceTest.java
@@ -44,6 +44,7 @@
import android.os.IHintSession;
import android.os.PerformanceHintManager;
import android.os.Process;
+import android.os.WorkDuration;
import android.util.Log;
import com.android.server.FgThread;
@@ -89,6 +90,11 @@
private static final long[] DURATIONS_ZERO = new long[] {};
private static final long[] TIMESTAMPS_ZERO = new long[] {};
private static final long[] TIMESTAMPS_TWO = new long[] {1L, 2L};
+ private static final WorkDuration[] WORK_DURATIONS_THREE = new WorkDuration[] {
+ new WorkDuration(1L, 11L, 8L, 4L, 1L),
+ new WorkDuration(2L, 13L, 8L, 6L, 2L),
+ new WorkDuration(3L, 333333333L, 8L, 333333333L, 3L),
+ };
@Mock private Context mContext;
@Mock private HintManagerService.NativeWrapper mNativeWrapperMock;
@@ -593,4 +599,55 @@
}
a.close();
}
+
+ @Test
+ public void testReportActualWorkDuration2() throws Exception {
+ HintManagerService service = createService();
+ IBinder token = new Binder();
+
+ AppHintSession a = (AppHintSession) service.getBinderServiceInstance()
+ .createHintSession(token, SESSION_TIDS_A, DEFAULT_TARGET_DURATION);
+
+ a.updateTargetWorkDuration(100L);
+ a.reportActualWorkDuration2(WORK_DURATIONS_THREE);
+ verify(mNativeWrapperMock, times(1)).halReportActualWorkDuration(anyLong(),
+ eq(WORK_DURATIONS_THREE));
+
+ assertThrows(IllegalArgumentException.class, () -> {
+ a.reportActualWorkDuration2(new WorkDuration[] {});
+ });
+
+ assertThrows(IllegalArgumentException.class, () -> {
+ a.reportActualWorkDuration2(new WorkDuration[] {new WorkDuration(0L, 11L, 8L, 4L, 1L)});
+ });
+
+ assertThrows(IllegalArgumentException.class, () -> {
+ a.reportActualWorkDuration2(new WorkDuration[] {new WorkDuration(1L, 0L, 8L, 4L, 1L)});
+ });
+
+ assertThrows(IllegalArgumentException.class, () -> {
+ a.reportActualWorkDuration2(new WorkDuration[] {new WorkDuration(1L, 11L, 0L, 4L, 1L)});
+ });
+
+ assertThrows(IllegalArgumentException.class, () -> {
+ a.reportActualWorkDuration2(
+ new WorkDuration[] {new WorkDuration(1L, 11L, 8L, -1L, 1L)});
+ });
+
+ reset(mNativeWrapperMock);
+ // Set session to background, then the duration would not be updated.
+ service.mUidObserver.onUidStateChanged(
+ a.mUid, ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND, 0, 0);
+
+ // Using CountDownLatch to ensure above onUidStateChanged() job was digested.
+ final CountDownLatch latch = new CountDownLatch(1);
+ FgThread.getHandler().post(() -> {
+ latch.countDown();
+ });
+ latch.await();
+
+ assertFalse(service.mUidObserver.isUidForeground(a.mUid));
+ a.reportActualWorkDuration2(WORK_DURATIONS_THREE);
+ verify(mNativeWrapperMock, never()).halReportActualWorkDuration(anyLong(), any(), any());
+ }
}