From b94da308c86bed6b89043aaced3724c2710ab4a0 Mon Sep 17 00:00:00 2001
From: Chavi Weingarten <chaviw@google.com>
Date: Thu, 29 Feb 2024 16:33:27 +0000
Subject: [PATCH] Add native InputTransferToken

Added a native class that corresponds to the Java InputTransferToken.

Test: SurfaceControlInputReceiverTests
Test: AInputTransferTokenTest
Bug: 324271765
Change-Id: Ic5549227ad8c8ab311f8953eaf370e1a2896d8f3
---
 .../android/window/InputTransferToken.java    |  44 ++++--
 core/jni/Android.bp                           |   1 +
 core/jni/AndroidRuntime.cpp                   |   2 +
 .../jni/android_window_InputTransferToken.cpp | 138 ++++++++++++++++++
 .../android_window_InputTransferToken.h       |  33 +++++
 native/android/Android.bp                     |   1 +
 native/android/input_transfer_token.cpp       |  63 ++++++++
 native/android/libandroid.map.txt             |   3 +
 .../server/wm/WindowManagerService.java       |   7 +-
 9 files changed, 279 insertions(+), 13 deletions(-)
 create mode 100644 core/jni/android_window_InputTransferToken.cpp
 create mode 100644 core/jni/include/android_runtime/android_window_InputTransferToken.h
 create mode 100644 native/android/input_transfer_token.cpp

diff --git a/core/java/android/window/InputTransferToken.java b/core/java/android/window/InputTransferToken.java
index e572853e5d5d..c62eee40da98 100644
--- a/core/java/android/window/InputTransferToken.java
+++ b/core/java/android/window/InputTransferToken.java
@@ -18,7 +18,6 @@ package android.window;
 
 import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
-import android.os.Binder;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Parcel;
@@ -30,6 +29,8 @@ import android.view.SurfaceControlViewHost;
 
 import com.android.window.flags.Flags;
 
+import libcore.util.NativeAllocationRegistry;
+
 import java.util.Objects;
 
 /**
@@ -51,28 +52,51 @@ import java.util.Objects;
  */
 @FlaggedApi(Flags.FLAG_SURFACE_CONTROL_INPUT_RECEIVER)
 public final class InputTransferToken implements Parcelable {
+    private static native long nativeCreate();
+    private static native long nativeCreate(IBinder token);
+    private static native void nativeWriteToParcel(long nativeObject, Parcel out);
+    private static native long nativeReadFromParcel(Parcel in);
+    private static native IBinder nativeGetBinderToken(long nativeObject);
+    private static native long nativeGetNativeInputTransferTokenFinalizer();
+    private static native boolean nativeEquals(long nativeObject1, long nativeObject2);
+
+    private static final NativeAllocationRegistry sRegistry =
+            NativeAllocationRegistry.createMalloced(InputTransferToken.class.getClassLoader(),
+                    nativeGetNativeInputTransferTokenFinalizer());
+
     /**
      * @hide
      */
-    @NonNull
-    public final IBinder mToken;
+    public final long mNativeObject;
+
+    private InputTransferToken(long nativeObject) {
+        mNativeObject = nativeObject;
+        sRegistry.registerNativeAllocation(this, nativeObject);
+    }
 
     /**
      * @hide
      */
     public InputTransferToken(@NonNull IBinder token) {
-        mToken = token;
+        this(nativeCreate(token));
     }
 
     /**
      * @hide
      */
     public InputTransferToken() {
-        mToken = new Binder();
+        this(nativeCreate());
+    }
+
+    /**
+     * @hide
+     */
+    public IBinder getToken() {
+        return nativeGetBinderToken(mNativeObject);
     }
 
     private InputTransferToken(Parcel in) {
-        mToken = in.readStrongBinder();
+        this(nativeReadFromParcel(in));
     }
 
     /**
@@ -88,7 +112,7 @@ public final class InputTransferToken implements Parcelable {
      */
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
-        dest.writeStrongBinder(mToken);
+        nativeWriteToParcel(mNativeObject, dest);
     }
 
     public static final @NonNull Creator<InputTransferToken> CREATOR = new Creator<>() {
@@ -101,13 +125,12 @@ public final class InputTransferToken implements Parcelable {
         }
     };
 
