diff options
| -rw-r--r-- | core/api/current.txt | 18 | ||||
| -rw-r--r-- | core/java/android/os/IHintSession.aidl | 3 | ||||
| -rw-r--r-- | core/java/android/os/PerformanceHintManager.java | 39 | ||||
| -rw-r--r-- | core/java/android/os/SystemClock.java | 3 | ||||
| -rw-r--r-- | core/java/android/os/WorkDuration.aidl | 19 | ||||
| -rw-r--r-- | core/java/android/os/WorkDuration.java | 213 | ||||
| -rw-r--r-- | core/java/android/os/flags.aconfig | 9 | ||||
| -rw-r--r-- | core/jni/android_os_PerformanceHintManager.cpp | 77 | ||||
| -rw-r--r-- | core/tests/coretests/src/android/os/PerformanceHintManagerTest.java | 38 | ||||
| -rw-r--r-- | native/android/libandroid.map.txt | 7 | ||||
| -rw-r--r-- | native/android/performance_hint.cpp | 167 | ||||
| -rw-r--r-- | native/android/tests/performance_hint/PerformanceHintNativeTest.cpp | 127 | ||||
| -rw-r--r-- | services/core/java/com/android/server/power/hint/HintManagerService.java | 55 | ||||
| -rw-r--r-- | services/core/jni/com_android_server_hint_HintManagerService.cpp | 44 | ||||
| -rw-r--r-- | services/tests/servicestests/src/com/android/server/power/hint/HintManagerServiceTest.java | 57 |
15 files changed, 830 insertions, 46 deletions
diff --git a/core/api/current.txt b/core/api/current.txt index 3f4a34b51e40..e7ca0c53398d 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -33137,6 +33137,7 @@ package android.os { 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 @@ package android.os { 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 @@ package android.os { 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 6b43e73d10e7..fe85da26e610 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 @@ oneway interface IHintSession { 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 11084b88fad1..e0059105c21f 100644 --- a/core/java/android/os/PerformanceHintManager.java +++ b/core/java/android/os/PerformanceHintManager.java @@ -103,7 +103,7 @@ public final class PerformanceHintManager { * 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 final class PerformanceHintManager { 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 @@ public final class PerformanceHintManager { 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 831ca86504af..fb67cc07641b 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 @@ public final class SystemClock { * 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 000000000000..0f61204d72c4 --- /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 000000000000..4fdc34fb60d1 --- /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 940ddf2b2597..0809b3bada20 100644 --- a/core/java/android/os/flags.aconfig +++ b/core/java/android/os/flags.aconfig @@ -54,4 +54,11 @@ flag { 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 95bf49fe501e..aebe7ea7ee61 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 void (*APH_sendHint)(APerformanceHintSession*, int32_t); 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_sendHint gAPH_sendHintFn = nullptr; 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 @@ void ensureAPerformanceHintBindingInitialized() { (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 @@ static void nativeSetPreferPowerEfficiency(JNIEnv* env, jclass clazz, jlong nati 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 @@ static const JNINativeMethod gPerformanceHintMethods[] = { {"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 20ba4270e6fc..9b4dec4118a1 100644 --- a/core/tests/coretests/src/android/os/PerformanceHintManagerTest.java +++ b/core/tests/coretests/src/android/os/PerformanceHintManagerTest.java @@ -182,4 +182,42 @@ public class PerformanceHintManagerTest { 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 b0af09c19b7e..f4be33c7e6ea 100644 --- a/native/android/libandroid.map.txt +++ b/native/android/libandroid.map.txt @@ -335,6 +335,13 @@ LIBANDROID { 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 c25df6e08fd0..c4c81284780e 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 @@ public: 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 @@ private: // 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 @@ int APerformanceHintSession::updateTargetWorkDuration(int64_t targetDurationNano * 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 @@ int APerformanceHintSession::reportActualWorkDuration(int64_t actualDurationNano 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 @@ int APerformanceHintSession::setPreferPowerEfficiency(bool enabled) { 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 @@ int APerformanceHint_setPreferPowerEfficiency(APerformanceHintSession* session, 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 22d33b139ccf..4553b4919d2d 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 @@ public: 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 @@ TEST_F(PerformanceHintTest, TestSession) { 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 @@ TEST_F(PerformanceHintTest, CreateZeroTargetDurationSession) { 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 dd39fb02573e..ee3b74653b75 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.PerformanceHintManager; 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 @@ public final class HintManagerService extends SystemService { 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 @@ public final class HintManagerService extends SystemService { nativeSetMode(halPtr, mode, enabled); } + /** Wrapper for HintManager.nativeReportActualWorkDuration */ + public void halReportActualWorkDuration(long halPtr, WorkDuration[] workDurations) { + nativeReportActualWorkDuration(halPtr, workDurations); + } } @VisibleForTesting @@ -624,6 +633,52 @@ public final class HintManagerService extends SystemService { } } + @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 7edf445d7604..ccd9bd0a50ca 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 @@ using android::base::StringPrintf; 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 @@ static void nativeSetMode(JNIEnv* env, jclass /* clazz */, jlong session_ptr, ji 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 @@ static const JNINativeMethod sHintManagerServiceMethods[] = { {"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 d09aa89179b8..37485275dac7 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.IBinder; 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 @@ public class HintManagerServiceTest { 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 @@ public class HintManagerServiceTest { } 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()); + } } |