diff --git a/core/java/Android.bp b/core/java/Android.bp index 6c001f305ce77..26c83eeca508d 100644 --- a/core/java/Android.bp +++ b/core/java/Android.bp @@ -112,6 +112,8 @@ filegroup { srcs: [ "android/os/Temperature.aidl", "android/os/CoolingDevice.aidl", + "android/os/IHintManager.aidl", + "android/os/IHintSession.aidl", "android/os/IThermalEventListener.aidl", "android/os/IThermalStatusListener.aidl", "android/os/IThermalService.aidl", diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index 871d48b07a20d..32ea41b2c75f5 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -158,7 +158,6 @@ import android.os.IBatteryPropertiesRegistrar; import android.os.IBinder; import android.os.IDumpstate; import android.os.IHardwarePropertiesManager; -import android.os.IHintManager; import android.os.IPowerManager; import android.os.IRecoverySystem; import android.os.ISystemUpdateManager; @@ -600,10 +599,7 @@ public final class SystemServiceRegistry { @Override public PerformanceHintManager createService(ContextImpl ctx) throws ServiceNotFoundException { - IBinder hintBinder = ServiceManager.getServiceOrThrow( - Context.PERFORMANCE_HINT_SERVICE); - IHintManager hintService = IHintManager.Stub.asInterface(hintBinder); - return new PerformanceHintManager(hintService); + return PerformanceHintManager.create(); }}); registerService(Context.RECOVERY_SERVICE, RecoverySystem.class, diff --git a/core/java/android/os/PerformanceHintManager.java b/core/java/android/os/PerformanceHintManager.java index 6791844a2a007..a75b5ef6d65e3 100644 --- a/core/java/android/os/PerformanceHintManager.java +++ b/core/java/android/os/PerformanceHintManager.java @@ -24,24 +24,23 @@ import android.content.Context; import com.android.internal.util.Preconditions; import java.io.Closeable; -import java.util.ArrayList; /** The PerformanceHintManager allows apps to send performance hint to system. */ @SystemService(Context.PERFORMANCE_HINT_SERVICE) public final class PerformanceHintManager { - private static final String TAG = "PerformanceHintManager"; - private final IHintManager mService; - // HAL preferred update rate - private final long mPreferredRate; + private final long mNativeManagerPtr; /** @hide */ - public PerformanceHintManager(IHintManager service) { - mService = service; - try { - mPreferredRate = mService.getHintSessionPreferredRate(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); + public static PerformanceHintManager create() throws ServiceManager.ServiceNotFoundException { + long nativeManagerPtr = nativeAcquireManager(); + if (nativeManagerPtr == 0) { + throw new ServiceManager.ServiceNotFoundException(Context.PERFORMANCE_HINT_SERVICE); } + return new PerformanceHintManager(nativeManagerPtr); + } + + private PerformanceHintManager(long nativeManagerPtr) { + mNativeManagerPtr = nativeManagerPtr; } /** @@ -57,16 +56,13 @@ public final class PerformanceHintManager { */ @Nullable public Session createHintSession(@NonNull int[] tids, long initialTargetWorkDurationNanos) { - try { - IBinder token = new Binder(); - IHintSession session = mService.createHintSession(token, tids, - initialTargetWorkDurationNanos); - if (session == null) return null; - return new Session(session, sNanoClock, mPreferredRate, - initialTargetWorkDurationNanos); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + Preconditions.checkNotNull(tids, "tids cannot be null"); + Preconditions.checkArgumentPositive(initialTargetWorkDurationNanos, + "the hint target duration should be positive."); + long nativeSessionPtr = nativeCreateSession(mNativeManagerPtr, tids, + initialTargetWorkDurationNanos); + if (nativeSessionPtr == 0) return null; + return new Session(nativeSessionPtr); } /** @@ -75,7 +71,7 @@ public final class PerformanceHintManager { * @return the preferred update rate supported by device software. */ public long getPreferredUpdateRateNanos() { - return mPreferredRate; + return nativeGetPreferredUpdateRateNanos(mNativeManagerPtr); } /** @@ -101,28 +97,21 @@ public final class PerformanceHintManager { *

All timings should be in {@link SystemClock#elapsedRealtimeNanos()}.

*/ public static class Session implements Closeable { - private final IHintSession mSession; - private final NanoClock mElapsedRealtimeClock; - // Target duration for choosing update rate - private long mTargetDurationInNanos; - // HAL preferred update rate - private long mPreferredRate; - // Last update timestamp - private long mLastUpdateTimeStamp = -1L; - // Cached samples - private final ArrayList mActualDurationNanos; - private final ArrayList mTimeStampNanos; + private long mNativeSessionPtr; /** @hide */ - public Session(IHintSession session, NanoClock elapsedRealtimeClock, long preferredRate, - long durationNanos) { - mSession = session; - mElapsedRealtimeClock = elapsedRealtimeClock; - mTargetDurationInNanos = durationNanos; - mPreferredRate = preferredRate; - mActualDurationNanos = new ArrayList(); - mTimeStampNanos = new ArrayList(); - mLastUpdateTimeStamp = mElapsedRealtimeClock.nanos(); + public Session(long nativeSessionPtr) { + mNativeSessionPtr = nativeSessionPtr; + } + + /** @hide */ + @Override + protected void finalize() throws Throwable { + try { + close(); + } finally { + super.finalize(); + } } /** @@ -133,19 +122,7 @@ public final class PerformanceHintManager { public void updateTargetWorkDuration(long targetDurationNanos) { Preconditions.checkArgumentPositive(targetDurationNanos, "the hint target duration" + " should be positive."); - try { - mSession.updateTargetWorkDuration(targetDurationNanos); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - mTargetDurationInNanos = targetDurationNanos; - /** - * Most of the workload is target_duration dependent, so now clear the cached samples - * as they are most likely obsolete. - */ - mActualDurationNanos.clear(); - mTimeStampNanos.clear(); - mLastUpdateTimeStamp = mElapsedRealtimeClock.nanos(); + nativeUpdateTargetWorkDuration(mNativeSessionPtr, targetDurationNanos); } /** @@ -161,38 +138,7 @@ public final class PerformanceHintManager { public void reportActualWorkDuration(long actualDurationNanos) { Preconditions.checkArgumentPositive(actualDurationNanos, "the actual duration should" + " be positive."); - final long now = mElapsedRealtimeClock.nanos(); - mActualDurationNanos.add(actualDurationNanos); - mTimeStampNanos.add(now); - - /** - * Use current sample to determine the rate limit. We can pick a shorter rate limit - * if any sample underperformed, however, it could be the lower level system is slow - * to react. So here we explicitly choose the rate limit with the latest sample. - */ - long rateLimit = - actualDurationNanos > mTargetDurationInNanos ? mPreferredRate - : 10 * mPreferredRate; - - if (now - mLastUpdateTimeStamp <= rateLimit) { - return; - } - Preconditions.checkState(mActualDurationNanos.size() == mTimeStampNanos.size()); - final int size = mActualDurationNanos.size(); - long[] actualDurationArray = new long[size]; - long[] timeStampArray = new long[size]; - for (int i = 0; i < size; i++) { - actualDurationArray[i] = mActualDurationNanos.get(i); - timeStampArray[i] = mTimeStampNanos.get(i); - } - try { - mSession.reportActualWorkDuration(actualDurationArray, timeStampArray); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - mActualDurationNanos.clear(); - mTimeStampNanos.clear(); - mLastUpdateTimeStamp = now; + nativeReportActualWorkDuration(mNativeSessionPtr, actualDurationNanos); } /** @@ -201,26 +147,20 @@ public final class PerformanceHintManager { *

Once called, you should not call anything else on this object.

*/ public void close() { - try { - mSession.close(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); + if (mNativeSessionPtr != 0) { + nativeCloseSession(mNativeSessionPtr); + mNativeSessionPtr = 0; } } } - /** - * The interface is to make the FakeClock for testing. - * @hide - */ - public interface NanoClock { - /** Gets the current nanosecond instant of the clock. */ - long nanos(); - } - - private static final NanoClock sNanoClock = new NanoClock() { - public long nanos() { - return SystemClock.elapsedRealtimeNanos(); - } - }; + private static native long nativeAcquireManager(); + private static native long nativeGetPreferredUpdateRateNanos(long nativeManagerPtr); + private static native long nativeCreateSession(long nativeManagerPtr, + int[] tids, long initialTargetWorkDurationNanos); + private static native void nativeUpdateTargetWorkDuration(long nativeSessionPtr, + long targetDurationNanos); + private static native void nativeReportActualWorkDuration(long nativeSessionPtr, + long actualDurationNanos); + private static native void nativeCloseSession(long nativeSessionPtr); } diff --git a/core/jni/Android.bp b/core/jni/Android.bp index 91a19e087bf17..6b9d3754c2471 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -146,6 +146,7 @@ cc_library_shared { "android_os_MemoryFile.cpp", "android_os_MessageQueue.cpp", "android_os_Parcel.cpp", + "android_os_PerformanceHintManager.cpp", "android_os_SELinux.cpp", "android_os_ServiceManager.cpp", "android_os_SharedMemory.cpp", diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 406ccde533306..2fd1e543cc5bc 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -143,6 +143,7 @@ extern int register_android_os_NativeHandle(JNIEnv *env); extern int register_android_os_ServiceManager(JNIEnv *env); extern int register_android_os_MessageQueue(JNIEnv* env); extern int register_android_os_Parcel(JNIEnv* env); +extern int register_android_os_PerformanceHintManager(JNIEnv* env); extern int register_android_os_SELinux(JNIEnv* env); extern int register_android_os_VintfObject(JNIEnv *env); extern int register_android_os_VintfRuntimeInfo(JNIEnv *env); @@ -1518,6 +1519,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_os_SystemProperties), REG_JNI(register_android_os_Binder), REG_JNI(register_android_os_Parcel), + REG_JNI(register_android_os_PerformanceHintManager), REG_JNI(register_android_os_HidlMemory), REG_JNI(register_android_os_HidlSupport), REG_JNI(register_android_os_HwBinder), diff --git a/core/jni/android_os_PerformanceHintManager.cpp b/core/jni/android_os_PerformanceHintManager.cpp new file mode 100644 index 0000000000000..d05a24fe7c6e1 --- /dev/null +++ b/core/jni/android_os_PerformanceHintManager.cpp @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2021 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. + */ + +#define LOG_TAG "PerfHint-jni" + +#include "jni.h" + +#include +#include +#include +#include +#include + +#include "core_jni_helpers.h" + +namespace android { + +namespace { + +struct APerformanceHintManager; +struct APerformanceHintSession; + +typedef APerformanceHintManager* (*APH_getManager)(); +typedef APerformanceHintSession* (*APH_createSession)(APerformanceHintManager*, const int32_t*, + size_t, int64_t); +typedef int64_t (*APH_getPreferredUpdateRateNanos)(APerformanceHintManager* manager); +typedef void (*APH_updateTargetWorkDuration)(APerformanceHintSession*, int64_t); +typedef void (*APH_reportActualWorkDuration)(APerformanceHintSession*, int64_t); +typedef void (*APH_closeSession)(APerformanceHintSession* session); + +bool gAPerformanceHintBindingInitialized = false; +APH_getManager gAPH_getManagerFn = nullptr; +APH_createSession gAPH_createSessionFn = nullptr; +APH_getPreferredUpdateRateNanos gAPH_getPreferredUpdateRateNanosFn = nullptr; +APH_updateTargetWorkDuration gAPH_updateTargetWorkDurationFn = nullptr; +APH_reportActualWorkDuration gAPH_reportActualWorkDurationFn = nullptr; +APH_closeSession gAPH_closeSessionFn = nullptr; + +void ensureAPerformanceHintBindingInitialized() { + if (gAPerformanceHintBindingInitialized) return; + + void* handle_ = dlopen("libandroid.so", RTLD_NOW | RTLD_NODELETE); + LOG_ALWAYS_FATAL_IF(handle_ == nullptr, "Failed to dlopen libandroid.so!"); + + gAPH_getManagerFn = (APH_getManager)dlsym(handle_, "APerformanceHint_getManager"); + LOG_ALWAYS_FATAL_IF(gAPH_getManagerFn == nullptr, + "Failed to find required symbol APerformanceHint_getManager!"); + + gAPH_createSessionFn = (APH_createSession)dlsym(handle_, "APerformanceHint_createSession"); + LOG_ALWAYS_FATAL_IF(gAPH_createSessionFn == nullptr, + "Failed to find required symbol APerformanceHint_createSession!"); + + gAPH_getPreferredUpdateRateNanosFn = + (APH_getPreferredUpdateRateNanos)dlsym(handle_, + "APerformanceHint_getPreferredUpdateRateNanos"); + LOG_ALWAYS_FATAL_IF(gAPH_getPreferredUpdateRateNanosFn == nullptr, + "Failed to find required symbol " + "APerformanceHint_getPreferredUpdateRateNanos!"); + + gAPH_updateTargetWorkDurationFn = + (APH_updateTargetWorkDuration)dlsym(handle_, + "APerformanceHint_updateTargetWorkDuration"); + LOG_ALWAYS_FATAL_IF(gAPH_updateTargetWorkDurationFn == nullptr, + "Failed to find required symbol " + "APerformanceHint_updateTargetWorkDuration!"); + + gAPH_reportActualWorkDurationFn = + (APH_reportActualWorkDuration)dlsym(handle_, + "APerformanceHint_reportActualWorkDuration"); + LOG_ALWAYS_FATAL_IF(gAPH_reportActualWorkDurationFn == nullptr, + "Failed to find required symbol " + "APerformanceHint_reportActualWorkDuration!"); + + gAPH_closeSessionFn = (APH_closeSession)dlsym(handle_, "APerformanceHint_closeSession"); + LOG_ALWAYS_FATAL_IF(gAPH_closeSessionFn == nullptr, + "Failed to find required symbol APerformanceHint_closeSession!"); + + gAPerformanceHintBindingInitialized = true; +} + +} // namespace + +static jlong nativeAcquireManager(JNIEnv* env, jclass clazz) { + ensureAPerformanceHintBindingInitialized(); + return reinterpret_cast(gAPH_getManagerFn()); +} + +static jlong nativeGetPreferredUpdateRateNanos(JNIEnv* env, jclass clazz, jlong nativeManagerPtr) { + ensureAPerformanceHintBindingInitialized(); + return gAPH_getPreferredUpdateRateNanosFn( + reinterpret_cast(nativeManagerPtr)); +} + +static jlong nativeCreateSession(JNIEnv* env, jclass clazz, jlong nativeManagerPtr, jintArray tids, + jlong initialTargetWorkDurationNanos) { + ensureAPerformanceHintBindingInitialized(); + if (tids == nullptr) return 0; + std::vector tidsVector; + ScopedIntArrayRO tidsArray(env, tids); + for (size_t i = 0; i < tidsArray.size(); ++i) { + tidsVector.push_back(static_cast(tidsArray[i])); + } + return reinterpret_cast( + gAPH_createSessionFn(reinterpret_cast(nativeManagerPtr), + tidsVector.data(), tidsVector.size(), + initialTargetWorkDurationNanos)); +} + +static void nativeUpdateTargetWorkDuration(JNIEnv* env, jclass clazz, jlong nativeSessionPtr, + jlong targetDurationNanos) { + ensureAPerformanceHintBindingInitialized(); + gAPH_updateTargetWorkDurationFn(reinterpret_cast(nativeSessionPtr), + targetDurationNanos); +} + +static void nativeReportActualWorkDuration(JNIEnv* env, jclass clazz, jlong nativeSessionPtr, + jlong actualDurationNanos) { + ensureAPerformanceHintBindingInitialized(); + gAPH_reportActualWorkDurationFn(reinterpret_cast(nativeSessionPtr), + actualDurationNanos); +} + +static void nativeCloseSession(JNIEnv* env, jclass clazz, jlong nativeSessionPtr) { + ensureAPerformanceHintBindingInitialized(); + gAPH_closeSessionFn(reinterpret_cast(nativeSessionPtr)); +} + +static const JNINativeMethod gPerformanceHintMethods[] = { + {"nativeAcquireManager", "()J", (void*)nativeAcquireManager}, + {"nativeGetPreferredUpdateRateNanos", "(J)J", (void*)nativeGetPreferredUpdateRateNanos}, + {"nativeCreateSession", "(J[IJ)J", (void*)nativeCreateSession}, + {"nativeUpdateTargetWorkDuration", "(JJ)V", (void*)nativeUpdateTargetWorkDuration}, + {"nativeReportActualWorkDuration", "(JJ)V", (void*)nativeReportActualWorkDuration}, + {"nativeCloseSession", "(J)V", (void*)nativeCloseSession}, +}; + +int register_android_os_PerformanceHintManager(JNIEnv* env) { + return RegisterMethodsOrDie(env, "android/os/PerformanceHintManager", gPerformanceHintMethods, + NELEM(gPerformanceHintMethods)); +} + +} // namespace android diff --git a/core/tests/coretests/src/android/os/PerformanceHintManagerTest.java b/core/tests/coretests/src/android/os/PerformanceHintManagerTest.java index 7dea82d7ee54c..69eb13f7854ab 100644 --- a/core/tests/coretests/src/android/os/PerformanceHintManagerTest.java +++ b/core/tests/coretests/src/android/os/PerformanceHintManagerTest.java @@ -22,12 +22,6 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeNotNull; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; import android.os.PerformanceHintManager.Session; @@ -119,93 +113,10 @@ public class PerformanceHintManagerTest { }); } - @Test - public void testRateLimitWithDurationFastEnough() throws Exception { - FakeClock fakeClock = new FakeClock(); - Session s = new Session(mIHintSessionMock, fakeClock, RATE_1000, TARGET_166); - - reset(mIHintSessionMock); - fakeClock.setNow(0); - s.updateTargetWorkDuration(TARGET_166); - - s.reportActualWorkDuration(TARGET_166 - 1); - s.reportActualWorkDuration(TARGET_166); - // we should not see update as the rate should be 10X for over-perform case. - verify(mIHintSessionMock, never()).reportActualWorkDuration(any(), any()); - fakeClock.incrementClock(10 * RATE_1000); - s.reportActualWorkDuration(TARGET_166); - verify(mIHintSessionMock, never()).reportActualWorkDuration(any(), any()); - fakeClock.incrementClock(1); - s.reportActualWorkDuration(TARGET_166); - // we should see update after rate limit - verify(mIHintSessionMock, times(1)).reportActualWorkDuration( - eq(new long[] {TARGET_166 - 1, TARGET_166, TARGET_166, TARGET_166}), - eq(new long[] {0, 0, 10 * RATE_1000, 10 * RATE_1000 + 1})); - - reset(mIHintSessionMock); - s.reportActualWorkDuration(TARGET_166); - s.reportActualWorkDuration(TARGET_166 - 1); - s.reportActualWorkDuration(TARGET_166 - 2); - // we should not see update as the rate should be 10X for over-perform case. - verify(mIHintSessionMock, never()).reportActualWorkDuration(any(), any()); - fakeClock.incrementClock(10 * RATE_1000 + 1); - s.reportActualWorkDuration(TARGET_166); - s.reportActualWorkDuration(TARGET_166 - 1); - // we should see update now - verify(mIHintSessionMock, times(1)).reportActualWorkDuration( - eq(new long[] {TARGET_166, TARGET_166 - 1, TARGET_166 - 2, TARGET_166}), - eq(new long[] {10 * RATE_1000 + 1, 10 * RATE_1000 + 1, 10 * RATE_1000 + 1, - (10 * RATE_1000 + 1) * 2})); - } - - @Test - public void testRateLimitWithDurationTooSlow() throws Exception { - FakeClock fakeClock = new FakeClock(); - Session s = new Session(mIHintSessionMock, fakeClock, RATE_1000, TARGET_166); - - reset(mIHintSessionMock); - fakeClock.setNow(0); - s.updateTargetWorkDuration(TARGET_166); - - verify(mIHintSessionMock, times(1)).updateTargetWorkDuration(eq(TARGET_166)); - // shouldn't update before rate limit - s.reportActualWorkDuration(TARGET_166 + 1); - verify(mIHintSessionMock, never()).reportActualWorkDuration(any(), any()); - - // shouldn't update when the time is exactly at rate limit - fakeClock.incrementClock(RATE_1000); - s.reportActualWorkDuration(TARGET_166 + 1); - verify(mIHintSessionMock, never()).reportActualWorkDuration(any(), any()); - - // should be ready for sending hint - fakeClock.incrementClock(1); - s.reportActualWorkDuration(TARGET_166 + 1); - verify(mIHintSessionMock, times(1)).reportActualWorkDuration( - eq(new long[] {TARGET_166 + 1, TARGET_166 + 1, TARGET_166 + 1}), - eq(new long[] {0 , RATE_1000, RATE_1000 + 1})); - } - @Test public void testCloseHintSession() { Session s = createSession(); assumeNotNull(s); s.close(); } - - private static class FakeClock implements PerformanceHintManager.NanoClock { - private long mCurrentTime = 0L; - - @Override - public long nanos() { - return mCurrentTime; - } - - public void setNow(long nanos) { - mCurrentTime = nanos; - } - - public void incrementClock(long nanos) { - mCurrentTime += nanos; - } - } } diff --git a/graphics/java/android/graphics/HardwareRenderer.java b/graphics/java/android/graphics/HardwareRenderer.java index fe04f0dd4c83e..c3b1cd74d29b0 100644 --- a/graphics/java/android/graphics/HardwareRenderer.java +++ b/graphics/java/android/graphics/HardwareRenderer.java @@ -28,7 +28,6 @@ import android.content.res.Configuration; import android.hardware.display.DisplayManager; import android.os.IBinder; import android.os.ParcelFileDescriptor; -import android.os.PerformanceHintManager; import android.os.RemoteException; import android.os.ServiceManager; import android.util.Log; @@ -856,36 +855,6 @@ public class HardwareRenderer { callback.onPictureCaptured(picture); } - /** called by native */ - static PerformanceHintManager.Session createHintSession(int[] tids) { - PerformanceHintManager performanceHintManager = - ProcessInitializer.sInstance.getHintManager(); - if (performanceHintManager == null) { - return null; - } - // Native code will always set a target duration before reporting actual durations. - // So this is just a placeholder value that's never used. - long targetDurationNanos = 16666667; - return performanceHintManager.createHintSession(tids, targetDurationNanos); - } - - /** called by native */ - static void updateTargetWorkDuration(PerformanceHintManager.Session session, - long targetDurationNanos) { - session.updateTargetWorkDuration(targetDurationNanos); - } - - /** called by native */ - static void reportActualWorkDuration(PerformanceHintManager.Session session, - long actualDurationNanos) { - session.reportActualWorkDuration(actualDurationNanos); - } - - /** called by native */ - static void closeHintSession(PerformanceHintManager.Session session) { - session.close(); - } - /** * Interface used to receive callbacks when Webview requests a surface control. * @@ -1152,7 +1121,6 @@ public class HardwareRenderer { private boolean mIsolated = false; private Context mContext; private String mPackageName; - private PerformanceHintManager mPerformanceHintManager; private IGraphicsStats mGraphicsStatsService; private IGraphicsStatsCallback mGraphicsStatsCallback = new IGraphicsStatsCallback.Stub() { @Override @@ -1164,10 +1132,6 @@ public class HardwareRenderer { private ProcessInitializer() { } - synchronized PerformanceHintManager getHintManager() { - return mPerformanceHintManager; - } - synchronized void setPackageName(String name) { if (mInitialized) return; mPackageName = name; @@ -1218,10 +1182,6 @@ public class HardwareRenderer { initDisplayInfo(); - // HintManager and HintSession are designed to be accessible from isoalted processes - // so not checking for isolated process here. - initHintSession(); - nSetIsHighEndGfx(ActivityManager.isHighEndGfx()); // Defensively clear out the context in case we were passed a context that can leak // if we live longer than it, e.g. an activity context. @@ -1265,11 +1225,6 @@ public class HardwareRenderer { mDisplayInitialized = true; } - private void initHintSession() { - if (mContext == null) return; - mPerformanceHintManager = mContext.getSystemService(PerformanceHintManager.class); - } - private void rotateBuffer() { nRotateProcessStatsBuffer(); requestBuffer(); diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp index c4cdb7db7d866..54367b8334cbd 100644 --- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp +++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp @@ -44,8 +44,6 @@ #include #include -#include - #include #include #include @@ -60,10 +58,6 @@ using namespace android::uirenderer::renderthread; struct { jclass clazz; jmethodID invokePictureCapturedCallback; - jmethodID createHintSession; - jmethodID updateTargetWorkDuration; - jmethodID reportActualWorkDuration; - jmethodID closeHintSession; } gHardwareRenderer; struct { @@ -90,14 +84,6 @@ static JNIEnv* getenv(JavaVM* vm) { return env; } -static bool hasExceptionAndClear(JNIEnv* env) { - if (GraphicsJNI::hasException(env)) { - env->ExceptionClear(); - return true; - } - return false; -} - typedef ANativeWindow* (*ANW_fromSurface)(JNIEnv* env, jobject surface); ANW_fromSurface fromSurface; @@ -147,67 +133,6 @@ private: } }; -class HintSessionWrapper : public LightRefBase { -public: - static sp create(JNIEnv* env, RenderProxy* proxy) { - if (!Properties::useHintManager) return nullptr; - - // Include UI thread (self), render thread, and thread pool. - std::vector tids = CommonPool::getThreadIds(); - tids.push_back(proxy->getRenderThreadTid()); - tids.push_back(pthread_gettid_np(pthread_self())); - - jintArray tidsArray = env->NewIntArray(tids.size()); - if (hasExceptionAndClear(env)) return nullptr; - env->SetIntArrayRegion(tidsArray, 0, tids.size(), reinterpret_cast(tids.data())); - if (hasExceptionAndClear(env)) return nullptr; - jobject session = env->CallStaticObjectMethod( - gHardwareRenderer.clazz, gHardwareRenderer.createHintSession, tidsArray); - if (hasExceptionAndClear(env) || !session) return nullptr; - return new HintSessionWrapper(env, session); - } - - ~HintSessionWrapper() { - if (!mSession) return; - JNIEnv* env = getenv(mVm); - env->CallStaticVoidMethod(gHardwareRenderer.clazz, gHardwareRenderer.closeHintSession, - mSession); - hasExceptionAndClear(env); - env->DeleteGlobalRef(mSession); - mSession = nullptr; - } - - void updateTargetWorkDuration(long targetDurationNanos) { - if (!mSession) return; - JNIEnv* env = getenv(mVm); - env->CallStaticVoidMethod(gHardwareRenderer.clazz, - gHardwareRenderer.updateTargetWorkDuration, mSession, - static_cast(targetDurationNanos)); - hasExceptionAndClear(env); - } - - void reportActualWorkDuration(long actualDurationNanos) { - if (!mSession) return; - JNIEnv* env = getenv(mVm); - env->CallStaticVoidMethod(gHardwareRenderer.clazz, - gHardwareRenderer.reportActualWorkDuration, mSession, - static_cast(actualDurationNanos)); - hasExceptionAndClear(env); - } - -private: - HintSessionWrapper(JNIEnv* env, jobject jobject) { - env->GetJavaVM(&mVm); - if (jobject) { - mSession = env->NewGlobalRef(jobject); - LOG_ALWAYS_FATAL_IF(!mSession, "Failed to make global ref"); - } - } - - JavaVM* mVm = nullptr; - jobject mSession = nullptr; -}; - static void android_view_ThreadedRenderer_rotateProcessStatsBuffer(JNIEnv* env, jobject clazz) { RenderProxy::rotateProcessStatsBuffer(); } @@ -235,12 +160,6 @@ static jlong android_view_ThreadedRenderer_createProxy(JNIEnv* env, jobject claz RootRenderNode* rootRenderNode = reinterpret_cast(rootRenderNodePtr); ContextFactoryImpl factory(rootRenderNode); RenderProxy* proxy = new RenderProxy(translucent, rootRenderNode, &factory); - sp wrapper = HintSessionWrapper::create(env, proxy); - if (wrapper) { - proxy->setHintSessionCallbacks( - [wrapper](int64_t nanos) { wrapper->updateTargetWorkDuration(nanos); }, - [wrapper](int64_t nanos) { wrapper->reportActualWorkDuration(nanos); }); - } return (jlong) proxy; } @@ -1059,18 +978,6 @@ int register_android_view_ThreadedRenderer(JNIEnv* env) { gHardwareRenderer.invokePictureCapturedCallback = GetStaticMethodIDOrDie(env, hardwareRenderer, "invokePictureCapturedCallback", "(JLandroid/graphics/HardwareRenderer$PictureCapturedCallback;)V"); - gHardwareRenderer.createHintSession = - GetStaticMethodIDOrDie(env, hardwareRenderer, "createHintSession", - "([I)Landroid/os/PerformanceHintManager$Session;"); - gHardwareRenderer.updateTargetWorkDuration = - GetStaticMethodIDOrDie(env, hardwareRenderer, "updateTargetWorkDuration", - "(Landroid/os/PerformanceHintManager$Session;J)V"); - gHardwareRenderer.reportActualWorkDuration = - GetStaticMethodIDOrDie(env, hardwareRenderer, "reportActualWorkDuration", - "(Landroid/os/PerformanceHintManager$Session;J)V"); - gHardwareRenderer.closeHintSession = - GetStaticMethodIDOrDie(env, hardwareRenderer, "closeHintSession", - "(Landroid/os/PerformanceHintManager$Session;)V"); jclass aSurfaceTransactionCallbackClass = FindClassOrDie(env, "android/graphics/HardwareRenderer$ASurfaceTransactionCallback"); diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp index db29e342855bb..e7081df2b558d 100644 --- a/libs/hwui/renderthread/DrawFrameTask.cpp +++ b/libs/hwui/renderthread/DrawFrameTask.cpp @@ -16,6 +16,7 @@ #include "DrawFrameTask.h" +#include #include #include #include @@ -26,11 +27,63 @@ #include "../RenderNode.h" #include "CanvasContext.h" #include "RenderThread.h" +#include "thread/CommonPool.h" namespace android { namespace uirenderer { namespace renderthread { +namespace { + +typedef APerformanceHintManager* (*APH_getManager)(); +typedef APerformanceHintSession* (*APH_createSession)(APerformanceHintManager*, const int32_t*, + size_t, int64_t); +typedef void (*APH_updateTargetWorkDuration)(APerformanceHintSession*, int64_t); +typedef void (*APH_reportActualWorkDuration)(APerformanceHintSession*, int64_t); +typedef void (*APH_closeSession)(APerformanceHintSession* session); + +bool gAPerformanceHintBindingInitialized = false; +APH_getManager gAPH_getManagerFn = nullptr; +APH_createSession gAPH_createSessionFn = nullptr; +APH_updateTargetWorkDuration gAPH_updateTargetWorkDurationFn = nullptr; +APH_reportActualWorkDuration gAPH_reportActualWorkDurationFn = nullptr; +APH_closeSession gAPH_closeSessionFn = nullptr; + +void ensureAPerformanceHintBindingInitialized() { + if (gAPerformanceHintBindingInitialized) return; + + void* handle_ = dlopen("libandroid.so", RTLD_NOW | RTLD_NODELETE); + LOG_ALWAYS_FATAL_IF(handle_ == nullptr, "Failed to dlopen libandroid.so!"); + + gAPH_getManagerFn = (APH_getManager)dlsym(handle_, "APerformanceHint_getManager"); + LOG_ALWAYS_FATAL_IF(gAPH_getManagerFn == nullptr, + "Failed to find required symbol APerformanceHint_getManager!"); + + gAPH_createSessionFn = (APH_createSession)dlsym(handle_, "APerformanceHint_createSession"); + LOG_ALWAYS_FATAL_IF(gAPH_createSessionFn == nullptr, + "Failed to find required symbol APerformanceHint_createSession!"); + + gAPH_updateTargetWorkDurationFn = (APH_updateTargetWorkDuration)dlsym( + handle_, "APerformanceHint_updateTargetWorkDuration"); + LOG_ALWAYS_FATAL_IF( + gAPH_updateTargetWorkDurationFn == nullptr, + "Failed to find required symbol APerformanceHint_updateTargetWorkDuration!"); + + gAPH_reportActualWorkDurationFn = (APH_reportActualWorkDuration)dlsym( + handle_, "APerformanceHint_reportActualWorkDuration"); + LOG_ALWAYS_FATAL_IF( + gAPH_reportActualWorkDurationFn == nullptr, + "Failed to find required symbol APerformanceHint_reportActualWorkDuration!"); + + gAPH_closeSessionFn = (APH_closeSession)dlsym(handle_, "APerformanceHint_closeSession"); + LOG_ALWAYS_FATAL_IF(gAPH_closeSessionFn == nullptr, + "Failed to find required symbol APerformanceHint_closeSession!"); + + gAPerformanceHintBindingInitialized = true; +} + +} // namespace + DrawFrameTask::DrawFrameTask() : mRenderThread(nullptr) , mContext(nullptr) @@ -39,17 +92,13 @@ DrawFrameTask::DrawFrameTask() DrawFrameTask::~DrawFrameTask() {} -void DrawFrameTask::setContext(RenderThread* thread, CanvasContext* context, - RenderNode* targetNode) { +void DrawFrameTask::setContext(RenderThread* thread, CanvasContext* context, RenderNode* targetNode, + int32_t uiThreadId, int32_t renderThreadId) { mRenderThread = thread; mContext = context; mTargetNode = targetNode; -} - -void DrawFrameTask::setHintSessionCallbacks(std::function updateTargetWorkDuration, - std::function reportActualWorkDuration) { - mUpdateTargetWorkDuration = std::move(updateTargetWorkDuration); - mReportActualWorkDuration = std::move(reportActualWorkDuration); + mUiThreadId = uiThreadId; + mRenderThreadId = renderThreadId; } void DrawFrameTask::pushLayerUpdate(DeferredLayerUpdater* layer) { @@ -144,27 +193,25 @@ void DrawFrameTask::run() { unblockUiThread(); } - // These member callbacks are effectively const as they are set once during init, so it's safe - // to use these directly instead of making local copies. - if (mUpdateTargetWorkDuration && mReportActualWorkDuration) { - constexpr int64_t kSanityCheckLowerBound = 100000; // 0.1ms - constexpr int64_t kSanityCheckUpperBound = 10000000000; // 10s - int64_t targetWorkDuration = frameDeadline - intendedVsync; - targetWorkDuration = targetWorkDuration * Properties::targetCpuTimePercentage / 100; - if (targetWorkDuration > kSanityCheckLowerBound && - targetWorkDuration < kSanityCheckUpperBound && - targetWorkDuration != mLastTargetWorkDuration) { - mLastTargetWorkDuration = targetWorkDuration; - mUpdateTargetWorkDuration(targetWorkDuration); - } - int64_t frameDuration = systemTime(SYSTEM_TIME_MONOTONIC) - frameStartTime; - int64_t actualDuration = frameDuration - - (std::min(syncDelayDuration, mLastDequeueBufferDuration)) - - dequeueBufferDuration; - if (actualDuration > kSanityCheckLowerBound && actualDuration < kSanityCheckUpperBound) { - mReportActualWorkDuration(actualDuration); - } + if (!mHintSessionWrapper) mHintSessionWrapper.emplace(mUiThreadId, mRenderThreadId); + constexpr int64_t kSanityCheckLowerBound = 100000; // 0.1ms + constexpr int64_t kSanityCheckUpperBound = 10000000000; // 10s + int64_t targetWorkDuration = frameDeadline - intendedVsync; + targetWorkDuration = targetWorkDuration * Properties::targetCpuTimePercentage / 100; + if (targetWorkDuration > kSanityCheckLowerBound && + targetWorkDuration < kSanityCheckUpperBound && + targetWorkDuration != mLastTargetWorkDuration) { + mLastTargetWorkDuration = targetWorkDuration; + mHintSessionWrapper->updateTargetWorkDuration(targetWorkDuration); } + int64_t frameDuration = systemTime(SYSTEM_TIME_MONOTONIC) - frameStartTime; + int64_t actualDuration = frameDuration - + (std::min(syncDelayDuration, mLastDequeueBufferDuration)) - + dequeueBufferDuration; + if (actualDuration > kSanityCheckLowerBound && actualDuration < kSanityCheckUpperBound) { + mHintSessionWrapper->reportActualWorkDuration(actualDuration); + } + mLastDequeueBufferDuration = dequeueBufferDuration; } @@ -216,6 +263,44 @@ void DrawFrameTask::unblockUiThread() { mSignal.signal(); } +DrawFrameTask::HintSessionWrapper::HintSessionWrapper(int32_t uiThreadId, int32_t renderThreadId) { + if (!Properties::useHintManager) return; + if (uiThreadId < 0 || renderThreadId < 0) return; + + ensureAPerformanceHintBindingInitialized(); + + APerformanceHintManager* manager = gAPH_getManagerFn(); + if (!manager) return; + + std::vector tids = CommonPool::getThreadIds(); + tids.push_back(uiThreadId); + tids.push_back(renderThreadId); + + // DrawFrameTask code will always set a target duration before reporting actual durations. + // So this is just a placeholder value that's never used. + int64_t dummyTargetDurationNanos = 16666667; + mHintSession = + gAPH_createSessionFn(manager, tids.data(), tids.size(), dummyTargetDurationNanos); +} + +DrawFrameTask::HintSessionWrapper::~HintSessionWrapper() { + if (mHintSession) { + gAPH_closeSessionFn(mHintSession); + } +} + +void DrawFrameTask::HintSessionWrapper::updateTargetWorkDuration(long targetDurationNanos) { + if (mHintSession) { + gAPH_updateTargetWorkDurationFn(mHintSession, targetDurationNanos); + } +} + +void DrawFrameTask::HintSessionWrapper::reportActualWorkDuration(long actualDurationNanos) { + if (mHintSession) { + gAPH_reportActualWorkDurationFn(mHintSession, actualDurationNanos); + } +} + } /* namespace renderthread */ } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/renderthread/DrawFrameTask.h b/libs/hwui/renderthread/DrawFrameTask.h index 2455ea84c94eb..6a61a2bb645f7 100644 --- a/libs/hwui/renderthread/DrawFrameTask.h +++ b/libs/hwui/renderthread/DrawFrameTask.h @@ -16,8 +16,10 @@ #ifndef DRAWFRAMETASK_H #define DRAWFRAMETASK_H +#include #include +#include #include #include #include @@ -60,9 +62,8 @@ public: DrawFrameTask(); virtual ~DrawFrameTask(); - void setContext(RenderThread* thread, CanvasContext* context, RenderNode* targetNode); - void setHintSessionCallbacks(std::function updateTargetWorkDuration, - std::function reportActualWorkDuration); + void setContext(RenderThread* thread, CanvasContext* context, RenderNode* targetNode, + int32_t uiThreadId, int32_t renderThreadId); void setContentDrawBounds(int left, int top, int right, int bottom) { mContentDrawBounds.set(left, top, right, bottom); } @@ -85,6 +86,18 @@ public: } private: + class HintSessionWrapper { + public: + HintSessionWrapper(int32_t uiThreadId, int32_t renderThreadId); + ~HintSessionWrapper(); + + void updateTargetWorkDuration(long targetDurationNanos); + void reportActualWorkDuration(long actualDurationNanos); + + private: + APerformanceHintSession* mHintSession = nullptr; + }; + void postAndWait(); bool syncFrameState(TreeInfo& info); void unblockUiThread(); @@ -95,6 +108,8 @@ private: RenderThread* mRenderThread; CanvasContext* mContext; RenderNode* mTargetNode = nullptr; + int32_t mUiThreadId = -1; + int32_t mRenderThreadId = -1; Rect mContentDrawBounds; /********************************************* @@ -112,8 +127,7 @@ private: nsecs_t mLastDequeueBufferDuration = 0; nsecs_t mLastTargetWorkDuration = 0; - std::function mUpdateTargetWorkDuration; - std::function mReportActualWorkDuration; + std::optional mHintSessionWrapper; }; } /* namespace renderthread */ diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index a77b5b5699075..c485ce2781e5a 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -29,6 +29,8 @@ #include "utils/Macros.h" #include "utils/TimeUtils.h" +#include + namespace android { namespace uirenderer { namespace renderthread { @@ -39,7 +41,8 @@ RenderProxy::RenderProxy(bool translucent, RenderNode* rootRenderNode, mContext = mRenderThread.queue().runSync([&]() -> CanvasContext* { return CanvasContext::create(mRenderThread, translucent, rootRenderNode, contextFactory); }); - mDrawFrameTask.setContext(&mRenderThread, mContext, rootRenderNode); + mDrawFrameTask.setContext(&mRenderThread, mContext, rootRenderNode, + pthread_gettid_np(pthread_self()), getRenderThreadTid()); } RenderProxy::~RenderProxy() { @@ -48,7 +51,7 @@ RenderProxy::~RenderProxy() { void RenderProxy::destroyContext() { if (mContext) { - mDrawFrameTask.setContext(nullptr, nullptr, nullptr); + mDrawFrameTask.setContext(nullptr, nullptr, nullptr, -1, -1); // This is also a fence as we need to be certain that there are no // outstanding mDrawFrame tasks posted before it is destroyed mRenderThread.queue().runSync([this]() { delete mContext; }); @@ -76,12 +79,6 @@ void RenderProxy::setName(const char* name) { mRenderThread.queue().runSync([this, name]() { mContext->setName(std::string(name)); }); } -void RenderProxy::setHintSessionCallbacks(std::function updateTargetWorkDuration, - std::function reportActualWorkDuration) { - mDrawFrameTask.setHintSessionCallbacks(std::move(updateTargetWorkDuration), - std::move(reportActualWorkDuration)); -} - void RenderProxy::setSurface(ANativeWindow* window, bool enableTimeout) { if (window) { ANativeWindow_acquire(window); } mRenderThread.queue().post([this, win = window, enableTimeout]() mutable { diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h index 1b0f22e75a2de..2b5405c82563a 100644 --- a/libs/hwui/renderthread/RenderProxy.h +++ b/libs/hwui/renderthread/RenderProxy.h @@ -71,8 +71,6 @@ public: void setSwapBehavior(SwapBehavior swapBehavior); bool loadSystemProperties(); void setName(const char* name); - void setHintSessionCallbacks(std::function updateTargetWorkDuration, - std::function reportActualWorkDuration); void setSurface(ANativeWindow* window, bool enableTimeout = true); void setSurfaceControl(ASurfaceControl* surfaceControl); diff --git a/native/android/Android.bp b/native/android/Android.bp index 3ee2c18a0eab4..32b7a0780e632 100644 --- a/native/android/Android.bp +++ b/native/android/Android.bp @@ -57,6 +57,7 @@ cc_library_shared { "net.c", "obb.cpp", "permission_manager.cpp", + "performance_hint.cpp", "sensor.cpp", "sharedmem.cpp", "storage_manager.cpp", diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt index de6db1ae7d237..f33e11817730b 100644 --- a/native/android/libandroid.map.txt +++ b/native/android/libandroid.map.txt @@ -312,6 +312,13 @@ LIBANDROID { LIBANDROID_PLATFORM { global: + APerformanceHint_getManager; + APerformanceHint_createSession; + APerformanceHint_getPreferredUpdateRateNanos; + APerformanceHint_updateTargetWorkDuration; + APerformanceHint_reportActualWorkDuration; + APerformanceHint_closeSession; + APerformanceHint_setIHintManagerForTesting; extern "C++" { ASurfaceControl_registerSurfaceStatsListener*; ASurfaceControl_unregisterSurfaceStatsListener*; diff --git a/native/android/performance_hint.cpp b/native/android/performance_hint.cpp new file mode 100644 index 0000000000000..95a2da9226d96 --- /dev/null +++ b/native/android/performance_hint.cpp @@ -0,0 +1,241 @@ +/* + * Copyright (C) 2021 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. + */ + +#define LOG_TAG "perf_hint" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace android; +using namespace android::os; + +struct APerformanceHintSession; + +struct APerformanceHintManager { +public: + static APerformanceHintManager* getInstance(); + APerformanceHintManager(sp service, int64_t preferredRateNanos); + APerformanceHintManager() = delete; + ~APerformanceHintManager() = default; + + APerformanceHintSession* createSession(const int32_t* threadIds, size_t size, + int64_t initialTargetWorkDurationNanos); + int64_t getPreferredRateNanos() const; + +private: + static APerformanceHintManager* create(sp iHintManager); + + sp mHintManager; + const int64_t mPreferredRateNanos; +}; + +struct APerformanceHintSession { +public: + APerformanceHintSession(sp session, int64_t preferredRateNanos, + int64_t targetDurationNanos); + APerformanceHintSession() = delete; + ~APerformanceHintSession(); + + int updateTargetWorkDuration(int64_t targetDurationNanos); + int reportActualWorkDuration(int64_t actualDurationNanos); + +private: + friend struct APerformanceHintManager; + + sp mHintSession; + // HAL preferred update rate + const int64_t mPreferredRateNanos; + // Target duration for choosing update rate + int64_t mTargetDurationNanos; + // Last update timestamp + int64_t mLastUpdateTimestamp; + // Cached samples + std::vector mActualDurationsNanos; + std::vector mTimestampsNanos; +}; + +static IHintManager* gIHintManagerForTesting = nullptr; +static APerformanceHintManager* gHintManagerForTesting = nullptr; + +// ===================================== APerformanceHintManager implementation +APerformanceHintManager::APerformanceHintManager(sp manager, + int64_t preferredRateNanos) + : mHintManager(std::move(manager)), mPreferredRateNanos(preferredRateNanos) {} + +APerformanceHintManager* APerformanceHintManager::getInstance() { + if (gHintManagerForTesting) return gHintManagerForTesting; + if (gIHintManagerForTesting) { + APerformanceHintManager* manager = create(gIHintManagerForTesting); + gIHintManagerForTesting = nullptr; + return manager; + } + static APerformanceHintManager* instance = create(nullptr); + return instance; +} + +APerformanceHintManager* APerformanceHintManager::create(sp manager) { + if (!manager) { + manager = interface_cast( + defaultServiceManager()->checkService(String16("performance_hint"))); + } + if (manager == nullptr) { + ALOGE("%s: PerformanceHint service is not ready ", __FUNCTION__); + return nullptr; + } + int64_t preferredRateNanos = -1L; + binder::Status ret = manager->getHintSessionPreferredRate(&preferredRateNanos); + if (!ret.isOk()) { + ALOGE("%s: PerformanceHint cannot get preferred rate. %s", __FUNCTION__, + ret.exceptionMessage().c_str()); + return nullptr; + } + if (preferredRateNanos <= 0) { + ALOGE("%s: PerformanceHint invalid preferred rate.", __FUNCTION__); + return nullptr; + } + return new APerformanceHintManager(std::move(manager), preferredRateNanos); +} + +APerformanceHintSession* APerformanceHintManager::createSession( + const int32_t* threadIds, size_t size, int64_t initialTargetWorkDurationNanos) { + sp token = sp::make(); + std::vector tids(threadIds, threadIds + size); + sp session; + binder::Status ret = + mHintManager->createHintSession(token, tids, initialTargetWorkDurationNanos, &session); + if (!ret.isOk() || !session) { + return nullptr; + } + return new APerformanceHintSession(std::move(session), mPreferredRateNanos, + initialTargetWorkDurationNanos); +} + +int64_t APerformanceHintManager::getPreferredRateNanos() const { + return mPreferredRateNanos; +} + +// ===================================== APerformanceHintSession implementation + +APerformanceHintSession::APerformanceHintSession(sp session, + int64_t preferredRateNanos, + int64_t targetDurationNanos) + : mHintSession(std::move(session)), + mPreferredRateNanos(preferredRateNanos), + mTargetDurationNanos(targetDurationNanos), + mLastUpdateTimestamp(elapsedRealtimeNano()) {} + +APerformanceHintSession::~APerformanceHintSession() { + binder::Status ret = mHintSession->close(); + if (!ret.isOk()) { + ALOGE("%s: HintSession close failed: %s", __FUNCTION__, ret.exceptionMessage().c_str()); + } +} + +int APerformanceHintSession::updateTargetWorkDuration(int64_t targetDurationNanos) { + if (targetDurationNanos <= 0) { + ALOGE("%s: targetDurationNanos must be positive", __FUNCTION__); + return EINVAL; + } + binder::Status ret = mHintSession->updateTargetWorkDuration(targetDurationNanos); + if (!ret.isOk()) { + ALOGE("%s: HintSessionn updateTargetWorkDuration failed: %s", __FUNCTION__, + ret.exceptionMessage().c_str()); + return EPIPE; + } + mTargetDurationNanos = targetDurationNanos; + /** + * Most of the workload is target_duration dependent, so now clear the cached samples + * as they are most likely obsolete. + */ + mActualDurationsNanos.clear(); + mTimestampsNanos.clear(); + mLastUpdateTimestamp = elapsedRealtimeNano(); + return 0; +} + +int APerformanceHintSession::reportActualWorkDuration(int64_t actualDurationNanos) { + if (actualDurationNanos <= 0) { + ALOGE("%s: actualDurationNanos must be positive", __FUNCTION__); + return EINVAL; + } + int64_t now = elapsedRealtimeNano(); + mActualDurationsNanos.push_back(actualDurationNanos); + mTimestampsNanos.push_back(now); + + /** + * Use current sample to determine the rate limit. We can pick a shorter rate limit + * if any sample underperformed, however, it could be the lower level system is slow + * to react. So here we explicitly choose the rate limit with the latest sample. + */ + int64_t rateLimit = actualDurationNanos > mTargetDurationNanos ? mPreferredRateNanos + : 10 * mPreferredRateNanos; + if (now - mLastUpdateTimestamp <= rateLimit) return 0; + + binder::Status ret = + mHintSession->reportActualWorkDuration(mActualDurationsNanos, mTimestampsNanos); + mActualDurationsNanos.clear(); + mTimestampsNanos.clear(); + if (!ret.isOk()) { + ALOGE("%s: HintSession reportActualWorkDuration failed: %s", __FUNCTION__, + ret.exceptionMessage().c_str()); + return EPIPE; + } + mLastUpdateTimestamp = now; + return 0; +} + +// ===================================== C API +APerformanceHintManager* APerformanceHint_getManager() { + return APerformanceHintManager::getInstance(); +} + +APerformanceHintSession* APerformanceHint_createSession(APerformanceHintManager* manager, + const int32_t* threadIds, size_t size, + int64_t initialTargetWorkDurationNanos) { + return manager->createSession(threadIds, size, initialTargetWorkDurationNanos); +} + +int64_t APerformanceHint_getPreferredUpdateRateNanos(APerformanceHintManager* manager) { + return manager->getPreferredRateNanos(); +} + +int APerformanceHint_updateTargetWorkDuration(APerformanceHintSession* session, + int64_t targetDurationNanos) { + return session->updateTargetWorkDuration(targetDurationNanos); +} + +int APerformanceHint_reportActualWorkDuration(APerformanceHintSession* session, + int64_t actualDurationNanos) { + return session->reportActualWorkDuration(actualDurationNanos); +} + +void APerformanceHint_closeSession(APerformanceHintSession* session) { + delete session; +} + +void APerformanceHint_setIHintManagerForTesting(void* iManager) { + delete gHintManagerForTesting; + gHintManagerForTesting = nullptr; + gIHintManagerForTesting = static_cast(iManager); +} diff --git a/native/android/tests/performance_hint/Android.bp b/native/android/tests/performance_hint/Android.bp new file mode 100644 index 0000000000000..fdc1bc6a20d8e --- /dev/null +++ b/native/android/tests/performance_hint/Android.bp @@ -0,0 +1,65 @@ +// Copyright (C) 2021 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 { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + +cc_test { + name: "PerformanceHintNativeTestCases", + + multilib: { + lib32: { + suffix: "32", + }, + lib64: { + suffix: "64", + }, + }, + + srcs: ["PerformanceHintNativeTest.cpp"], + + shared_libs: [ + "libandroid", + "liblog", + "libbinder", + "libpowermanager", + "libutils", + ], + + static_libs: [ + "libbase", + "libgmock", + "libgtest", + ], + stl: "c++_shared", + + test_suites: [ + "device-tests", + ], + + cflags: [ + "-Werror", + "-Wall", + ], + + header_libs: [ + "libandroid_headers_private", + ], +} diff --git a/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp b/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp new file mode 100644 index 0000000000000..284e9ee909ee8 --- /dev/null +++ b/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2021 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. + */ + +#define LOG_TAG "PerformanceHintNativeTest" + +#include +#include +#include +#include +#include +#include +#include +#include + +using android::binder::Status; +using android::os::IHintManager; +using android::os::IHintSession; + +using namespace android; +using namespace testing; + +class MockIHintManager : public IHintManager { +public: + MOCK_METHOD(Status, createHintSession, + (const ::android::sp<::android::IBinder>& token, const ::std::vector& tids, + int64_t durationNanos, ::android::sp<::android::os::IHintSession>* _aidl_return), + (override)); + MOCK_METHOD(Status, getHintSessionPreferredRate, (int64_t * _aidl_return), (override)); + MOCK_METHOD(IBinder*, onAsBinder, (), (override)); +}; + +class MockIHintSession : public IHintSession { +public: + MOCK_METHOD(Status, updateTargetWorkDuration, (int64_t targetDurationNanos), (override)); + MOCK_METHOD(Status, reportActualWorkDuration, + (const ::std::vector& actualDurationNanos, + const ::std::vector& timeStampNanos), + (override)); + MOCK_METHOD(Status, close, (), (override)); + MOCK_METHOD(IBinder*, onAsBinder, (), (override)); +}; + +class PerformanceHintTest : public Test { +public: + void SetUp() override { + mMockIHintManager = new StrictMock(); + APerformanceHint_setIHintManagerForTesting(mMockIHintManager); + } + + void TearDown() override { + mMockIHintManager = nullptr; + // Destroys MockIHintManager. + APerformanceHint_setIHintManagerForTesting(nullptr); + } + + APerformanceHintManager* createManager() { + EXPECT_CALL(*mMockIHintManager, getHintSessionPreferredRate(_)) + .Times(Exactly(1)) + .WillRepeatedly(DoAll(SetArgPointee<0>(123L), Return(Status()))); + return APerformanceHint_getManager(); + } + + StrictMock* mMockIHintManager = nullptr; +}; + +TEST_F(PerformanceHintTest, TestGetPreferredUpdateRateNanos) { + APerformanceHintManager* manager = createManager(); + int64_t preferredUpdateRateNanos = APerformanceHint_getPreferredUpdateRateNanos(manager); + EXPECT_EQ(123L, preferredUpdateRateNanos); +} + +TEST_F(PerformanceHintTest, TestSession) { + APerformanceHintManager* manager = createManager(); + + std::vector tids; + tids.push_back(1); + tids.push_back(2); + int64_t targetDuration = 56789L; + + StrictMock* iSession = new StrictMock(); + sp 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. + int64_t actualDurationNanos = 20; + std::vector actualDurations; + actualDurations.push_back(20); + EXPECT_CALL(*iSession, reportActualWorkDuration(Eq(actualDurations), _)).Times(Exactly(1)); + result = APerformanceHint_reportActualWorkDuration(session, actualDurationNanos); + EXPECT_EQ(0, result); + + result = APerformanceHint_updateTargetWorkDuration(session, -1L); + EXPECT_EQ(EINVAL, result); + result = APerformanceHint_reportActualWorkDuration(session, -1L); + EXPECT_EQ(EINVAL, result); + + EXPECT_CALL(*iSession, close()).Times(Exactly(1)); + APerformanceHint_closeSession(session); +}