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 e2745959e15d3..ce6101f9d9edd 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 09d7ef0181a51..97cac291630cd 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -144,6 +144,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);
@@ -1523,6 +1524,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;
- }
- }
}