1/* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#define LOG_TAG "InputChannel-JNI" 18 19#include "JNIHelp.h" 20 21#include <android_runtime/AndroidRuntime.h> 22#include <binder/Parcel.h> 23#include <utils/Log.h> 24#include <input/InputTransport.h> 25#include "android_view_InputChannel.h" 26#include "android_os_Parcel.h" 27#include "android_util_Binder.h" 28 29#include "core_jni_helpers.h" 30 31namespace android { 32 33// ---------------------------------------------------------------------------- 34 35static struct { 36 jclass clazz; 37 38 jfieldID mPtr; // native object attached to the DVM InputChannel 39 jmethodID ctor; 40} gInputChannelClassInfo; 41 42// ---------------------------------------------------------------------------- 43 44class NativeInputChannel { 45public: 46 NativeInputChannel(const sp<InputChannel>& inputChannel); 47 ~NativeInputChannel(); 48 49 inline sp<InputChannel> getInputChannel() { return mInputChannel; } 50 51 void setDisposeCallback(InputChannelObjDisposeCallback callback, void* data); 52 void invokeAndRemoveDisposeCallback(JNIEnv* env, jobject obj); 53 54private: 55 sp<InputChannel> mInputChannel; 56 InputChannelObjDisposeCallback mDisposeCallback; 57 void* mDisposeData; 58}; 59 60// ---------------------------------------------------------------------------- 61 62NativeInputChannel::NativeInputChannel(const sp<InputChannel>& inputChannel) : 63 mInputChannel(inputChannel), mDisposeCallback(NULL) { 64} 65 66NativeInputChannel::~NativeInputChannel() { 67} 68 69void NativeInputChannel::setDisposeCallback(InputChannelObjDisposeCallback callback, void* data) { 70 mDisposeCallback = callback; 71 mDisposeData = data; 72} 73 74void NativeInputChannel::invokeAndRemoveDisposeCallback(JNIEnv* env, jobject obj) { 75 if (mDisposeCallback) { 76 mDisposeCallback(env, obj, mInputChannel, mDisposeData); 77 mDisposeCallback = NULL; 78 mDisposeData = NULL; 79 } 80} 81 82// ---------------------------------------------------------------------------- 83 84static NativeInputChannel* android_view_InputChannel_getNativeInputChannel(JNIEnv* env, 85 jobject inputChannelObj) { 86 jlong longPtr = env->GetLongField(inputChannelObj, gInputChannelClassInfo.mPtr); 87 return reinterpret_cast<NativeInputChannel*>(longPtr); 88} 89 90static void android_view_InputChannel_setNativeInputChannel(JNIEnv* env, jobject inputChannelObj, 91 NativeInputChannel* nativeInputChannel) { 92 env->SetLongField(inputChannelObj, gInputChannelClassInfo.mPtr, 93 reinterpret_cast<jlong>(nativeInputChannel)); 94} 95 96sp<InputChannel> android_view_InputChannel_getInputChannel(JNIEnv* env, jobject inputChannelObj) { 97 NativeInputChannel* nativeInputChannel = 98 android_view_InputChannel_getNativeInputChannel(env, inputChannelObj); 99 return nativeInputChannel != NULL ? nativeInputChannel->getInputChannel() : NULL; 100} 101 102void android_view_InputChannel_setDisposeCallback(JNIEnv* env, jobject inputChannelObj, 103 InputChannelObjDisposeCallback callback, void* data) { 104 NativeInputChannel* nativeInputChannel = 105 android_view_InputChannel_getNativeInputChannel(env, inputChannelObj); 106 if (nativeInputChannel == NULL) { 107 ALOGW("Cannot set dispose callback because input channel object has not been initialized."); 108 } else { 109 nativeInputChannel->setDisposeCallback(callback, data); 110 } 111} 112 113static jobject android_view_InputChannel_createInputChannel(JNIEnv* env, 114 NativeInputChannel* nativeInputChannel) { 115 jobject inputChannelObj = env->NewObject(gInputChannelClassInfo.clazz, 116 gInputChannelClassInfo.ctor); 117 if (inputChannelObj) { 118 android_view_InputChannel_setNativeInputChannel(env, inputChannelObj, nativeInputChannel); 119 } 120 return inputChannelObj; 121} 122 123static jobjectArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* env, 124 jclass clazz, jstring nameObj) { 125 const char* nameChars = env->GetStringUTFChars(nameObj, NULL); 126 String8 name(nameChars); 127 env->ReleaseStringUTFChars(nameObj, nameChars); 128 129 sp<InputChannel> serverChannel; 130 sp<InputChannel> clientChannel; 131 status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel); 132 133 if (result) { 134 String8 message; 135 message.appendFormat("Could not open input channel pair. status=%d", result); 136 jniThrowRuntimeException(env, message.string()); 137 return NULL; 138 } 139 140 jobjectArray channelPair = env->NewObjectArray(2, gInputChannelClassInfo.clazz, NULL); 141 if (env->ExceptionCheck()) { 142 return NULL; 143 } 144 145 jobject serverChannelObj = android_view_InputChannel_createInputChannel(env, 146 new NativeInputChannel(serverChannel)); 147 if (env->ExceptionCheck()) { 148 return NULL; 149 } 150 151 jobject clientChannelObj = android_view_InputChannel_createInputChannel(env, 152 new NativeInputChannel(clientChannel)); 153 if (env->ExceptionCheck()) { 154 return NULL; 155 } 156 157 env->SetObjectArrayElement(channelPair, 0, serverChannelObj); 158 env->SetObjectArrayElement(channelPair, 1, clientChannelObj); 159 return channelPair; 160} 161 162static void android_view_InputChannel_nativeDispose(JNIEnv* env, jobject obj, jboolean finalized) { 163 NativeInputChannel* nativeInputChannel = 164 android_view_InputChannel_getNativeInputChannel(env, obj); 165 if (nativeInputChannel) { 166 if (finalized) { 167 ALOGW("Input channel object '%s' was finalized without being disposed!", 168 nativeInputChannel->getInputChannel()->getName().string()); 169 } 170 171 nativeInputChannel->invokeAndRemoveDisposeCallback(env, obj); 172 173 android_view_InputChannel_setNativeInputChannel(env, obj, NULL); 174 delete nativeInputChannel; 175 } 176} 177 178static void android_view_InputChannel_nativeTransferTo(JNIEnv* env, jobject obj, 179 jobject otherObj) { 180 if (android_view_InputChannel_getNativeInputChannel(env, otherObj) != NULL) { 181 jniThrowException(env, "java/lang/IllegalStateException", 182 "Other object already has a native input channel."); 183 return; 184 } 185 186 NativeInputChannel* nativeInputChannel = 187 android_view_InputChannel_getNativeInputChannel(env, obj); 188 android_view_InputChannel_setNativeInputChannel(env, otherObj, nativeInputChannel); 189 android_view_InputChannel_setNativeInputChannel(env, obj, NULL); 190} 191 192static void android_view_InputChannel_nativeReadFromParcel(JNIEnv* env, jobject obj, 193 jobject parcelObj) { 194 if (android_view_InputChannel_getNativeInputChannel(env, obj) != NULL) { 195 jniThrowException(env, "java/lang/IllegalStateException", 196 "This object already has a native input channel."); 197 return; 198 } 199 200 Parcel* parcel = parcelForJavaObject(env, parcelObj); 201 if (parcel) { 202 bool isInitialized = parcel->readInt32(); 203 if (isInitialized) { 204 String8 name = parcel->readString8(); 205 int rawFd = parcel->readFileDescriptor(); 206 int dupFd = dup(rawFd); 207 if (dupFd < 0) { 208 ALOGE("Error %d dup channel fd %d.", errno, rawFd); 209 jniThrowRuntimeException(env, 210 "Could not read input channel file descriptors from parcel."); 211 return; 212 } 213 214 InputChannel* inputChannel = new InputChannel(name, dupFd); 215 NativeInputChannel* nativeInputChannel = new NativeInputChannel(inputChannel); 216 217 android_view_InputChannel_setNativeInputChannel(env, obj, nativeInputChannel); 218 } 219 } 220} 221 222static void android_view_InputChannel_nativeWriteToParcel(JNIEnv* env, jobject obj, 223 jobject parcelObj) { 224 Parcel* parcel = parcelForJavaObject(env, parcelObj); 225 if (parcel) { 226 NativeInputChannel* nativeInputChannel = 227 android_view_InputChannel_getNativeInputChannel(env, obj); 228 if (nativeInputChannel) { 229 sp<InputChannel> inputChannel = nativeInputChannel->getInputChannel(); 230 231 parcel->writeInt32(1); 232 parcel->writeString8(inputChannel->getName()); 233 parcel->writeDupFileDescriptor(inputChannel->getFd()); 234 } else { 235 parcel->writeInt32(0); 236 } 237 } 238} 239 240static jstring android_view_InputChannel_nativeGetName(JNIEnv* env, jobject obj) { 241 NativeInputChannel* nativeInputChannel = 242 android_view_InputChannel_getNativeInputChannel(env, obj); 243 if (! nativeInputChannel) { 244 return NULL; 245 } 246 247 jstring name = env->NewStringUTF(nativeInputChannel->getInputChannel()->getName().string()); 248 return name; 249} 250 251static void android_view_InputChannel_nativeDup(JNIEnv* env, jobject obj, jobject otherObj) { 252 NativeInputChannel* nativeInputChannel = 253 android_view_InputChannel_getNativeInputChannel(env, obj); 254 if (nativeInputChannel) { 255 android_view_InputChannel_setNativeInputChannel(env, otherObj, 256 new NativeInputChannel(nativeInputChannel->getInputChannel()->dup())); 257 } 258} 259 260// ---------------------------------------------------------------------------- 261 262static const JNINativeMethod gInputChannelMethods[] = { 263 /* name, signature, funcPtr */ 264 { "nativeOpenInputChannelPair", "(Ljava/lang/String;)[Landroid/view/InputChannel;", 265 (void*)android_view_InputChannel_nativeOpenInputChannelPair }, 266 { "nativeDispose", "(Z)V", 267 (void*)android_view_InputChannel_nativeDispose }, 268 { "nativeTransferTo", "(Landroid/view/InputChannel;)V", 269 (void*)android_view_InputChannel_nativeTransferTo }, 270 { "nativeReadFromParcel", "(Landroid/os/Parcel;)V", 271 (void*)android_view_InputChannel_nativeReadFromParcel }, 272 { "nativeWriteToParcel", "(Landroid/os/Parcel;)V", 273 (void*)android_view_InputChannel_nativeWriteToParcel }, 274 { "nativeGetName", "()Ljava/lang/String;", 275 (void*)android_view_InputChannel_nativeGetName }, 276 { "nativeDup", "(Landroid/view/InputChannel;)V", 277 (void*)android_view_InputChannel_nativeDup }, 278}; 279 280int register_android_view_InputChannel(JNIEnv* env) { 281 int res = RegisterMethodsOrDie(env, "android/view/InputChannel", gInputChannelMethods, 282 NELEM(gInputChannelMethods)); 283 284 jclass clazz = FindClassOrDie(env, "android/view/InputChannel"); 285 gInputChannelClassInfo.clazz = MakeGlobalRefOrDie(env, clazz); 286 287 gInputChannelClassInfo.mPtr = GetFieldIDOrDie(env, gInputChannelClassInfo.clazz, "mPtr", "J"); 288 289 gInputChannelClassInfo.ctor = GetMethodIDOrDie(env, gInputChannelClassInfo.clazz, "<init>", 290 "()V"); 291 292 return res; 293} 294 295} // namespace android 296