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; - } - } }