blob: d4a746290baa6377908b2286ff22bee38daa763d [file] [log] [blame]
/*
* Copyright (C) 2010 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 "InputChannel-JNI"
#include "android-base/stringprintf.h"
#include <nativehelper/JNIHelp.h>
#include "nativehelper/scoped_utf_chars.h"
#include <android_runtime/AndroidRuntime.h>
#include <binder/Parcel.h>
#include <utils/Log.h>
#include <input/InputTransport.h>
#include "android_view_InputChannel.h"
#include "android_os_Parcel.h"
#include "android_util_Binder.h"
#include "core_jni_helpers.h"
namespace android {
// ----------------------------------------------------------------------------
static struct {
jclass clazz;
jmethodID mCtor;
jmethodID mSetNativeInputChannel;
jfieldID mPtr; // native object attached to the DVM InputChannel
} gInputChannelClassInfo;
// ----------------------------------------------------------------------------
class NativeInputChannel {
public:
explicit NativeInputChannel(std::unique_ptr<InputChannel> inputChannel);
~NativeInputChannel();
inline std::shared_ptr<InputChannel> getInputChannel() { return mInputChannel; }
void setDisposeCallback(InputChannelObjDisposeCallback callback, void* data);
void dispose(JNIEnv* env, jobject obj);
private:
std::shared_ptr<InputChannel> mInputChannel;
InputChannelObjDisposeCallback mDisposeCallback;
void* mDisposeData;
};
// ----------------------------------------------------------------------------
NativeInputChannel::NativeInputChannel(std::unique_ptr<InputChannel> inputChannel)
: mInputChannel(std::move(inputChannel)), mDisposeCallback(nullptr) {}
NativeInputChannel::~NativeInputChannel() {
}
void NativeInputChannel::setDisposeCallback(InputChannelObjDisposeCallback callback, void* data) {
mDisposeCallback = callback;
mDisposeData = data;
}
void NativeInputChannel::dispose(JNIEnv* env, jobject obj) {
if (!mInputChannel) {
return;
}
if (mDisposeCallback) {
mDisposeCallback(env, obj, mInputChannel, mDisposeData);
mDisposeCallback = nullptr;
mDisposeData = nullptr;
}
mInputChannel.reset();
}
// ----------------------------------------------------------------------------
static NativeInputChannel* android_view_InputChannel_getNativeInputChannel(JNIEnv* env,
jobject inputChannelObj) {
jlong longPtr = env->GetLongField(inputChannelObj, gInputChannelClassInfo.mPtr);
return reinterpret_cast<NativeInputChannel*>(longPtr);
}
std::shared_ptr<InputChannel> android_view_InputChannel_getInputChannel(JNIEnv* env,
jobject inputChannelObj) {
NativeInputChannel* nativeInputChannel =
android_view_InputChannel_getNativeInputChannel(env, inputChannelObj);
return nativeInputChannel != nullptr ? nativeInputChannel->getInputChannel() : nullptr;
}
void android_view_InputChannel_setDisposeCallback(JNIEnv* env, jobject inputChannelObj,
InputChannelObjDisposeCallback callback, void* data) {
NativeInputChannel* nativeInputChannel =
android_view_InputChannel_getNativeInputChannel(env, inputChannelObj);
if (!nativeInputChannel || !nativeInputChannel->getInputChannel()) {
ALOGW("Cannot set dispose callback because input channel object has not been initialized.");
} else {
nativeInputChannel->setDisposeCallback(callback, data);
}
}
static jlong android_view_InputChannel_createInputChannel(
JNIEnv* env, std::unique_ptr<InputChannel> inputChannel) {
std::unique_ptr<NativeInputChannel> nativeInputChannel =
std::make_unique<NativeInputChannel>(std::move(inputChannel));
return reinterpret_cast<jlong>(nativeInputChannel.release());
}
jobject android_view_InputChannel_createJavaObject(JNIEnv* env,
std::unique_ptr<InputChannel> inputChannel) {
std::string name = inputChannel->getName();
jlong ptr = android_view_InputChannel_createInputChannel(env, std::move(inputChannel));
jobject javaInputChannel =
env->NewObject(gInputChannelClassInfo.clazz, gInputChannelClassInfo.mCtor);
if (!javaInputChannel) {
ALOGE("Failed to create a Java InputChannel for channel %s.", name.c_str());
return nullptr;
}
env->CallVoidMethod(javaInputChannel, gInputChannelClassInfo.mSetNativeInputChannel, ptr);
if (env->ExceptionOccurred()) {
ALOGE("Failed to set native ptr to the Java InputChannel for channel %s.",
inputChannel->getName().c_str());
return nullptr;
}
return javaInputChannel;
}
static jlongArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* env,
jclass clazz, jstring nameObj) {
ScopedUtfChars nameChars(env, nameObj);
std::string name = nameChars.c_str();
std::unique_ptr<InputChannel> serverChannel;
std::unique_ptr<InputChannel> clientChannel;
status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
if (result) {
std::string message = android::base::StringPrintf(
"Could not open input channel pair : %s", strerror(-result));
jniThrowRuntimeException(env, message.c_str());
return nullptr;
}
jlongArray channelPair = env->NewLongArray(2);
if (channelPair == nullptr) {
return nullptr;
}
jlong* outArray = env->GetLongArrayElements(channelPair, 0);
outArray[0] = android_view_InputChannel_createInputChannel(env, std::move(serverChannel));
if (env->ExceptionCheck()) {
return nullptr;
}
outArray[1] = android_view_InputChannel_createInputChannel(env, std::move(clientChannel));
if (env->ExceptionCheck()) {
return nullptr;
}
env->ReleaseLongArrayElements(channelPair, outArray, 0);
return channelPair;
}
static void InputChannel_nativeDestroy(void *rawInputChannel) {
NativeInputChannel* nativeInputChannel =
reinterpret_cast<NativeInputChannel *>(rawInputChannel);
if (nativeInputChannel) {
delete nativeInputChannel;
}
}
static jlong android_view_InputChannel_getNativeFinalizer(JNIEnv* env, jobject obj) {
return static_cast<jlong>(reinterpret_cast<uintptr_t>(&InputChannel_nativeDestroy));
}
static void android_view_InputChannel_nativeDispose(JNIEnv* env, jobject obj, jlong channel) {
NativeInputChannel* nativeInputChannel =
reinterpret_cast<NativeInputChannel*>(channel);
if (nativeInputChannel) {
nativeInputChannel->dispose(env, obj);
}
}
static jlong android_view_InputChannel_nativeReadFromParcel(JNIEnv* env, jobject obj,
jobject parcelObj) {
Parcel* parcel = parcelForJavaObject(env, parcelObj);
if (parcel) {
bool isInitialized = parcel->readInt32();
if (isInitialized) {
std::unique_ptr<InputChannel> inputChannel = std::make_unique<InputChannel>();
inputChannel->readFromParcel(parcel);
NativeInputChannel* nativeInputChannel =
new NativeInputChannel(std::move(inputChannel));
return reinterpret_cast<jlong>(nativeInputChannel);
}
}
return 0;
}
static void android_view_InputChannel_nativeWriteToParcel(JNIEnv* env, jobject obj,
jobject parcelObj, jlong channel) {
Parcel* parcel = parcelForJavaObject(env, parcelObj);
if (parcel == nullptr) {
ALOGE("Could not obtain parcel for Java object");
return;
}
NativeInputChannel* nativeInputChannel =
reinterpret_cast<NativeInputChannel*>(channel);
if (!nativeInputChannel || !nativeInputChannel->getInputChannel()) {
parcel->writeInt32(0); // not initialized
return;
}
parcel->writeInt32(1); // initialized
nativeInputChannel->getInputChannel()->writeToParcel(parcel);
}
static jstring android_view_InputChannel_nativeGetName(JNIEnv* env, jobject obj, jlong channel) {
NativeInputChannel* nativeInputChannel =
reinterpret_cast<NativeInputChannel*>(channel);
if (!nativeInputChannel || !nativeInputChannel->getInputChannel()) {
return nullptr;
}
jstring name = env->NewStringUTF(nativeInputChannel->getInputChannel()->getName().c_str());
return name;
}
static jlong android_view_InputChannel_nativeDup(JNIEnv* env, jobject obj, jlong channel) {
NativeInputChannel* nativeInputChannel =
reinterpret_cast<NativeInputChannel*>(channel);
if (nativeInputChannel == nullptr) {
jniThrowRuntimeException(env, "InputChannel has no valid NativeInputChannel");
return 0;
}
std::shared_ptr<InputChannel> inputChannel = nativeInputChannel->getInputChannel();
if (inputChannel == nullptr) {
jniThrowRuntimeException(env, "NativeInputChannel has no corresponding InputChannel");
return 0;
}
std::unique_ptr<InputChannel> dupInputChannel = inputChannel->dup();
if (dupInputChannel == nullptr) {
std::string message = android::base::StringPrintf(
"Could not duplicate input channel %s", inputChannel->getName().c_str());
jniThrowRuntimeException(env, message.c_str());
}
return reinterpret_cast<jlong>(new NativeInputChannel(std::move(dupInputChannel)));
}
static jobject android_view_InputChannel_nativeGetToken(JNIEnv* env, jobject obj, jlong channel) {
NativeInputChannel* nativeInputChannel =
reinterpret_cast<NativeInputChannel*>(channel);
if (nativeInputChannel && nativeInputChannel->getInputChannel()) {
return javaObjectForIBinder(env,
nativeInputChannel->getInputChannel()->getConnectionToken());
}
return 0;
}
// ----------------------------------------------------------------------------
static const JNINativeMethod gInputChannelMethods[] = {
/* name, signature, funcPtr */
{ "nativeOpenInputChannelPair", "(Ljava/lang/String;)[J",
(void*)android_view_InputChannel_nativeOpenInputChannelPair },
{ "nativeGetFinalizer", "()J",
(void*)android_view_InputChannel_getNativeFinalizer },
{ "nativeDispose", "(J)V",
(void*)android_view_InputChannel_nativeDispose },
{ "nativeReadFromParcel", "(Landroid/os/Parcel;)J",
(void*)android_view_InputChannel_nativeReadFromParcel },
{ "nativeWriteToParcel", "(Landroid/os/Parcel;J)V",
(void*)android_view_InputChannel_nativeWriteToParcel },
{ "nativeGetName", "(J)Ljava/lang/String;",
(void*)android_view_InputChannel_nativeGetName },
{ "nativeDup", "(J)J",
(void*)android_view_InputChannel_nativeDup },
{ "nativeGetToken", "(J)Landroid/os/IBinder;",
(void*)android_view_InputChannel_nativeGetToken },
};
int register_android_view_InputChannel(JNIEnv* env) {
int res = RegisterMethodsOrDie(env, "android/view/InputChannel", gInputChannelMethods,
NELEM(gInputChannelMethods));
jclass clazz = FindClassOrDie(env, "android/view/InputChannel");
gInputChannelClassInfo.clazz = MakeGlobalRefOrDie(env, clazz);
gInputChannelClassInfo.mCtor =
GetMethodIDOrDie(env, gInputChannelClassInfo.clazz, "<init>", "()V");
gInputChannelClassInfo.mSetNativeInputChannel =
GetMethodIDOrDie(env, gInputChannelClassInfo.clazz, "setNativeInputChannel", "(J)V");
gInputChannelClassInfo.mPtr = GetFieldIDOrDie(env, gInputChannelClassInfo.clazz, "mPtr", "J");
return res;
}
} // namespace android