Merge "Use NDK version of transaction completed callback" into 24D1-dev
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index d30ddca..3f795a6 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -105,6 +105,7 @@
 import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.server.inputmethod.InputMethodManagerInternal;
 import com.android.server.statusbar.StatusBarManagerInternal;
+import com.android.server.wm.utils.SurfaceControlUtils;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -1631,8 +1632,7 @@
         if (mTransactionCompletedListeners != null) {
             for (int i = 0; i < mTransactionCompletedListeners.size(); i++) {
                 final Runnable listener = mTransactionCompletedListeners.get(i);
-                transaction.addTransactionCompletedListener(Runnable::run,
-                        (stats) -> listener.run());
+                SurfaceControlUtils.addTransactionCompletedListener(transaction, listener);
             }
         }
 
diff --git a/services/core/java/com/android/server/wm/utils/SurfaceControlUtils.java b/services/core/java/com/android/server/wm/utils/SurfaceControlUtils.java
new file mode 100644
index 0000000..e658cc9
--- /dev/null
+++ b/services/core/java/com/android/server/wm/utils/SurfaceControlUtils.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+
+package com.android.server.wm.utils;
+
+import android.view.SurfaceControl;
+
+/**
+ * JNI wrapper for ASurfaceTransaction_setOnComplete NDK method which is not
+ * exposed as a Java API on older Android versions
+ */
+public class SurfaceControlUtils {
+
+    /**
+     * Adds a listener for transaction completion (when transaction is presented on the screen)
+     * @param transaction transaction to which add the listener
+     * @param runnable callback which will be called when transaction is presented
+     */
+    public static void addTransactionCompletedListener(SurfaceControl.Transaction transaction,
+            Runnable onComplete) {
+        nativeAddTransactionCompletedListener(transaction, onComplete);
+    }
+
+    private static native void nativeAddTransactionCompletedListener(
+        SurfaceControl.Transaction transaction, Runnable callback);
+}
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 3607ddd..a348d58 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -76,6 +76,7 @@
         "com_android_server_pm_PackageManagerShellCommandDataLoader.cpp",
         "com_android_server_sensor_SensorService.cpp",
         "com_android_server_wm_TaskFpsCallbackController.cpp",
+        "com_android_server_wm_utils_SurfaceControlUtils.cpp",
         "onload.cpp",
         ":lib_cachedAppOptimizer_native",
         ":lib_gameManagerService_native",
diff --git a/services/core/jni/com_android_server_wm_utils_SurfaceControlUtils.cpp b/services/core/jni/com_android_server_wm_utils_SurfaceControlUtils.cpp
new file mode 100644
index 0000000..d29b396
--- /dev/null
+++ b/services/core/jni/com_android_server_wm_utils_SurfaceControlUtils.cpp
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2024 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 "SurfaceControlUtils"
+
+#include <android_runtime/AndroidRuntime.h>
+#include <android_runtime/Log.h>
+#include <android/surface_control.h>
+#include <android/surface_control_jni.h>
+#include <nativehelper/JNIHelp.h>
+#include <utils/Log.h>
+#include <utils/RefBase.h>
+
+#include "core_jni_helpers.h"
+
+namespace android {
+
+namespace {
+
+static struct {
+    jclass clazz;
+    jmethodID run;
+} gRunnableClassInfo;
+
+class TransactionCompletedListenerWrapper {
+public:
+    explicit TransactionCompletedListenerWrapper(JNIEnv* env, jobject object) {
+        env->GetJavaVM(&mVm);
+        mTransactionCompletedListenerObject = env->NewGlobalRef(object);
+        LOG_ALWAYS_FATAL_IF(!mTransactionCompletedListenerObject, "Failed to make global ref");
+    }
+
+    ~TransactionCompletedListenerWrapper() {
+        getenv()->DeleteGlobalRef(mTransactionCompletedListenerObject);
+    }
+
+    void callback() {
+        JNIEnv* env = getenv();
+
+        env->CallVoidMethod(mTransactionCompletedListenerObject, gRunnableClassInfo.run);
+
+        DieIfException(env, "Uncaught exception in TransactionCompletedListener.");
+    }
+
+    static void transactionCallbackThunk(void* context, ASurfaceTransactionStats* stats) {
+        TransactionCompletedListenerWrapper* listener =
+                reinterpret_cast<TransactionCompletedListenerWrapper*>(context);
+        listener->callback();
+        delete listener;
+    }
+
+private:
+    jobject mTransactionCompletedListenerObject;
+    JavaVM* mVm;
+
+    JNIEnv* getenv() {
+        JNIEnv* env;
+        mVm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6);
+        return env;
+    }
+};
+
+static void nativeAddTransactionCompletedListener(JNIEnv* env, jclass clazz,
+                                                  jobject transactionObj,
+                                                  jobject transactionCompletedListener) {
+    ASurfaceTransaction* transaction = ASurfaceTransaction_fromJava(env, transactionObj);
+    auto context = new TransactionCompletedListenerWrapper(env, transactionCompletedListener);
+    ASurfaceTransaction_setOnComplete(transaction, reinterpret_cast<void*>(context),
+                                      TransactionCompletedListenerWrapper::
+                                                         transactionCallbackThunk);
+}
+
+static const JNINativeMethod gMethods[] = {
+        /* name, signature, funcPtr */
+        {"nativeAddTransactionCompletedListener",
+        "(Landroid/view/SurfaceControl$Transaction;Ljava/lang/Runnable;)V",
+        (void*) nativeAddTransactionCompletedListener}};
+} // namespace
+
+int register_com_android_server_wm_utils_SurfaceControlUtils(JNIEnv* env) {
+    int res = jniRegisterNativeMethods(env, "com/android/server/wm/utils/SurfaceControlUtils",
+                                       gMethods, NELEM(gMethods));
+    LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register native methods.");
+
+    jclass runnableClazz = FindClassOrDie(env, "java/lang/Runnable");
+    gRunnableClassInfo.clazz = MakeGlobalRefOrDie(env, runnableClazz);
+    gRunnableClassInfo.run = GetMethodIDOrDie(env, runnableClazz, "run", "()V");
+
+    return 0;
+}
+
+} // namespace android
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index 0936888..dbca8d1 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -67,6 +67,7 @@
 int register_android_server_companion_virtual_InputController(JNIEnv* env);
 int register_android_server_app_GameManagerService(JNIEnv* env);
 int register_com_android_server_wm_TaskFpsCallbackController(JNIEnv* env);
+int register_com_android_server_wm_utils_SurfaceControlUtils(JNIEnv* env);
 int register_com_android_server_display_DisplayControl(JNIEnv* env);
 int register_com_android_server_SystemClockTime(JNIEnv* env);
 int register_android_server_display_smallAreaDetectionController(JNIEnv* env);
@@ -130,6 +131,7 @@
     register_android_server_companion_virtual_InputController(env);
     register_android_server_app_GameManagerService(env);
     register_com_android_server_wm_TaskFpsCallbackController(env);
+    register_com_android_server_wm_utils_SurfaceControlUtils(env);
     register_com_android_server_display_DisplayControl(env);
     register_com_android_server_SystemClockTime(env);
     register_android_server_display_smallAreaDetectionController(env);