diff options
author | 2024-03-04 22:49:03 +0000 | |
---|---|---|
committer | 2024-03-15 23:30:34 +0000 | |
commit | d57801ec8903aa2f11b2594e27659a2de600350d (patch) | |
tree | a9712cc3af8026555acb7bace2c3cdaf67a15c94 | |
parent | da3a32841cf9708cb91ef0754dec9805798be3d0 (diff) |
Add surface_control_input_receiver native API
Bug: 324271765
Test: ASurfaceControlInputReceiverTest
Change-Id: Ia85a07af09878846c681e552c1f8471652f932fc
19 files changed, 427 insertions, 62 deletions
diff --git a/core/api/current.txt b/core/api/current.txt index 925cf4c605de..cc7dcb1b6406 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -54419,9 +54419,9 @@ package android.view { method @Deprecated public android.view.Display getDefaultDisplay(); method @NonNull public default android.view.WindowMetrics getMaximumWindowMetrics(); method public default boolean isCrossWindowBlurEnabled(); - method @FlaggedApi("com.android.window.flags.surface_control_input_receiver") @NonNull public default android.window.InputTransferToken registerBatchedSurfaceControlInputReceiver(int, @NonNull android.window.InputTransferToken, @NonNull android.view.SurfaceControl, @NonNull android.view.Choreographer, @NonNull android.view.SurfaceControlInputReceiver); + method @FlaggedApi("com.android.window.flags.surface_control_input_receiver") @NonNull public default android.window.InputTransferToken registerBatchedSurfaceControlInputReceiver(@NonNull android.window.InputTransferToken, @NonNull android.view.SurfaceControl, @NonNull android.view.Choreographer, @NonNull android.view.SurfaceControlInputReceiver); method @FlaggedApi("com.android.window.flags.trusted_presentation_listener_for_window") public default void registerTrustedPresentationListener(@NonNull android.os.IBinder, @NonNull android.window.TrustedPresentationThresholds, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>); - method @FlaggedApi("com.android.window.flags.surface_control_input_receiver") @NonNull public default android.window.InputTransferToken registerUnbatchedSurfaceControlInputReceiver(int, @NonNull android.window.InputTransferToken, @NonNull android.view.SurfaceControl, @NonNull android.os.Looper, @NonNull android.view.SurfaceControlInputReceiver); + method @FlaggedApi("com.android.window.flags.surface_control_input_receiver") @NonNull public default android.window.InputTransferToken registerUnbatchedSurfaceControlInputReceiver(@NonNull android.window.InputTransferToken, @NonNull android.view.SurfaceControl, @NonNull android.os.Looper, @NonNull android.view.SurfaceControlInputReceiver); method public default void removeCrossWindowBlurEnabledListener(@NonNull java.util.function.Consumer<java.lang.Boolean>); method public default void removeProposedRotationListener(@NonNull java.util.function.IntConsumer); method @FlaggedApi("com.android.window.flags.screen_recording_callbacks") @RequiresPermission(android.Manifest.permission.DETECT_SCREEN_RECORDING) public default void removeScreenRecordingCallback(@NonNull java.util.function.Consumer<java.lang.Integer>); diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 107c5f20acde..71f1c34388c5 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -6193,9 +6193,6 @@ public interface WindowManager extends ViewManager { * caller must invoke {@link #unregisterSurfaceControlInputReceiver(SurfaceControl)} to clean up * the resources when no longer needing to use the {@link SurfaceControlInputReceiver} * - * @param displayId The display that the SurfaceControl will be placed on. Input - * will only work if SurfaceControl is on that display and that - * display was touched. * @param surfaceControl The SurfaceControl to register the InputChannel for * @param hostInputTransferToken The host token to link the embedded. This is used to handle * transferring touch gesture from host to embedded and for ANRs @@ -6210,7 +6207,7 @@ public interface WindowManager extends ViewManager { */ @FlaggedApi(Flags.FLAG_SURFACE_CONTROL_INPUT_RECEIVER) @NonNull - default InputTransferToken registerBatchedSurfaceControlInputReceiver(int displayId, + default InputTransferToken registerBatchedSurfaceControlInputReceiver( @NonNull InputTransferToken hostInputTransferToken, @NonNull SurfaceControl surfaceControl, @NonNull Choreographer choreographer, @NonNull SurfaceControlInputReceiver receiver) { @@ -6221,15 +6218,12 @@ public interface WindowManager extends ViewManager { /** * Registers a {@link SurfaceControlInputReceiver} for a {@link SurfaceControl} that will * receive every input event. This is different than calling - * {@link #registerBatchedSurfaceControlInputReceiver(int, InputTransferToken, SurfaceControl, + * {@link #registerBatchedSurfaceControlInputReceiver(InputTransferToken, SurfaceControl, * Choreographer, SurfaceControlInputReceiver)} in that the input events are received * unbatched. * The caller must invoke {@link #unregisterSurfaceControlInputReceiver(SurfaceControl)} to * clean up the resources when no longer needing to use the {@link SurfaceControlInputReceiver} * - * @param displayId The display that the SurfaceControl will be placed on. Input - * will only work if SurfaceControl is on that display and that - * display was touched. * @param surfaceControl The SurfaceControl to register the InputChannel for * @param hostInputTransferToken The host token to link the embedded. This is used to handle * transferring touch gesture from host to embedded and for ANRs @@ -6243,7 +6237,7 @@ public interface WindowManager extends ViewManager { */ @FlaggedApi(Flags.FLAG_SURFACE_CONTROL_INPUT_RECEIVER) @NonNull - default InputTransferToken registerUnbatchedSurfaceControlInputReceiver(int displayId, + default InputTransferToken registerUnbatchedSurfaceControlInputReceiver( @NonNull InputTransferToken hostInputTransferToken, @NonNull SurfaceControl surfaceControl, @NonNull Looper looper, @NonNull SurfaceControlInputReceiver receiver) { @@ -6256,9 +6250,9 @@ public interface WindowManager extends ViewManager { * specified token. * <p> * Must be called on the same {@link Looper} thread to which was passed to the - * {@link #registerBatchedSurfaceControlInputReceiver(int, InputTransferToken, SurfaceControl, + * {@link #registerBatchedSurfaceControlInputReceiver(InputTransferToken, SurfaceControl, * Choreographer, SurfaceControlInputReceiver)} or - * {@link #registerUnbatchedSurfaceControlInputReceiver(int, InputTransferToken, SurfaceControl, + * {@link #registerUnbatchedSurfaceControlInputReceiver(InputTransferToken, SurfaceControl, * Looper, SurfaceControlInputReceiver)} * * @param surfaceControl The SurfaceControl to remove and unregister the input channel for. @@ -6272,9 +6266,9 @@ public interface WindowManager extends ViewManager { /** * Returns the input client token for the {@link SurfaceControl}. This will only return non * null if the SurfaceControl was registered for input via - * {@link #registerBatchedSurfaceControlInputReceiver(int, InputTransferToken, SurfaceControl, + * {@link #registerBatchedSurfaceControlInputReceiver(InputTransferToken, SurfaceControl, * Choreographer, SurfaceControlInputReceiver)} or - * {@link #registerUnbatchedSurfaceControlInputReceiver(int, InputTransferToken, + * {@link #registerUnbatchedSurfaceControlInputReceiver(InputTransferToken, * SurfaceControl, Looper, SurfaceControlInputReceiver)}. * <p> * This is helpful for testing to ensure the test waits for the layer to be registered with @@ -6304,9 +6298,9 @@ public interface WindowManager extends ViewManager { * </li> * <li> * Registering a SurfaceControl for input and passing the host's token to either - * {@link #registerBatchedSurfaceControlInputReceiver(int, InputTransferToken, SurfaceControl, + * {@link #registerBatchedSurfaceControlInputReceiver(InputTransferToken, SurfaceControl, * Choreographer, SurfaceControlInputReceiver)} or - * {@link #registerUnbatchedSurfaceControlInputReceiver(int, InputTransferToken, + * {@link #registerUnbatchedSurfaceControlInputReceiver(InputTransferToken, * SurfaceControl, Looper, SurfaceControlInputReceiver)}. * </li> * </ul> @@ -6321,9 +6315,9 @@ public interface WindowManager extends ViewManager { * When the host wants to transfer touch gesture to the embedded, it can retrieve the embedded * token via {@link SurfaceControlViewHost.SurfacePackage#getInputTransferToken()} or use the * value returned from either - * {@link #registerBatchedSurfaceControlInputReceiver(int, InputTransferToken, SurfaceControl, + * {@link #registerBatchedSurfaceControlInputReceiver(InputTransferToken, SurfaceControl, * Choreographer, SurfaceControlInputReceiver)} or - * {@link #registerUnbatchedSurfaceControlInputReceiver(int, InputTransferToken, SurfaceControl, + * {@link #registerUnbatchedSurfaceControlInputReceiver(InputTransferToken, SurfaceControl, * Looper, SurfaceControlInputReceiver)} and pass its own token as the transferFromToken. * <p> * When the embedded wants to transfer touch gesture to the host, it can pass in its own diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java index c6d0454fbcfd..961a9c438ba7 100644 --- a/core/java/android/view/WindowManagerGlobal.java +++ b/core/java/android/view/WindowManagerGlobal.java @@ -16,6 +16,7 @@ package android.view; +import static android.view.Display.INVALID_DISPLAY; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import android.animation.ValueAnimator; @@ -839,20 +840,40 @@ public final class WindowManagerGlobal { mTrustedPresentationListener.removeListener(listener); } - InputTransferToken registerBatchedSurfaceControlInputReceiver(int displayId, + private static InputChannel createInputChannel(@NonNull IBinder clientToken, @NonNull InputTransferToken hostToken, @NonNull SurfaceControl surfaceControl, - @NonNull Choreographer choreographer, @NonNull SurfaceControlInputReceiver receiver) { - IBinder clientToken = new Binder(); - InputTransferToken inputTransferToken = new InputTransferToken(); + @Nullable InputTransferToken inputTransferToken) { InputChannel inputChannel = new InputChannel(); try { - WindowManagerGlobal.getWindowSession().grantInputChannel(displayId, surfaceControl, - clientToken, hostToken, 0, 0, TYPE_APPLICATION, 0, null, inputTransferToken, - surfaceControl.getName(), inputChannel); + // TODO (b/329860681): Use INVALID_DISPLAY for now because the displayId will be + // selected in SurfaceFlinger. This should be cleaned up so grantInputChannel doesn't + // take in a displayId at all + WindowManagerGlobal.getWindowSession().grantInputChannel(INVALID_DISPLAY, + surfaceControl, clientToken, hostToken, 0, 0, TYPE_APPLICATION, 0, null, + inputTransferToken, surfaceControl.getName(), inputChannel); } catch (RemoteException e) { Log.e(TAG, "Failed to create input channel", e); e.rethrowAsRuntimeException(); } + return inputChannel; + } + + private static void removeInputChannel(IBinder clientToken) { + try { + WindowManagerGlobal.getWindowSession().remove(clientToken); + } catch (RemoteException e) { + Log.e(TAG, "Failed to remove input channel", e); + e.rethrowAsRuntimeException(); + } + } + + InputTransferToken registerBatchedSurfaceControlInputReceiver( + @NonNull InputTransferToken hostToken, @NonNull SurfaceControl surfaceControl, + @NonNull Choreographer choreographer, @NonNull SurfaceControlInputReceiver receiver) { + IBinder clientToken = new Binder(); + InputTransferToken inputTransferToken = new InputTransferToken(); + InputChannel inputChannel = createInputChannel(clientToken, hostToken, + surfaceControl, inputTransferToken); synchronized (mSurfaceControlInputReceivers) { mSurfaceControlInputReceivers.put(surfaceControl.getLayerId(), @@ -869,20 +890,13 @@ public final class WindowManagerGlobal { return inputTransferToken; } - InputTransferToken registerUnbatchedSurfaceControlInputReceiver(int displayId, + InputTransferToken registerUnbatchedSurfaceControlInputReceiver( @NonNull InputTransferToken hostToken, @NonNull SurfaceControl surfaceControl, @NonNull Looper looper, @NonNull SurfaceControlInputReceiver receiver) { IBinder clientToken = new Binder(); InputTransferToken inputTransferToken = new InputTransferToken(); - InputChannel inputChannel = new InputChannel(); - try { - WindowManagerGlobal.getWindowSession().grantInputChannel(displayId, surfaceControl, - clientToken, hostToken, 0, 0, TYPE_APPLICATION, 0, null, inputTransferToken, - surfaceControl.getName(), inputChannel); - } catch (RemoteException e) { - Log.e(TAG, "Failed to create input channel", e); - e.rethrowAsRuntimeException(); - } + InputChannel inputChannel = createInputChannel(clientToken, hostToken, + surfaceControl, inputTransferToken); synchronized (mSurfaceControlInputReceivers) { mSurfaceControlInputReceivers.put(surfaceControl.getLayerId(), @@ -909,13 +923,7 @@ public final class WindowManagerGlobal { Log.w(TAG, "No registered input event receiver with sc: " + surfaceControl); return; } - try { - WindowManagerGlobal.getWindowSession().remove( - surfaceControlInputReceiverInfo.mClientToken); - } catch (RemoteException e) { - Log.e(TAG, "Failed to remove input channel", e); - e.rethrowAsRuntimeException(); - } + removeInputChannel(surfaceControlInputReceiverInfo.mClientToken); surfaceControlInputReceiverInfo.mInputEventReceiver.dispose(); } diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java index df4fed6a45f1..b667427fd2c5 100644 --- a/core/java/android/view/WindowManagerImpl.java +++ b/core/java/android/view/WindowManagerImpl.java @@ -535,7 +535,7 @@ public final class WindowManagerImpl implements WindowManager { @NonNull @Override - public InputTransferToken registerBatchedSurfaceControlInputReceiver(int displayId, + public InputTransferToken registerBatchedSurfaceControlInputReceiver( @NonNull InputTransferToken hostInputTransferToken, @NonNull SurfaceControl surfaceControl, @NonNull Choreographer choreographer, @NonNull SurfaceControlInputReceiver receiver) { @@ -543,13 +543,13 @@ public final class WindowManagerImpl implements WindowManager { Objects.requireNonNull(surfaceControl); Objects.requireNonNull(choreographer); Objects.requireNonNull(receiver); - return mGlobal.registerBatchedSurfaceControlInputReceiver(displayId, hostInputTransferToken, + return mGlobal.registerBatchedSurfaceControlInputReceiver(hostInputTransferToken, surfaceControl, choreographer, receiver); } @NonNull @Override - public InputTransferToken registerUnbatchedSurfaceControlInputReceiver(int displayId, + public InputTransferToken registerUnbatchedSurfaceControlInputReceiver( @NonNull InputTransferToken hostInputTransferToken, @NonNull SurfaceControl surfaceControl, @NonNull Looper looper, @NonNull SurfaceControlInputReceiver receiver) { @@ -557,7 +557,7 @@ public final class WindowManagerImpl implements WindowManager { Objects.requireNonNull(surfaceControl); Objects.requireNonNull(looper); Objects.requireNonNull(receiver); - return mGlobal.registerUnbatchedSurfaceControlInputReceiver(displayId, + return mGlobal.registerUnbatchedSurfaceControlInputReceiver( hostInputTransferToken, surfaceControl, looper, receiver); } diff --git a/core/java/android/window/InputTransferToken.java b/core/java/android/window/InputTransferToken.java index c62eee40da98..5fab48f93316 100644 --- a/core/java/android/window/InputTransferToken.java +++ b/core/java/android/window/InputTransferToken.java @@ -38,9 +38,9 @@ import java.util.Objects; * {@link SurfaceControlViewHost} or {@link android.view.SurfaceControl} that has an input channel. * <p> * The {@link android.view.SurfaceControl} needs to have been registered for input via - * {@link android.view.WindowManager#registerUnbatchedSurfaceControlInputReceiver(int, + * {@link android.view.WindowManager#registerUnbatchedSurfaceControlInputReceiver( * InputTransferToken, SurfaceControl, Looper, SurfaceControlInputReceiver)} or - * {@link android.view.WindowManager#registerBatchedSurfaceControlInputReceiver(int, + * {@link android.view.WindowManager#registerBatchedSurfaceControlInputReceiver( * InputTransferToken, SurfaceControl, Choreographer, SurfaceControlInputReceiver)} and the * returned token can be used to call * {@link android.view.WindowManager#transferTouchGesture(InputTransferToken, InputTransferToken)} diff --git a/core/jni/Android.bp b/core/jni/Android.bp index ac961ee07af4..444288c613ea 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -164,6 +164,7 @@ cc_library_shared_for_libandroid_runtime { "android_view_Surface.cpp", "android_view_SurfaceControl.cpp", "android_view_SurfaceControlHdrLayerInfoListener.cpp", + "android_view_WindowManagerGlobal.cpp", "android_graphics_BLASTBufferQueue.cpp", "android_view_SurfaceSession.cpp", "android_view_TextureView.cpp", diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 9bbd19122153..71d041c11980 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -222,6 +222,7 @@ 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); +extern int register_android_view_WindowManagerGlobal(JNIEnv* env); // Namespace for Android Runtime flags applied during boot time. static const char* RUNTIME_NATIVE_BOOT_NAMESPACE = "runtime_native_boot"; @@ -1680,6 +1681,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_tracing_PerfettoDataSourceInstance), REG_JNI(register_android_tracing_PerfettoProducer), REG_JNI(register_android_window_InputTransferToken), + REG_JNI(register_android_view_WindowManagerGlobal), }; /* diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index 6fec527aaa16..1eab9910b651 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -254,6 +254,8 @@ static struct { static struct { jclass clazz; jfieldID mNativeObject; + jfieldID mName; + jmethodID ctor; jmethodID invokeReleaseCallback; } gSurfaceControlClassInfo; @@ -2177,6 +2179,20 @@ SurfaceControl* android_view_SurfaceControl_getNativeSurfaceControl(JNIEnv* env, } } +jobject android_view_SurfaceControl_getJavaSurfaceControl(JNIEnv* env, + const SurfaceControl& surfaceControl) { + jobject surfaceControlObj = + env->NewObject(gSurfaceControlClassInfo.clazz, gSurfaceControlClassInfo.ctor); + env->SetLongField(surfaceControlObj, gSurfaceControlClassInfo.mNativeObject, + reinterpret_cast<jlong>(&surfaceControl)); + env->SetObjectField(surfaceControlObj, gSurfaceControlClassInfo.mName, + ScopedLocalRef<jobject>(env, + env->NewStringUTF(surfaceControl.getName().c_str())) + .get()); + surfaceControl.incStrong((void*)nativeCreate); + return surfaceControlObj; +} + SurfaceComposerClient::Transaction* android_view_SurfaceTransaction_getNativeSurfaceTransaction( JNIEnv* env, jobject surfaceTransactionObj) { if (!!surfaceTransactionObj && @@ -2652,6 +2668,9 @@ int register_android_view_SurfaceControl(JNIEnv* env) gSurfaceControlClassInfo.clazz = MakeGlobalRefOrDie(env, surfaceControlClazz); gSurfaceControlClassInfo.mNativeObject = GetFieldIDOrDie(env, gSurfaceControlClassInfo.clazz, "mNativeObject", "J"); + gSurfaceControlClassInfo.mName = + GetFieldIDOrDie(env, gSurfaceControlClassInfo.clazz, "mName", "Ljava/lang/String;"); + gSurfaceControlClassInfo.ctor = GetMethodIDOrDie(env, surfaceControlClazz, "<init>", "()V"); gSurfaceControlClassInfo.invokeReleaseCallback = GetStaticMethodIDOrDie(env, surfaceControlClazz, "invokeReleaseCallback", "(Ljava/util/function/Consumer;J)V"); diff --git a/core/jni/android_view_WindowManagerGlobal.cpp b/core/jni/android_view_WindowManagerGlobal.cpp new file mode 100644 index 000000000000..b03ac88a36ca --- /dev/null +++ b/core/jni/android_view_WindowManagerGlobal.cpp @@ -0,0 +1,91 @@ +/* + * 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 "WindowManagerGlobal" + +#include "android_view_WindowManagerGlobal.h" + +#include <android_runtime/AndroidRuntime.h> +#include <android_runtime/android_view_SurfaceControl.h> +#include <android_runtime/android_window_InputTransferToken.h> +#include <jni.h> +#include <nativehelper/ScopedLocalRef.h> + +#include "android_util_Binder.h" +#include "android_view_InputChannel.h" +#include "jni_wrappers.h" + +namespace android { + +static struct { + jclass clazz; + jmethodID createInputChannel; + jmethodID removeInputChannel; +} gWindowManagerGlobal; + +std::shared_ptr<InputChannel> createInputChannel( + const sp<IBinder>& clientToken, const InputTransferToken& hostInputTransferToken, + const SurfaceControl& surfaceControl, const InputTransferToken& clientInputTransferToken) { + JNIEnv* env = AndroidRuntime::getJNIEnv(); + ScopedLocalRef<jobject> hostInputTransferTokenObj( + env, + android_window_InputTransferToken_getJavaInputTransferToken(env, + hostInputTransferToken)); + ScopedLocalRef<jobject> + surfaceControlObj(env, + android_view_SurfaceControl_getJavaSurfaceControl(env, + surfaceControl)); + jobject clientTokenObj = javaObjectForIBinder(env, clientToken); + ScopedLocalRef<jobject> clientInputTransferTokenObj( + env, + android_window_InputTransferToken_getJavaInputTransferToken(env, + clientInputTransferToken)); + ScopedLocalRef<jobject> + inputChannelObj(env, + env->CallStaticObjectMethod(gWindowManagerGlobal.clazz, + gWindowManagerGlobal.createInputChannel, + clientTokenObj, + hostInputTransferTokenObj.get(), + surfaceControlObj.get(), + clientInputTransferTokenObj.get())); + + return android_view_InputChannel_getInputChannel(env, inputChannelObj.get()); +} + +void removeInputChannel(const sp<IBinder>& clientToken) { + JNIEnv* env = AndroidRuntime::getJNIEnv(); + + jobject clientTokenObj(javaObjectForIBinder(env, clientToken)); + env->CallStaticObjectMethod(gWindowManagerGlobal.clazz, gWindowManagerGlobal.removeInputChannel, + clientTokenObj); +} + +int register_android_view_WindowManagerGlobal(JNIEnv* env) { + jclass windowManagerGlobalClass = FindClassOrDie(env, "android/view/WindowManagerGlobal"); + gWindowManagerGlobal.clazz = MakeGlobalRefOrDie(env, windowManagerGlobalClass); + gWindowManagerGlobal.createInputChannel = + GetStaticMethodIDOrDie(env, windowManagerGlobalClass, "createInputChannel", + "(Landroid/os/IBinder;Landroid/window/" + "InputTransferToken;Landroid/view/SurfaceControl;Landroid/" + "window/InputTransferToken;)Landroid/view/InputChannel;"); + gWindowManagerGlobal.removeInputChannel = + GetStaticMethodIDOrDie(env, windowManagerGlobalClass, "removeInputChannel", + "(Landroid/os/IBinder;)V"); + + return NO_ERROR; +} + +} // namespace android
\ No newline at end of file diff --git a/core/jni/android_view_WindowManagerGlobal.h b/core/jni/android_view_WindowManagerGlobal.h new file mode 100644 index 000000000000..fcdeaa1c7d20 --- /dev/null +++ b/core/jni/android_view_WindowManagerGlobal.h @@ -0,0 +1,29 @@ +/* + * 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. + */ + +#include <binder/IBinder.h> +#include <gui/InputTransferToken.h> +#include <gui/SurfaceControl.h> +#include <input/InputTransport.h> + +namespace android { +extern std::shared_ptr<InputChannel> createInputChannel( + const sp<IBinder>& clientToken, const InputTransferToken& hostInputTransferToken, + const SurfaceControl& surfaceControl, + const InputTransferToken& clientInputTransferTokenObj); +extern void removeInputChannel(const sp<IBinder>& clientToken); + +} // namespace android
\ No newline at end of file diff --git a/core/jni/android_window_InputTransferToken.cpp b/core/jni/android_window_InputTransferToken.cpp index 60568e30ccb2..8fb668d6bbd9 100644 --- a/core/jni/android_window_InputTransferToken.cpp +++ b/core/jni/android_window_InputTransferToken.cpp @@ -83,14 +83,10 @@ InputTransferToken* android_window_InputTransferToken_getNativeInputTransferToke } jobject android_window_InputTransferToken_getJavaInputTransferToken( - JNIEnv* env, const InputTransferToken* inputTransferToken) { - if (inputTransferToken == nullptr || env == nullptr) { - return nullptr; - } - - inputTransferToken->incStrong((void*)nativeCreate); + JNIEnv* env, const InputTransferToken& inputTransferToken) { + inputTransferToken.incStrong((void*)nativeCreate); return env->NewObject(gInputTransferTokenClassInfo.clazz, gInputTransferTokenClassInfo.ctor, - reinterpret_cast<jlong>(inputTransferToken)); + reinterpret_cast<jlong>(&inputTransferToken)); } static void release(InputTransferToken* inputTransferToken) { diff --git a/core/jni/include/android_runtime/android_view_SurfaceControl.h b/core/jni/include/android_runtime/android_view_SurfaceControl.h index 10a754903208..543deb8c5621 100644 --- a/core/jni/include/android_runtime/android_view_SurfaceControl.h +++ b/core/jni/include/android_runtime/android_view_SurfaceControl.h @@ -28,6 +28,9 @@ namespace android { extern SurfaceControl* android_view_SurfaceControl_getNativeSurfaceControl( JNIEnv* env, jobject surfaceControlObj); +extern jobject android_view_SurfaceControl_getJavaSurfaceControl( + JNIEnv* env, const SurfaceControl& surfaceControl); + /* Gets the underlying native SurfaceControl for a java SurfaceControl. */ extern SurfaceComposerClient::Transaction* android_view_SurfaceTransaction_getNativeSurfaceTransaction(JNIEnv* env, diff --git a/core/jni/include/android_runtime/android_window_InputTransferToken.h b/core/jni/include/android_runtime/android_window_InputTransferToken.h index 75dbe37f781f..5ac48f322003 100644 --- a/core/jni/include/android_runtime/android_window_InputTransferToken.h +++ b/core/jni/include/android_runtime/android_window_InputTransferToken.h @@ -26,7 +26,7 @@ extern InputTransferToken* android_window_InputTransferToken_getNativeInputTrans JNIEnv* env, jobject inputTransferTokenObj); extern jobject android_window_InputTransferToken_getJavaInputTransferToken( - JNIEnv* env, const InputTransferToken* inputTransferToken); + JNIEnv* env, const InputTransferToken& inputTransferToken); } // namespace android diff --git a/native/android/Android.bp b/native/android/Android.bp index 481268516b51..db3a67a90862 100644 --- a/native/android/Android.bp +++ b/native/android/Android.bp @@ -54,6 +54,7 @@ cc_library_shared { srcs: [ "activity_manager.cpp", "asset_manager.cpp", + "surface_control_input_receiver.cpp", "choreographer.cpp", "configuration.cpp", "hardware_buffer_jni.cpp", diff --git a/native/android/input_transfer_token.cpp b/native/android/input_transfer_token.cpp index 501e1d312bfc..2e74aa3dbcbe 100644 --- a/native/android/input_transfer_token.cpp +++ b/native/android/input_transfer_token.cpp @@ -25,7 +25,7 @@ using namespace android; #define CHECK_NOT_NULL(name) \ LOG_ALWAYS_FATAL_IF(name == nullptr, "nullptr passed as " #name " argument"); -void InputTransferToken_acquire(InputTransferToken* inputTransferToken) { +extern void InputTransferToken_acquire(InputTransferToken* inputTransferToken) { // incStrong/decStrong token must be the same, doesn't matter what it is inputTransferToken->incStrong((void*)InputTransferToken_acquire); } @@ -52,7 +52,7 @@ jobject AInputTransferToken_toJava(JNIEnv* _Nonnull env, CHECK_NOT_NULL(aInputTransferToken); const InputTransferToken* inputTransferToken = reinterpret_cast<const InputTransferToken*>(aInputTransferToken); - return android_window_InputTransferToken_getJavaInputTransferToken(env, inputTransferToken); + return android_window_InputTransferToken_getJavaInputTransferToken(env, *inputTransferToken); } void AInputTransferToken_release(AInputTransferToken* aInputTransferToken) { diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt index b2925bfa6881..1c203e37c710 100644 --- a/native/android/libandroid.map.txt +++ b/native/android/libandroid.map.txt @@ -98,6 +98,14 @@ LIBANDROID { AInputQueue_getEvent; AInputQueue_hasEvents; AInputQueue_preDispatchEvent; + AInputReceiver_createBatchedInputReceiver; # introduced=35 + AInputReceiver_createUnbatchedInputReceiver; # introduced=35 + AInputReceiver_release; # introduced=35 + AInputReceiver_getInputTransferToken; # introduced=35 + AInputReceiverCallbacks_create; # introduced=35 + AInputReceiverCallbacks_release; # introduced=35 + AInputReceiverCallbacks_setKeyEventCallback; # introduced=35 + AInputReceiverCallbacks_setMotionEventCallback; # introduced=35 AInputTransferToken_fromJava; # introduced=35 AInputTransferToken_release; # introduced=35 AInputTransferToken_toJava; # introduced=35 diff --git a/native/android/surface_control_input_receiver.cpp b/native/android/surface_control_input_receiver.cpp new file mode 100644 index 000000000000..da0defd9fd17 --- /dev/null +++ b/native/android/surface_control_input_receiver.cpp @@ -0,0 +1,213 @@ +/* + * 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. + */ + +#include <android/choreographer.h> +#include <android/surface_control_input_receiver.h> +#include <binder/Binder.h> +#include <gui/Choreographer.h> +#include <gui/InputTransferToken.h> +#include <input/Input.h> +#include <input/InputConsumerNoResampling.h> + +#include "android_view_WindowManagerGlobal.h" + +using namespace android; + +extern void InputTransferToken_acquire(InputTransferToken* inputTransferToken); + +struct AInputReceiverCallbacks { + AInputReceiverCallbacks(void* context) : context(context) {} + void* context; + AInputReceiver_onMotionEvent onMotionEvent = nullptr; + AInputReceiver_onKeyEvent onKeyEvent = nullptr; +}; + +class InputReceiver : public InputConsumerCallbacks { +public: + InputReceiver(const sp<Looper>& looper, const std::shared_ptr<InputChannel>& inputChannel, + const sp<IBinder>& clientToken, const sp<InputTransferToken>& inputTransferToken, + AInputReceiverCallbacks* callbacks) + : mCallbacks(callbacks), + mInputConsumer(inputChannel, looper, *this), + mClientToken(clientToken), + mInputTransferToken(inputTransferToken) {} + + ~InputReceiver() { + remove(); + } + + void onKeyEvent(std::unique_ptr<KeyEvent> event, uint32_t seq) override { + if (mCallbacks->onKeyEvent != nullptr) { + const bool handled = mCallbacks->onKeyEvent(mCallbacks->context, + static_cast<AInputEvent*>(event.release())); + mInputConsumer.finishInputEvent(seq, handled); + } + } + + void onMotionEvent(std::unique_ptr<MotionEvent> event, uint32_t seq) override { + if (mCallbacks->onMotionEvent != nullptr) { + const bool handled = + mCallbacks->onMotionEvent(mCallbacks->context, + static_cast<AInputEvent*>(event.release())); + mInputConsumer.finishInputEvent(seq, handled); + } + } + + void onFocusEvent(std::unique_ptr<FocusEvent>, uint32_t seq) override { + mInputConsumer.finishInputEvent(seq, false); + } + void onCaptureEvent(std::unique_ptr<CaptureEvent>, uint32_t seq) override { + mInputConsumer.finishInputEvent(seq, false); + } + void onDragEvent(std::unique_ptr<DragEvent>, uint32_t seq) override { + mInputConsumer.finishInputEvent(seq, false); + } + void onTouchModeEvent(std::unique_ptr<TouchModeEvent>, uint32_t seq) override { + mInputConsumer.finishInputEvent(seq, false); + } + + virtual void onBatchedInputEventPending(int32_t) override { + mInputConsumer.consumeBatchedInputEvents(std::nullopt); + } + + const AInputTransferToken* getInputTransferToken() { + InputTransferToken_acquire(mInputTransferToken.get()); + return reinterpret_cast<const AInputTransferToken*>(mInputTransferToken.get()); + } + + void remove() { + removeInputChannel(mClientToken); + } + + AInputReceiverCallbacks* mCallbacks; + +protected: + InputConsumerNoResampling mInputConsumer; + +private: + const sp<IBinder> mClientToken; + const sp<InputTransferToken> mInputTransferToken; +}; + +class BatchedInputReceiver : public InputReceiver { +public: + BatchedInputReceiver(Choreographer& choreographer, + const std::shared_ptr<InputChannel>& inputChannel, + const sp<IBinder>& clientToken, + const sp<InputTransferToken>& inputTransferToken, + AInputReceiverCallbacks* callbacks) + : InputReceiver(choreographer.getLooper(), inputChannel, clientToken, inputTransferToken, + callbacks), + mChoreographer(choreographer) {} + + static void vsyncCallback(const AChoreographerFrameCallbackData* callbackData, void* data) { + BatchedInputReceiver* receiver = static_cast<BatchedInputReceiver*>(data); + receiver->onVsyncCallback(callbackData); + } + + void onVsyncCallback(const AChoreographerFrameCallbackData* callbackData) { + int64_t frameTimeNanos = AChoreographerFrameCallbackData_getFrameTimeNanos(callbackData); + mInputConsumer.consumeBatchedInputEvents(frameTimeNanos); + mBatchedInputScheduled = false; + } + + void onBatchedInputEventPending(int32_t) override { + scheduleBatchedInput(); + } + +private: + Choreographer& mChoreographer; + bool mBatchedInputScheduled = false; + + void scheduleBatchedInput() { + if (!mBatchedInputScheduled) { + mBatchedInputScheduled = true; + mChoreographer.postFrameCallbackDelayed(nullptr, nullptr, vsyncCallback, this, 0, + CallbackType::CALLBACK_INPUT); + } + } +}; + +static inline AInputReceiver* InputReceiver_to_AInputReceiver(InputReceiver* inputReceiver) { + return reinterpret_cast<AInputReceiver*>(inputReceiver); +} + +static inline InputReceiver* AInputReceiver_to_InputReceiver(AInputReceiver* aInputReceiver) { + return reinterpret_cast<InputReceiver*>(aInputReceiver); +} + +AInputReceiver* AInputReceiver_createBatchedInputReceiver(AChoreographer* aChoreographer, + const AInputTransferToken* hostToken, + const ASurfaceControl* aSurfaceControl, + AInputReceiverCallbacks* callbacks) { + // create input channel here through WMS + sp<IBinder> clientToken = sp<BBinder>::make(); + sp<InputTransferToken> clientInputTransferToken = sp<InputTransferToken>::make(); + + std::shared_ptr<InputChannel> inputChannel = + createInputChannel(clientToken, reinterpret_cast<const InputTransferToken&>(*hostToken), + reinterpret_cast<const SurfaceControl&>(*aSurfaceControl), + *clientInputTransferToken); + return InputReceiver_to_AInputReceiver( + new BatchedInputReceiver(reinterpret_cast<Choreographer&>(*aChoreographer), + inputChannel, clientToken, clientInputTransferToken, + callbacks)); +} + +AInputReceiver* AInputReceiver_createUnbatchedInputReceiver(ALooper* aLooper, + const AInputTransferToken* hostToken, + const ASurfaceControl* aSurfaceControl, + AInputReceiverCallbacks* callbacks) { + // create input channel here through WMS + sp<IBinder> clientToken = sp<BBinder>::make(); + sp<InputTransferToken> clientInputTransferToken = sp<InputTransferToken>::make(); + + std::shared_ptr<InputChannel> inputChannel = + createInputChannel(clientToken, reinterpret_cast<const InputTransferToken&>(*hostToken), + reinterpret_cast<const SurfaceControl&>(*aSurfaceControl), + *clientInputTransferToken); + return InputReceiver_to_AInputReceiver(new InputReceiver(reinterpret_cast<Looper*>(aLooper), + inputChannel, clientToken, + clientInputTransferToken, callbacks)); +} + +const AInputTransferToken* AInputReceiver_getInputTransferToken(AInputReceiver* aInputReceiver) { + return AInputReceiver_to_InputReceiver(aInputReceiver)->getInputTransferToken(); +} + +void AInputReceiver_release(AInputReceiver* aInputReceiver) { + InputReceiver* inputReceiver = AInputReceiver_to_InputReceiver(aInputReceiver); + inputReceiver->remove(); + delete inputReceiver; +} + +void AInputReceiverCallbacks_setMotionEventCallback(AInputReceiverCallbacks* callbacks, + AInputReceiver_onMotionEvent onMotionEvent) { + callbacks->onMotionEvent = onMotionEvent; +} + +void AInputReceiverCallbacks_setKeyEventCallback(AInputReceiverCallbacks* callbacks, + AInputReceiver_onKeyEvent onKeyEvent) { + callbacks->onKeyEvent = onKeyEvent; +} + +AInputReceiverCallbacks* AInputReceiverCallbacks_create(void* context) { + return new AInputReceiverCallbacks(context); +} + +void AInputReceiverCallbacks_release(AInputReceiverCallbacks* callbacks) { + delete callbacks; +}
\ No newline at end of file diff --git a/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/EmbeddedWindowService.java b/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/EmbeddedWindowService.java index 0fb4f90f354f..56fb30ccca9c 100644 --- a/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/EmbeddedWindowService.java +++ b/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/EmbeddedWindowService.java @@ -135,7 +135,7 @@ public class EmbeddedWindowService extends Service { c.drawText("Remote", 250, 250, paint); surface.unlockCanvasAndPost(c); WindowManager wm = getSystemService(WindowManager.class); - wm.registerBatchedSurfaceControlInputReceiver(displayId, inputTransferToken, + wm.registerBatchedSurfaceControlInputReceiver(inputTransferToken, mSurfaceControl, Choreographer.getInstance(), event -> { Log.d(TAG, "onInputEvent-remote " + event); diff --git a/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceInputTestActivity.java b/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceInputTestActivity.java index e700bc2f3d21..ac7dc9e2f31f 100644 --- a/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceInputTestActivity.java +++ b/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceInputTestActivity.java @@ -138,7 +138,7 @@ public class SurfaceInputTestActivity extends Activity { c.drawText("Local SC", 0, 0, paint); surface.unlockCanvasAndPost(c); WindowManager wm = getSystemService(WindowManager.class); - wm.registerBatchedSurfaceControlInputReceiver(getDisplayId(), + wm.registerBatchedSurfaceControlInputReceiver( attachedSurfaceControl.getInputTransferToken(), mLocalSurfaceControl, Choreographer.getInstance(), event -> { Log.d(TAG, "onInputEvent-sc " + event); @@ -159,7 +159,7 @@ public class SurfaceInputTestActivity extends Activity { holder.unlockCanvasAndPost(c); WindowManager wm = getSystemService(WindowManager.class); - wm.registerBatchedSurfaceControlInputReceiver(getDisplayId(), + wm.registerBatchedSurfaceControlInputReceiver( mLocalSurfaceView.getRootSurfaceControl().getInputTransferToken(), mLocalSurfaceView.getSurfaceControl(), Choreographer.getInstance(), event -> { |