/* * 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. */ #include #include #define LOG_TAG "ASurfaceControlInputReceiverTest" #include #include #include #include #include #include #include #include #include #include #include #define ALOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) namespace { static struct { jmethodID onMotionEvent; jmethodID onKeyEvent; } gInputReceiver; class InputReceiverCallbackWrapper { public: explicit InputReceiverCallbackWrapper(JNIEnv* env, jobject object) { env->GetJavaVM(&mVm); mInputReceiverObj = env->NewGlobalRef(object); mAInputReceiverCallbacks = AInputReceiverCallbacks_create(this); AInputReceiverCallbacks_setKeyEventCallback(mAInputReceiverCallbacks, onKeyEventThunk); AInputReceiverCallbacks_setMotionEventCallback(mAInputReceiverCallbacks, onMotionEventThunk); } bool onKeyEvent(AInputEvent* inputEvent) { JNIEnv* env = getenv(); ScopedLocalRef event(env, AInputEvent_toJava(env, inputEvent)); AInputEvent_release(inputEvent); return env->CallBooleanMethod(mInputReceiverObj, gInputReceiver.onKeyEvent, event.get()); } bool onMotionEvent(AInputEvent* inputEvent) { JNIEnv* env = getenv(); ScopedLocalRef event(env, AInputEvent_toJava(env, inputEvent)); AInputEvent_release(inputEvent); return env->CallBooleanMethod(mInputReceiverObj, gInputReceiver.onMotionEvent, event.get()); } static bool onKeyEventThunk(void* context, AInputEvent* inputEvent) { InputReceiverCallbackWrapper* listener = reinterpret_cast(context); return listener->onKeyEvent(inputEvent); } static bool onMotionEventThunk(void* context, AInputEvent* inputEvent) { InputReceiverCallbackWrapper* listener = reinterpret_cast(context); return listener->onMotionEvent(inputEvent); } AInputReceiverCallbacks* getAInputReceiverCallbacks() { return mAInputReceiverCallbacks; } ~InputReceiverCallbackWrapper() { getenv()->DeleteGlobalRef(mInputReceiverObj); AInputReceiverCallbacks_release(mAInputReceiverCallbacks); } private: JNIEnv* getenv() { JNIEnv* env; mVm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6); return env; } jobject mInputReceiverObj; JavaVM* mVm; AInputReceiverCallbacks* mAInputReceiverCallbacks; }; class InputReceiverWrapper { public: InputReceiverWrapper(AInputReceiver* aInputReceiver, std::unique_ptr callback) : mInputReceiver(aInputReceiver), mInputReceiverCallbackWrapper(std::move(callback)) {} ~InputReceiverWrapper() { AInputReceiver_release(mInputReceiver); } AInputReceiver* mInputReceiver; private: std::unique_ptr mInputReceiverCallbackWrapper; }; static jlong nativeCreateInputReceiver(JNIEnv* env, jclass, jboolean batched, jobject hostInputTransferTokenObj, jlong surfaceControlObj, jobject inputReceiverObj) { AInputTransferToken* hostTransferToken = AInputTransferToken_fromJava(env, hostInputTransferTokenObj); ASurfaceControl* aSurfaceControl = reinterpret_cast(surfaceControlObj); std::unique_ptr callbackWrapper = std::make_unique(env, inputReceiverObj); AInputReceiver* receiver; ALooper* looper = ALooper_prepare(0); if (batched) { AChoreographer* choreographer = AChoreographer_getInstance(); receiver = AInputReceiver_createBatchedInputReceiver(choreographer, hostTransferToken, aSurfaceControl, callbackWrapper ->getAInputReceiverCallbacks()); } else { receiver = AInputReceiver_createUnbatchedInputReceiver(looper, hostTransferToken, aSurfaceControl, callbackWrapper ->getAInputReceiverCallbacks()); } InputReceiverWrapper* inputReceiverWrapper = new InputReceiverWrapper(receiver, std::move(callbackWrapper)); return reinterpret_cast(inputReceiverWrapper); } static void nativeDeleteInputReceiver(JNIEnv*, jclass, jlong receiverObj) { InputReceiverWrapper* receiver = reinterpret_cast(receiverObj); delete receiver; } static jobject nativeGetInputTransferToken(JNIEnv* env, jclass, jlong receiverObj) { InputReceiverWrapper* receiver = reinterpret_cast(receiverObj); return AInputTransferToken_toJava(env, AInputReceiver_getInputTransferToken( receiver->mInputReceiver)); } static const JNINativeMethod sMethods[] = { // clang-format off {"nCreateInputReceiver", "(ZLandroid/window/InputTransferToken;JLandroid/view/cts/util/ASurfaceControlInputReceiverTestUtils$InputReceiver;)J", (void *) nativeCreateInputReceiver}, {"nDeleteInputReceiver", "(J)V", (void *) nativeDeleteInputReceiver}, {"nGetInputTransferToken", "(J)Landroid/window/InputTransferToken;", (void*) nativeGetInputTransferToken}, // clang-format on }; } // anonymous namespace jint register_android_window_cts_ASurfaceControlInputReceiverTest(JNIEnv* env) { jclass clazz = env->FindClass("android/view/cts/util/ASurfaceControlInputReceiverTestUtils"); int err = env->RegisterNatives(clazz, sMethods, NELEM(sMethods)); jclass inputReceiver = env->FindClass( "android/view/cts/util/ASurfaceControlInputReceiverTestUtils$InputReceiver"); gInputReceiver.onMotionEvent = env->GetMethodID(inputReceiver, "onMotionEvent", "(Landroid/view/MotionEvent;)Z"); gInputReceiver.onKeyEvent = env->GetMethodID(inputReceiver, "onKeyEvent", "(Landroid/view/KeyEvent;)Z"); return err; }