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
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 5f2f623..9424b51 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -3132,6 +3132,9 @@
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 91febcd..3dc79cf 100644
--- a/core/java/android/view/Choreographer.java
+++ b/core/java/android/view/Choreographer.java
@@ -270,10 +270,14 @@
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;
@@ -313,6 +317,26 @@
}
/**
+ * 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
*/
@@ -334,6 +358,15 @@
}
/**
+ * Dispose the DisplayEventReceiver on the Choreographer.
+ * @hide
+ */
+ @UnsupportedAppUsage
+ void invalidate() {
+ dispose();
+ }
+
+ /**
* The amount of time, in milliseconds, between each frame of the animation.
* <p>
* This is a requested time that the animation will attempt to honor, but the actual delay
@@ -1166,8 +1199,8 @@
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 ce7606a0..26fda34 100644
--- a/core/java/android/view/DisplayEventReceiver.java
+++ b/core/java/android/view/DisplayEventReceiver.java
@@ -80,7 +80,7 @@
private MessageQueue mMessageQueue;
private static native long nativeInit(WeakReference<DisplayEventReceiver> 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 @@
*/
@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 @@
* @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<DisplayEventReceiver>(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 54e1a53..b003659 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -64,6 +64,7 @@
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 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")
@@ -1269,6 +1274,59 @@
}
/**
+ * 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 @@
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 a8d8a43..8855b78 100644
--- a/core/jni/android_view_DisplayEventReceiver.cpp
+++ b/core/jni/android_view_DisplayEventReceiver.cpp
@@ -65,7 +65,7 @@
public:
NativeDisplayEventReceiver(JNIEnv* env, jobject receiverWeak,
const sp<MessageQueue>& messageQueue, jint vsyncSource,
- jint eventRegistration);
+ jint eventRegistration, jlong layerHandle);
void dispose();
@@ -88,11 +88,15 @@
NativeDisplayEventReceiver::NativeDisplayEventReceiver(JNIEnv* env, jobject receiverWeak,
const sp<MessageQueue>& messageQueue,
- jint vsyncSource, jint eventRegistration)
+ jint vsyncSource, jint eventRegistration,
+ jlong layerHandle)
: DisplayEventDispatcher(messageQueue->getLooper(),
static_cast<gui::ISurfaceComposer::VsyncSource>(vsyncSource),
static_cast<gui::ISurfaceComposer::EventRegistration>(
- eventRegistration)),
+ eventRegistration),
+ layerHandle != 0 ? sp<IBinder>::fromExisting(
+ reinterpret_cast<IBinder*>(layerHandle))
+ : nullptr),
mReceiverWeakGlobal(env->NewGlobalRef(receiverWeak)),
mMessageQueue(messageQueue) {
ALOGV("receiver %p ~ Initializing display event receiver.", this);
@@ -214,7 +218,7 @@
}
static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak, jobject messageQueueObj,
- jint vsyncSource, jint eventRegistration) {
+ jint vsyncSource, jint eventRegistration, jlong layerHandle) {
sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
if (messageQueue == NULL) {
jniThrowRuntimeException(env, "MessageQueue is not initialized.");
@@ -223,7 +227,7 @@
sp<NativeDisplayEventReceiver> receiver =
new NativeDisplayEventReceiver(env, receiverWeak, messageQueue, vsyncSource,
- eventRegistration);
+ eventRegistration, layerHandle);
status_t status = receiver->initialize();
if (status) {
String8 message;
@@ -268,7 +272,7 @@
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