From b13f16cfcdf9437e15aceeddfd8c7bcaf563f1ae Mon Sep 17 00:00:00 2001 From: ramindani Date: Mon, 21 Nov 2022 18:51:05 -0800 Subject: [PATCH] Attach the Choreographer instance with SurfaceControl The attached choreographer will register with the event receiver through native api. Test: atest ChoreographerTest SurfaceControlTest BUG: 258235154 Change-Id: I33c0e686a1fd42becc0bfb1ae0185efd673e7b5a --- core/api/test-current.txt | 3 + core/java/android/view/Choreographer.java | 39 ++++++++++- .../android/view/DisplayEventReceiver.java | 14 ++-- core/java/android/view/SurfaceControl.java | 65 +++++++++++++++++++ .../jni/android_view_DisplayEventReceiver.cpp | 16 +++-- 5 files changed, 124 insertions(+), 13 deletions(-) diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 5f2f623683762..9424b51327aae 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -3132,6 +3132,9 @@ package android.view { public final class SurfaceControl implements android.os.Parcelable { ctor public SurfaceControl(@NonNull android.view.SurfaceControl, @NonNull String); + method @NonNull public android.view.Choreographer getChoreographer(); + method @NonNull public android.view.Choreographer getChoreographer(@NonNull android.os.Looper); + method public boolean hasChoreographer(); method public boolean isSameSurface(@NonNull android.view.SurfaceControl); } diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java index 91febcdcb0c44..3dc79cf13422f 100644 --- a/core/java/android/view/Choreographer.java +++ b/core/java/android/view/Choreographer.java @@ -270,10 +270,14 @@ public final class Choreographer { private static final int CALLBACK_LAST = CALLBACK_COMMIT; private Choreographer(Looper looper, int vsyncSource) { + this(looper, vsyncSource, /* layerHandle */ 0L); + } + + private Choreographer(Looper looper, int vsyncSource, long layerHandle) { mLooper = looper; mHandler = new FrameHandler(looper); mDisplayEventReceiver = USE_VSYNC - ? new FrameDisplayEventReceiver(looper, vsyncSource) + ? new FrameDisplayEventReceiver(looper, vsyncSource, layerHandle) : null; mLastFrameTimeNanos = Long.MIN_VALUE; @@ -312,6 +316,26 @@ public final class Choreographer { return sSfThreadInstance.get(); } + /** + * Gets the choreographer associated with the SurfaceControl. + * + * @param layerHandle to which the choreographer will be attached. + * @param looper the choreographer is attached on this looper. + * + * @return The choreographer for the looper which is attached + * to the sourced SurfaceControl::mNativeHandle. + * @throws IllegalStateException if the looper sourced is null. + * @hide + */ + @NonNull + static Choreographer getInstanceForSurfaceControl(long layerHandle, + @NonNull Looper looper) { + if (looper == null) { + throw new IllegalStateException("The current thread must have a looper!"); + } + return new Choreographer(looper, VSYNC_SOURCE_APP, layerHandle); + } + /** * @return The Choreographer of the main thread, if it exists, or {@code null} otherwise. * @hide @@ -333,6 +357,15 @@ public final class Choreographer { mDisplayEventReceiver.dispose(); } + /** + * Dispose the DisplayEventReceiver on the Choreographer. + * @hide + */ + @UnsupportedAppUsage + void invalidate() { + dispose(); + } + /** * The amount of time, in milliseconds, between each frame of the animation. *

@@ -1166,8 +1199,8 @@ public final class Choreographer { private int mFrame; private VsyncEventData mLastVsyncEventData = new VsyncEventData(); - public FrameDisplayEventReceiver(Looper looper, int vsyncSource) { - super(looper, vsyncSource, 0); + FrameDisplayEventReceiver(Looper looper, int vsyncSource, long layerHandle) { + super(looper, vsyncSource, /* eventRegistration */ 0, layerHandle); } // TODO(b/116025192): physicalDisplayId is ignored because SF only emits VSYNC events for diff --git a/core/java/android/view/DisplayEventReceiver.java b/core/java/android/view/DisplayEventReceiver.java index ce7606a0cf70d..26fda3444c4ec 100644 --- a/core/java/android/view/DisplayEventReceiver.java +++ b/core/java/android/view/DisplayEventReceiver.java @@ -80,7 +80,7 @@ public abstract class DisplayEventReceiver { private MessageQueue mMessageQueue; private static native long nativeInit(WeakReference receiver, - MessageQueue messageQueue, int vsyncSource, int eventRegistration); + MessageQueue messageQueue, int vsyncSource, int eventRegistration, long layerHandle); private static native void nativeDispose(long receiverPtr); @FastNative private static native void nativeScheduleVsync(long receiverPtr); @@ -93,7 +93,11 @@ public abstract class DisplayEventReceiver { */ @UnsupportedAppUsage public DisplayEventReceiver(Looper looper) { - this(looper, VSYNC_SOURCE_APP, 0); + this(looper, VSYNC_SOURCE_APP, /* eventRegistration */ 0, /* layerHandle */ 0L); + } + + public DisplayEventReceiver(Looper looper, int vsyncSource, int eventRegistration) { + this(looper, vsyncSource, eventRegistration, /* layerHandle */ 0L); } /** @@ -103,15 +107,17 @@ public abstract class DisplayEventReceiver { * @param vsyncSource The source of the vsync tick. Must be on of the VSYNC_SOURCE_* values. * @param eventRegistration Which events to dispatch. Must be a bitfield consist of the * EVENT_REGISTRATION_*_FLAG values. + * @param layerHandle Layer to which the current instance is attached to */ - public DisplayEventReceiver(Looper looper, int vsyncSource, int eventRegistration) { + public DisplayEventReceiver(Looper looper, int vsyncSource, int eventRegistration, + long layerHandle) { if (looper == null) { throw new IllegalArgumentException("looper must not be null"); } mMessageQueue = looper.getQueue(); mReceiverPtr = nativeInit(new WeakReference(this), mMessageQueue, - vsyncSource, eventRegistration); + vsyncSource, eventRegistration, layerHandle); } @Override diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index 54e1a53ed7c99..b003659affd32 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -64,6 +64,7 @@ import android.opengl.EGLDisplay; import android.opengl.EGLSync; import android.os.Build; import android.os.IBinder; +import android.os.Looper; import android.os.Parcel; import android.os.Parcelable; import android.os.RemoteException; @@ -463,6 +464,10 @@ public final class SurfaceControl implements Parcelable { public long mNativeObject; private long mNativeHandle; + private final Object mChoreographerLock = new Object(); + @GuardedBy("mChoreographerLock") + private Choreographer mChoreographer; + // TODO: Move width/height to native and fix locking through out. private final Object mLock = new Object(); @GuardedBy("mLock") @@ -1268,6 +1273,59 @@ public final class SurfaceControl implements Parcelable { return other.mNativeHandle == mNativeHandle; } + /** + * Returns the associated {@link Choreographer} instance with the + * current instance of the SurfaceControl. + * Must be called from a thread that already has a {@link android.os.Looper} + * associated with it. + * If there is no {@link Choreographer} associated with the SurfaceControl then a new instance + * of the {@link Choreographer} is created. + * + * @hide + */ + @TestApi + public @NonNull Choreographer getChoreographer() { + return getChoreographer(Looper.myLooper()); + } + + /** + * Returns the associated {@link Choreographer} instance with the + * current instance of the SurfaceControl. + * If there is no {@link Choreographer} associated with the SurfaceControl then a new instance + * of the {@link Choreographer} is created. + * + * @param looper the choreographer is attached on this looper + * + * @hide + */ + @TestApi + public @NonNull Choreographer getChoreographer(@NonNull Looper looper) { + checkNotReleased(); + synchronized (mChoreographerLock) { + if (mChoreographer != null) { + return mChoreographer; + } + + mChoreographer = Choreographer.getInstanceForSurfaceControl(mNativeHandle, looper); + return mChoreographer; + } + } + + /** + * Returns true if {@link Choreographer} is present otherwise false. + * To check the validity use {@link #isValid} on the SurfaceControl, a valid SurfaceControl with + * choreographer will have the valid Choreographer. + * + * @hide + */ + @TestApi + @UnsupportedAppUsage + public boolean hasChoreographer() { + synchronized (mChoreographerLock) { + return mChoreographer != null; + } + } + /** * Write to a protocol buffer output stream. Protocol buffer message definition is at {@link * android.view.SurfaceControlProto}. @@ -1325,6 +1383,13 @@ public final class SurfaceControl implements Parcelable { mNativeObject = 0; mNativeHandle = 0; mCloseGuard.close(); + synchronized (mChoreographerLock) { + if (mChoreographer != null) { + mChoreographer.invalidate(); + // TODO(b/266121235): Use NativeAllocationRegistry to clean up Choreographer. + mChoreographer = null; + } + } } } diff --git a/core/jni/android_view_DisplayEventReceiver.cpp b/core/jni/android_view_DisplayEventReceiver.cpp index a8d8a431666a2..8855b7806cc18 100644 --- a/core/jni/android_view_DisplayEventReceiver.cpp +++ b/core/jni/android_view_DisplayEventReceiver.cpp @@ -65,7 +65,7 @@ class NativeDisplayEventReceiver : public DisplayEventDispatcher { public: NativeDisplayEventReceiver(JNIEnv* env, jobject receiverWeak, const sp& messageQueue, jint vsyncSource, - jint eventRegistration); + jint eventRegistration, jlong layerHandle); void dispose(); @@ -88,11 +88,15 @@ private: NativeDisplayEventReceiver::NativeDisplayEventReceiver(JNIEnv* env, jobject receiverWeak, const sp& messageQueue, - jint vsyncSource, jint eventRegistration) + jint vsyncSource, jint eventRegistration, + jlong layerHandle) : DisplayEventDispatcher(messageQueue->getLooper(), static_cast(vsyncSource), static_cast( - eventRegistration)), + eventRegistration), + layerHandle != 0 ? sp::fromExisting( + reinterpret_cast(layerHandle)) + : nullptr), mReceiverWeakGlobal(env->NewGlobalRef(receiverWeak)), mMessageQueue(messageQueue) { ALOGV("receiver %p ~ Initializing display event receiver.", this); @@ -214,7 +218,7 @@ void NativeDisplayEventReceiver::dispatchFrameRateOverrides( } static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak, jobject messageQueueObj, - jint vsyncSource, jint eventRegistration) { + jint vsyncSource, jint eventRegistration, jlong layerHandle) { sp messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj); if (messageQueue == NULL) { jniThrowRuntimeException(env, "MessageQueue is not initialized."); @@ -223,7 +227,7 @@ static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak, jobject sp receiver = new NativeDisplayEventReceiver(env, receiverWeak, messageQueue, vsyncSource, - eventRegistration); + eventRegistration, layerHandle); status_t status = receiver->initialize(); if (status) { String8 message; @@ -268,7 +272,7 @@ static jobject nativeGetLatestVsyncEventData(JNIEnv* env, jclass clazz, jlong re static const JNINativeMethod gMethods[] = { /* name, signature, funcPtr */ - {"nativeInit", "(Ljava/lang/ref/WeakReference;Landroid/os/MessageQueue;II)J", + {"nativeInit", "(Ljava/lang/ref/WeakReference;Landroid/os/MessageQueue;IIJ)J", (void*)nativeInit}, {"nativeDispose", "(J)V", (void*)nativeDispose}, // @FastNative