Merge changes from topic "native_hint" into sc-dev am: ea52c87596

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/15326475

Change-Id: I9e341b02a701d6a20ce8b60cacfc647beae219b2
This commit is contained in:
TreeHugger Robot
2021-07-22 21:34:10 +00:00
committed by Automerger Merge Worker
6 changed files with 204 additions and 199 deletions

View File

@@ -158,7 +158,6 @@ import android.os.IBatteryPropertiesRegistrar;
import android.os.IBinder; import android.os.IBinder;
import android.os.IDumpstate; import android.os.IDumpstate;
import android.os.IHardwarePropertiesManager; import android.os.IHardwarePropertiesManager;
import android.os.IHintManager;
import android.os.IPowerManager; import android.os.IPowerManager;
import android.os.IRecoverySystem; import android.os.IRecoverySystem;
import android.os.ISystemUpdateManager; import android.os.ISystemUpdateManager;
@@ -600,10 +599,7 @@ public final class SystemServiceRegistry {
@Override @Override
public PerformanceHintManager createService(ContextImpl ctx) public PerformanceHintManager createService(ContextImpl ctx)
throws ServiceNotFoundException { throws ServiceNotFoundException {
IBinder hintBinder = ServiceManager.getServiceOrThrow( return PerformanceHintManager.create();
Context.PERFORMANCE_HINT_SERVICE);
IHintManager hintService = IHintManager.Stub.asInterface(hintBinder);
return new PerformanceHintManager(hintService);
}}); }});
registerService(Context.RECOVERY_SERVICE, RecoverySystem.class, registerService(Context.RECOVERY_SERVICE, RecoverySystem.class,

View File

@@ -24,24 +24,23 @@ import android.content.Context;
import com.android.internal.util.Preconditions; import com.android.internal.util.Preconditions;
import java.io.Closeable; import java.io.Closeable;
import java.util.ArrayList;
/** The PerformanceHintManager allows apps to send performance hint to system. */ /** The PerformanceHintManager allows apps to send performance hint to system. */
@SystemService(Context.PERFORMANCE_HINT_SERVICE) @SystemService(Context.PERFORMANCE_HINT_SERVICE)
public final class PerformanceHintManager { public final class PerformanceHintManager {
private static final String TAG = "PerformanceHintManager"; private final long mNativeManagerPtr;
private final IHintManager mService;
// HAL preferred update rate
private final long mPreferredRate;
/** @hide */ /** @hide */
public PerformanceHintManager(IHintManager service) { public static PerformanceHintManager create() throws ServiceManager.ServiceNotFoundException {
mService = service; long nativeManagerPtr = nativeAcquireManager();
try { if (nativeManagerPtr == 0) {
mPreferredRate = mService.getHintSessionPreferredRate(); throw new ServiceManager.ServiceNotFoundException(Context.PERFORMANCE_HINT_SERVICE);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
} }
return new PerformanceHintManager(nativeManagerPtr);
}
private PerformanceHintManager(long nativeManagerPtr) {
mNativeManagerPtr = nativeManagerPtr;
} }
/** /**
@@ -57,16 +56,13 @@ public final class PerformanceHintManager {
*/ */
@Nullable @Nullable
public Session createHintSession(@NonNull int[] tids, long initialTargetWorkDurationNanos) { public Session createHintSession(@NonNull int[] tids, long initialTargetWorkDurationNanos) {
try { Preconditions.checkNotNull(tids, "tids cannot be null");
IBinder token = new Binder(); Preconditions.checkArgumentPositive(initialTargetWorkDurationNanos,
IHintSession session = mService.createHintSession(token, tids, "the hint target duration should be positive.");
long nativeSessionPtr = nativeCreateSession(mNativeManagerPtr, tids,
initialTargetWorkDurationNanos); initialTargetWorkDurationNanos);
if (session == null) return null; if (nativeSessionPtr == 0) return null;
return new Session(session, sNanoClock, mPreferredRate, return new Session(nativeSessionPtr);
initialTargetWorkDurationNanos);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
} }
/** /**
@@ -75,7 +71,7 @@ public final class PerformanceHintManager {
* @return the preferred update rate supported by device software. * @return the preferred update rate supported by device software.
*/ */
public long getPreferredUpdateRateNanos() { public long getPreferredUpdateRateNanos() {
return mPreferredRate; return nativeGetPreferredUpdateRateNanos(mNativeManagerPtr);
} }
/** /**
@@ -101,28 +97,21 @@ public final class PerformanceHintManager {
* <p>All timings should be in {@link SystemClock#elapsedRealtimeNanos()}.</p> * <p>All timings should be in {@link SystemClock#elapsedRealtimeNanos()}.</p>
*/ */
public static class Session implements Closeable { public static class Session implements Closeable {
private final IHintSession mSession; private long mNativeSessionPtr;
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<Long> mActualDurationNanos;
private final ArrayList<Long> mTimeStampNanos;
/** @hide */ /** @hide */
public Session(IHintSession session, NanoClock elapsedRealtimeClock, long preferredRate, public Session(long nativeSessionPtr) {
long durationNanos) { mNativeSessionPtr = nativeSessionPtr;
mSession = session; }
mElapsedRealtimeClock = elapsedRealtimeClock;
mTargetDurationInNanos = durationNanos; /** @hide */
mPreferredRate = preferredRate; @Override
mActualDurationNanos = new ArrayList<Long>(); protected void finalize() throws Throwable {
mTimeStampNanos = new ArrayList<Long>(); try {
mLastUpdateTimeStamp = mElapsedRealtimeClock.nanos(); close();
} finally {
super.finalize();
}
} }
/** /**
@@ -133,19 +122,7 @@ public final class PerformanceHintManager {
public void updateTargetWorkDuration(long targetDurationNanos) { public void updateTargetWorkDuration(long targetDurationNanos) {
Preconditions.checkArgumentPositive(targetDurationNanos, "the hint target duration" Preconditions.checkArgumentPositive(targetDurationNanos, "the hint target duration"
+ " should be positive."); + " should be positive.");
try { nativeUpdateTargetWorkDuration(mNativeSessionPtr, targetDurationNanos);
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();
} }
/** /**
@@ -161,38 +138,7 @@ public final class PerformanceHintManager {
public void reportActualWorkDuration(long actualDurationNanos) { public void reportActualWorkDuration(long actualDurationNanos) {
Preconditions.checkArgumentPositive(actualDurationNanos, "the actual duration should" Preconditions.checkArgumentPositive(actualDurationNanos, "the actual duration should"
+ " be positive."); + " be positive.");
final long now = mElapsedRealtimeClock.nanos(); nativeReportActualWorkDuration(mNativeSessionPtr, actualDurationNanos);
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;
} }
/** /**
@@ -201,26 +147,20 @@ public final class PerformanceHintManager {
* <p>Once called, you should not call anything else on this object.</p> * <p>Once called, you should not call anything else on this object.</p>
*/ */
public void close() { public void close() {
try { if (mNativeSessionPtr != 0) {
mSession.close(); nativeCloseSession(mNativeSessionPtr);
} catch (RemoteException e) { mNativeSessionPtr = 0;
throw e.rethrowFromSystemServer();
} }
} }
} }
/** private static native long nativeAcquireManager();
* The interface is to make the FakeClock for testing. private static native long nativeGetPreferredUpdateRateNanos(long nativeManagerPtr);
* @hide private static native long nativeCreateSession(long nativeManagerPtr,
*/ int[] tids, long initialTargetWorkDurationNanos);
public interface NanoClock { private static native void nativeUpdateTargetWorkDuration(long nativeSessionPtr,
/** Gets the current nanosecond instant of the clock. */ long targetDurationNanos);
long nanos(); private static native void nativeReportActualWorkDuration(long nativeSessionPtr,
} long actualDurationNanos);
private static native void nativeCloseSession(long nativeSessionPtr);
private static final NanoClock sNanoClock = new NanoClock() {
public long nanos() {
return SystemClock.elapsedRealtimeNanos();
}
};
} }

View File

@@ -146,6 +146,7 @@ cc_library_shared {
"android_os_MemoryFile.cpp", "android_os_MemoryFile.cpp",
"android_os_MessageQueue.cpp", "android_os_MessageQueue.cpp",
"android_os_Parcel.cpp", "android_os_Parcel.cpp",
"android_os_PerformanceHintManager.cpp",
"android_os_SELinux.cpp", "android_os_SELinux.cpp",
"android_os_ServiceManager.cpp", "android_os_ServiceManager.cpp",
"android_os_SharedMemory.cpp", "android_os_SharedMemory.cpp",

View File

@@ -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_ServiceManager(JNIEnv *env);
extern int register_android_os_MessageQueue(JNIEnv* env); extern int register_android_os_MessageQueue(JNIEnv* env);
extern int register_android_os_Parcel(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_SELinux(JNIEnv* env);
extern int register_android_os_VintfObject(JNIEnv *env); extern int register_android_os_VintfObject(JNIEnv *env);
extern int register_android_os_VintfRuntimeInfo(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_SystemProperties),
REG_JNI(register_android_os_Binder), REG_JNI(register_android_os_Binder),
REG_JNI(register_android_os_Parcel), REG_JNI(register_android_os_Parcel),
REG_JNI(register_android_os_PerformanceHintManager),
REG_JNI(register_android_os_HidlMemory), REG_JNI(register_android_os_HidlMemory),
REG_JNI(register_android_os_HidlSupport), REG_JNI(register_android_os_HidlSupport),
REG_JNI(register_android_os_HwBinder), REG_JNI(register_android_os_HwBinder),

View File

@@ -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 <dlfcn.h>
#include <nativehelper/JNIHelp.h>
#include <nativehelper/ScopedPrimitiveArray.h>
#include <utils/Log.h>
#include <vector>
#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<jlong>(gAPH_getManagerFn());
}
static jlong nativeGetPreferredUpdateRateNanos(JNIEnv* env, jclass clazz, jlong nativeManagerPtr) {
ensureAPerformanceHintBindingInitialized();
return gAPH_getPreferredUpdateRateNanosFn(
reinterpret_cast<APerformanceHintManager*>(nativeManagerPtr));
}
static jlong nativeCreateSession(JNIEnv* env, jclass clazz, jlong nativeManagerPtr, jintArray tids,
jlong initialTargetWorkDurationNanos) {
ensureAPerformanceHintBindingInitialized();
if (tids == nullptr) return 0;
std::vector<int32_t> tidsVector;
ScopedIntArrayRO tidsArray(env, tids);
for (size_t i = 0; i < tidsArray.size(); ++i) {
tidsVector.push_back(static_cast<int32_t>(tidsArray[i]));
}
return reinterpret_cast<jlong>(
gAPH_createSessionFn(reinterpret_cast<APerformanceHintManager*>(nativeManagerPtr),
tidsVector.data(), tidsVector.size(),
initialTargetWorkDurationNanos));
}
static void nativeUpdateTargetWorkDuration(JNIEnv* env, jclass clazz, jlong nativeSessionPtr,
jlong targetDurationNanos) {
ensureAPerformanceHintBindingInitialized();
gAPH_updateTargetWorkDurationFn(reinterpret_cast<APerformanceHintSession*>(nativeSessionPtr),
targetDurationNanos);
}
static void nativeReportActualWorkDuration(JNIEnv* env, jclass clazz, jlong nativeSessionPtr,
jlong actualDurationNanos) {
ensureAPerformanceHintBindingInitialized();
gAPH_reportActualWorkDurationFn(reinterpret_cast<APerformanceHintSession*>(nativeSessionPtr),
actualDurationNanos);
}
static void nativeCloseSession(JNIEnv* env, jclass clazz, jlong nativeSessionPtr) {
ensureAPerformanceHintBindingInitialized();
gAPH_closeSessionFn(reinterpret_cast<APerformanceHintSession*>(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

View File

@@ -22,12 +22,6 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeNotNull; 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; 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 @Test
public void testCloseHintSession() { public void testCloseHintSession() {
Session s = createSession(); Session s = createSession();
assumeNotNull(s); assumeNotNull(s);
s.close(); 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;
}
}
} }