blob: 28164795c936f6db2eaac2363b320412fc0f5ca8 [file] [log] [blame]
/*
* 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 <memory>
#include <utility>
#define LOG_TAG "ASurfaceControlInputReceiverTest"
#include <android/choreographer.h>
#include <android/input.h>
#include <android/input_transfer_token_jni.h>
#include <android/log.h>
#include <android/looper.h>
#include <android/surface_control.h>
#include <android/surface_control_input_receiver.h>
#include <android/surface_control_jni.h>
#include <jni.h>
#include <nativehelper/JNIHelp.h>
#include <nativehelper/scoped_local_ref.h>
#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<jobject> 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<jobject> 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<InputReceiverCallbackWrapper*>(context);
return listener->onKeyEvent(inputEvent);
}
static bool onMotionEventThunk(void* context, AInputEvent* inputEvent) {
InputReceiverCallbackWrapper* listener =
reinterpret_cast<InputReceiverCallbackWrapper*>(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<void**>(&env), JNI_VERSION_1_6);
return env;
}
jobject mInputReceiverObj;
JavaVM* mVm;
AInputReceiverCallbacks* mAInputReceiverCallbacks;
};
class InputReceiverWrapper {
public:
InputReceiverWrapper(AInputReceiver* aInputReceiver,
std::unique_ptr<InputReceiverCallbackWrapper> callback)
: mInputReceiver(aInputReceiver), mInputReceiverCallbackWrapper(std::move(callback)) {}
~InputReceiverWrapper() { AInputReceiver_release(mInputReceiver); }
AInputReceiver* mInputReceiver;
private:
std::unique_ptr<InputReceiverCallbackWrapper> 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<ASurfaceControl*>(surfaceControlObj);
std::unique_ptr<InputReceiverCallbackWrapper> callbackWrapper =
std::make_unique<InputReceiverCallbackWrapper>(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<jlong>(inputReceiverWrapper);
}
static void nativeDeleteInputReceiver(JNIEnv*, jclass, jlong receiverObj) {
InputReceiverWrapper* receiver = reinterpret_cast<InputReceiverWrapper*>(receiverObj);
delete receiver;
}
static jobject nativeGetInputTransferToken(JNIEnv* env, jclass, jlong receiverObj) {
InputReceiverWrapper* receiver = reinterpret_cast<InputReceiverWrapper*>(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;
}