-
     /**
      * @hide
      */
     @Override
     public int hashCode() {
-        return Objects.hash(mToken);
+        return Objects.hash(getToken());
     }
 
     /**
@@ -118,7 +141,8 @@ public final class InputTransferToken implements Parcelable {
         if (this == obj) return true;
         if (obj == null || getClass() != obj.getClass()) return false;
         InputTransferToken other = (InputTransferToken) obj;
-        return other.mToken == mToken;
+        if (other.mNativeObject == mNativeObject) return true;
+        return nativeEquals(mNativeObject, other.mNativeObject);
     }
 
 }
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 76e71380017c..77c6436a8c43 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -265,6 +265,7 @@ cc_library_shared_for_libandroid_runtime {
                 "fd_utils.cpp",
                 "android_hardware_input_InputWindowHandle.cpp",
                 "android_hardware_input_InputApplicationHandle.cpp",
+                "android_window_InputTransferToken.cpp",
                 "android_window_WindowInfosListener.cpp",
                 "android_window_ScreenCapture.cpp",
                 "jni_common.cpp",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index aa63f4fa03d4..9bbd19122153 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -221,6 +221,7 @@ extern int register_jni_common(JNIEnv* env);
 extern int register_android_tracing_PerfettoDataSource(JNIEnv* env);
 extern int register_android_tracing_PerfettoDataSourceInstance(JNIEnv* env);
 extern int register_android_tracing_PerfettoProducer(JNIEnv* env);
+extern int register_android_window_InputTransferToken(JNIEnv* env);
 
 // Namespace for Android Runtime flags applied during boot time.
 static const char* RUNTIME_NATIVE_BOOT_NAMESPACE = "runtime_native_boot";
@@ -1678,6 +1679,7 @@ static const RegJNIRec gRegJNI[] = {
         REG_JNI(register_android_tracing_PerfettoDataSource),
         REG_JNI(register_android_tracing_PerfettoDataSourceInstance),
         REG_JNI(register_android_tracing_PerfettoProducer),
+        REG_JNI(register_android_window_InputTransferToken),
 };
 
 /*
diff --git a/core/jni/android_window_InputTransferToken.cpp b/core/jni/android_window_InputTransferToken.cpp
new file mode 100644
index 000000000000..60568e30ccb2
--- /dev/null
+++ b/core/jni/android_window_InputTransferToken.cpp
@@ -0,0 +1,138 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "InputTransferToken"
+
+#include <android_runtime/android_window_InputTransferToken.h>
+#include <gui/InputTransferToken.h>
+#include <nativehelper/JNIHelp.h>
+
+#include "android_os_Parcel.h"
+#include "android_util_Binder.h"
+#include "core_jni_helpers.h"
+
+namespace android {
+
+static struct {
+    jclass clazz;
+    jfieldID mNativeObject;
+    jmethodID ctor;
+} gInputTransferTokenClassInfo;
+
+static jlong nativeCreate(JNIEnv* env, jclass clazz) {
+    sp<InputTransferToken> inputTransferToken = sp<InputTransferToken>::make();
+    inputTransferToken->incStrong((void*)nativeCreate);
+    return reinterpret_cast<jlong>(inputTransferToken.get());
+}
+
+static jlong nativeCreateFromBinder(JNIEnv* env, jclass clazz, jobject tokenBinderObj) {
+    if (tokenBinderObj == nullptr) {
+        return 0;
+    }
+    sp<IBinder> token(ibinderForJavaObject(env, tokenBinderObj));
+    if (token == nullptr) {
+        return 0;
+    }
+    sp<InputTransferToken> inputTransferToken = sp<InputTransferToken>::make(token);
+    inputTransferToken->incStrong((void*)nativeCreate);
+    return reinterpret_cast<jlong>(inputTransferToken.get());
+}
+
+static void nativeWriteToParcel(JNIEnv* env, jclass clazz, jlong nativeObj, jobject parcelObj) {
+    InputTransferToken* inputTransferToken = reinterpret_cast<InputTransferToken*>(nativeObj);
+    Parcel* parcel = parcelForJavaObject(env, parcelObj);
+    inputTransferToken->writeToParcel(parcel);
+}
+
+static jlong nativeReadFromParcel(JNIEnv* env, jclass clazz, jobject parcelObj) {
+    sp<InputTransferToken> inputTransferToken = sp<InputTransferToken>::make();
+    Parcel* parcel = parcelForJavaObject(env, parcelObj);
+    inputTransferToken->readFromParcel(parcel);
+    inputTransferToken->incStrong((void*)nativeCreate);
+    return reinterpret_cast<jlong>(inputTransferToken.get());
+}
+
+static jobject nativeGetBinderToken(JNIEnv* env, jclass clazz, jlong nativeObj) {
+    sp<InputTransferToken> inputTransferToken = reinterpret_cast<InputTransferToken*>(nativeObj);
+    return javaObjectForIBinder(env, inputTransferToken->mToken);
+}
+
+InputTransferToken* android_window_InputTransferToken_getNativeInputTransferToken(
+        JNIEnv* env, jobject inputTransferTokenObj) {
+    if (inputTransferTokenObj != nullptr &&
+        env->IsInstanceOf(inputTransferTokenObj, gInputTransferTokenClassInfo.clazz)) {
+        return reinterpret_cast<InputTransferToken*>(
+                env->GetLongField(inputTransferTokenObj,
+                                  gInputTransferTokenClassInfo.mNativeObject));
+    } else {
+        return nullptr;
+    }
+}
+
+jobject android_window_InputTransferToken_getJavaInputTransferToken(
+        JNIEnv* env, const InputTransferToken* inputTransferToken) {
+    if (inputTransferToken == nullptr || env == nullptr) {
+        return nullptr;
+    }
+
+    inputTransferToken->incStrong((void*)nativeCreate);
+    return env->NewObject(gInputTransferTokenClassInfo.clazz, gInputTransferTokenClassInfo.ctor,
+                          reinterpret_cast<jlong>(inputTransferToken));
+}
+
+static void release(InputTransferToken* inputTransferToken) {
+    inputTransferToken->decStrong((void*)nativeCreate);
+}
+
+static jlong nativeGetNativeInputTransferTokenFinalizer(JNIEnv* env, jclass clazz) {
+    return static_cast<jlong>(reinterpret_cast<uintptr_t>(&release));
+}
+
+static bool nativeEquals(JNIEnv* env, jclass clazz, jlong inputTransferTokenObj1,
+                         jlong inputTransferTokenObj2) {
+    sp<InputTransferToken> inputTransferToken1(
+            reinterpret_cast<InputTransferToken*>(inputTransferTokenObj1));
+    sp<InputTransferToken> inputTransferToken2(
+            reinterpret_cast<InputTransferToken*>(inputTransferTokenObj2));
+
+    return inputTransferToken1 == inputTransferToken2;
+}
+
+static const JNINativeMethod sInputTransferTokenMethods[] = {
+        // clang-format off
+    {"nativeCreate", "()J", (void*)nativeCreate},
+    {"nativeCreate", "(Landroid/os/IBinder;)J", (void*)nativeCreateFromBinder},
+    {"nativeWriteToParcel", "(JLandroid/os/Parcel;)V", (void*)nativeWriteToParcel},
+    {"nativeReadFromParcel", "(Landroid/os/Parcel;)J", (void*)nativeReadFromParcel},
+    {"nativeGetBinderToken", "(J)Landroid/os/IBinder;", (void*)nativeGetBinderToken},
+    {"nativeGetNativeInputTransferTokenFinalizer", "()J", (void*)nativeGetNativeInputTransferTokenFinalizer},
+        {"nativeEquals", "(JJ)Z", (void*) nativeEquals},
+        // clang-format on
+};
+
+int register_android_window_InputTransferToken(JNIEnv* env) {
+    int err = RegisterMethodsOrDie(env, "android/window/InputTransferToken",
+                                   sInputTransferTokenMethods, NELEM(sInputTransferTokenMethods));
+    jclass inputTransferTokenClass = FindClassOrDie(env, "android/window/InputTransferToken");
+    gInputTransferTokenClassInfo.clazz = MakeGlobalRefOrDie(env, inputTransferTokenClass);
+    gInputTransferTokenClassInfo.mNativeObject =
+            GetFieldIDOrDie(env, gInputTransferTokenClassInfo.clazz, "mNativeObject", "J");
+    gInputTransferTokenClassInfo.ctor =
+            GetMethodIDOrDie(env, gInputTransferTokenClassInfo.clazz, "<init>", "(J)V");
+    return err;
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/core/jni/include/android_runtime/android_window_InputTransferToken.h b/core/jni/include/android_runtime/android_window_InputTransferToken.h
new file mode 100644
index 000000000000..75dbe37f781f
--- /dev/null
+++ b/core/jni/include/android_runtime/android_window_InputTransferToken.h
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+#ifndef _ANDROID_WINDOW_INPUTTRANSFERTOKEN_H
+#define _ANDROID_WINDOW_INPUTTRANSFERTOKEN_H
+
+#include <gui/InputTransferToken.h>
+#include <jni.h>
+
+namespace android {
+
+extern InputTransferToken* android_window_InputTransferToken_getNativeInputTransferToken(
+        JNIEnv* env, jobject inputTransferTokenObj);
+
+extern jobject android_window_InputTransferToken_getJavaInputTransferToken(
+        JNIEnv* env, const InputTransferToken* inputTransferToken);
+
+} // namespace android
+
+#endif // _ANDROID_WINDOW_INPUTTRANSFERTOKEN_H
diff --git a/native/android/Android.bp b/native/android/Android.bp
index 752ebdf3d0e3..481268516b51 100644
--- a/native/android/Android.bp
+++ b/native/android/Android.bp
@@ -58,6 +58,7 @@ cc_library_shared {
         "configuration.cpp",
         "hardware_buffer_jni.cpp",
         "input.cpp",
+        "input_transfer_token.cpp",
         "looper.cpp",
         "native_activity.cpp",
         "native_window_jni.cpp",
diff --git a/native/android/input_transfer_token.cpp b/native/android/input_transfer_token.cpp
new file mode 100644
index 000000000000..501e1d312bfc
--- /dev/null
+++ b/native/android/input_transfer_token.cpp
@@ -0,0 +1,63 @@
+/*
+ * 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 "InputTransferToken"
+
+#include <android/input_transfer_token_jni.h>
+#include <android_runtime/android_window_InputTransferToken.h>
+#include <gui/InputTransferToken.h>
+#include <log/log_main.h>
+
+using namespace android;
+
+#define CHECK_NOT_NULL(name) \
+    LOG_ALWAYS_FATAL_IF(name == nullptr, "nullptr passed as " #name " argument");
+
+void InputTransferToken_acquire(InputTransferToken* inputTransferToken) {
+    // incStrong/decStrong token must be the same, doesn't matter what it is
+    inputTransferToken->incStrong((void*)InputTransferToken_acquire);
+}
+
+void InputTransferToken_release(InputTransferToken* inputTransferToken) {
+    // incStrong/decStrong token must be the same, doesn't matter what it is
+    inputTransferToken->decStrong((void*)InputTransferToken_acquire);
+}
+
+AInputTransferToken* AInputTransferToken_fromJava(JNIEnv* env, jobject inputTransferTokenObj) {
+    CHECK_NOT_NULL(env);
+    CHECK_NOT_NULL(inputTransferTokenObj);
+    InputTransferToken* inputTransferToken =
+            android_window_InputTransferToken_getNativeInputTransferToken(env,
+                                                                          inputTransferTokenObj);
+    CHECK_NOT_NULL(inputTransferToken);
+    InputTransferToken_acquire(inputTransferToken);
+    return reinterpret_cast<AInputTransferToken*>(inputTransferToken);
+}
+
+jobject AInputTransferToken_toJava(JNIEnv* _Nonnull env,
+                                   const AInputTransferToken* aInputTransferToken) {
+    CHECK_NOT_NULL(env);
+    CHECK_NOT_NULL(aInputTransferToken);
+    const InputTransferToken* inputTransferToken =
+            reinterpret_cast<const InputTransferToken*>(aInputTransferToken);
+    return android_window_InputTransferToken_getJavaInputTransferToken(env, inputTransferToken);
+}
+
+void AInputTransferToken_release(AInputTransferToken* aInputTransferToken) {
+    CHECK_NOT_NULL(aInputTransferToken);
+    InputTransferToken* inputTransferToken =
+            reinterpret_cast<InputTransferToken*>(aInputTransferToken);
+    InputTransferToken_release(inputTransferToken);
+}
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index 35e37b2a6893..b2925bfa6881 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -98,6 +98,9 @@ LIBANDROID {
     AInputQueue_getEvent;
     AInputQueue_hasEvents;
     AInputQueue_preDispatchEvent;
+    AInputTransferToken_fromJava; # introduced=35
+    AInputTransferToken_release; # introduced=35
+    AInputTransferToken_toJava; # introduced=35
     AKeyEvent_getAction;
     AKeyEvent_getDownTime;
     AKeyEvent_getEventTime;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index a055db274ae8..d49b4e3795ee 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -9082,7 +9082,7 @@ public class WindowManagerService extends IWindowManager.Stub
         Objects.requireNonNull(outInputChannel);
         synchronized (mGlobalLock) {
             WindowState hostWindowState = hostInputTransferToken != null
-                    ? mInputToWindowMap.get(hostInputTransferToken.mToken) : null;
+                    ? mInputToWindowMap.get(hostInputTransferToken.getToken()) : null;
             EmbeddedWindowController.EmbeddedWindow win =
                     new EmbeddedWindowController.EmbeddedWindow(session, this, clientToken,
                             hostWindowState, callingUid, callingPid, sanitizedType, displayId,
@@ -9111,12 +9111,13 @@ public class WindowManagerService extends IWindowManager.Stub
                 // If the transferToToken exists in the input to window map, it means the request
                 // is to transfer from embedded to host. Otherwise, the transferToToken
                 // represents an embedded window so transfer from host to embedded.
-                WindowState windowStateTo = mInputToWindowMap.get(transferToToken.mToken);
+                WindowState windowStateTo = mInputToWindowMap.get(transferToToken.getToken());
                 if (windowStateTo != null) {
                     didTransfer = mEmbeddedWindowController.transferToHost(transferFromToken,
                             windowStateTo);
                 } else {
-                    WindowState windowStateFrom = mInputToWindowMap.get(transferFromToken.mToken);
+                    WindowState windowStateFrom = mInputToWindowMap.get(
+                            transferFromToken.getToken());
                     didTransfer = mEmbeddedWindowController.transferToEmbedded(windowStateFrom,
                             transferToToken);
                 }
-- 
GitLab