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 namespace android { 30 31 // ---------------------------------------------------------------------------- 32 33 static struct { 34 jclass clazz; 35 36 jfieldID mPtr; // native object attached to the DVM InputChannel 37 jmethodID ctor; 38 } gInputChannelClassInfo; 39 40 // ---------------------------------------------------------------------------- 41 42 class NativeInputChannel { 43 public: 44 NativeInputChannel(const sp<InputChannel>& inputChannel); 45 ~NativeInputChannel(); 46 47 inline sp<InputChannel> getInputChannel() { return mInputChannel; } 48 49 void setDisposeCallback(InputChannelObjDisposeCallback callback, void* data); 50 void invokeAndRemoveDisposeCallback(JNIEnv* env, jobject obj); 51 52 private: 53 sp<InputChannel> mInputChannel; 54 InputChannelObjDisposeCallback mDisposeCallback; 55 void* mDisposeData; 56 }; 57 58 // ---------------------------------------------------------------------------- 59 60 NativeInputChannel::NativeInputChannel(const sp<InputChannel>& inputChannel) : 61 mInputChannel(inputChannel), mDisposeCallback(NULL) { 62 } 63 64 NativeInputChannel::~NativeInputChannel() { 65 } 66 67 void NativeInputChannel::setDisposeCallback(InputChannelObjDisposeCallback callback, void* data) { 68 mDisposeCallback = callback; 69 mDisposeData = data; 70 } 71 72 void NativeInputChannel::invokeAndRemoveDisposeCallback(JNIEnv* env, jobject obj) { 73 if (mDisposeCallback) { 74 mDisposeCallback(env, obj, mInputChannel, mDisposeData); 75 mDisposeCallback = NULL; 76 mDisposeData = NULL; 77 } 78 } 79 80 // ---------------------------------------------------------------------------- 81 82 static NativeInputChannel* android_view_InputChannel_getNativeInputChannel(JNIEnv* env, 83 jobject inputChannelObj) { 84 jlong longPtr = env->GetLongField(inputChannelObj, gInputChannelClassInfo.mPtr); 85 return reinterpret_cast<NativeInputChannel*>(longPtr); 86 } 87 88 static void android_view_InputChannel_setNativeInputChannel(JNIEnv* env, jobject inputChannelObj, 89 NativeInputChannel* nativeInputChannel) { 90 env->SetLongField(inputChannelObj, gInputChannelClassInfo.mPtr, 91 reinterpret_cast<jlong>(nativeInputChannel)); 92 } 93 94 sp<InputChannel> android_view_InputChannel_getInputChannel(JNIEnv* env, jobject inputChannelObj) { 95 NativeInputChannel* nativeInputChannel = 96 android_view_InputChannel_getNativeInputChannel(env, inputChannelObj); 97 return nativeInputChannel != NULL ? nativeInputChannel->getInputChannel() : NULL; 98 } 99 100 void android_view_InputChannel_setDisposeCallback(JNIEnv* env, jobject inputChannelObj, 101 InputChannelObjDisposeCallback callback, void* data) { 102 NativeInputChannel* nativeInputChannel = 103 android_view_InputChannel_getNativeInputChannel(env, inputChannelObj); 104 if (nativeInputChannel == NULL) { 105 ALOGW("Cannot set dispose callback because input channel object has not been initialized."); 106 } else { 107 nativeInputChannel->setDisposeCallback(callback, data); 108 } 109 } 110 111 static jobject android_view_InputChannel_createInputChannel(JNIEnv* env, 112 NativeInputChannel* nativeInputChannel) { 113 jobject inputChannelObj = env->NewObject(gInputChannelClassInfo.clazz, 114 gInputChannelClassInfo.ctor); 115 if (inputChannelObj) { 116 android_view_InputChannel_setNativeInputChannel(env, inputChannelObj, nativeInputChannel); 117 } 118 return inputChannelObj; 119 } 120 121 static jobjectArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* env, 122 jclass clazz, jstring nameObj) { 123 const char* nameChars = env->GetStringUTFChars(nameObj, NULL); 124 String8 name(nameChars); 125 env->ReleaseStringUTFChars(nameObj, nameChars); 126 127 sp<InputChannel> serverChannel; 128 sp<InputChannel> clientChannel; 129 status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel); 130 131 if (result) { 132 String8 message; 133 message.appendFormat("Could not open input channel pair. status=%d", result); 134 jniThrowRuntimeException(env, message.string()); 135 return NULL; 136 } 137 138 jobjectArray channelPair = env->NewObjectArray(2, gInputChannelClassInfo.clazz, NULL); 139 if (env->ExceptionCheck()) { 140 return NULL; 141 } 142 143 jobject serverChannelObj = android_view_InputChannel_createInputChannel(env, 144 new NativeInputChannel(serverChannel)); 145 if (env->ExceptionCheck()) { 146 return NULL; 147 } 148 149 jobject clientChannelObj = android_view_InputChannel_createInputChannel(env, 150 new NativeInputChannel(clientChannel)); 151 if (env->ExceptionCheck()) { 152 return NULL; 153 } 154 155 env->SetObjectArrayElement(channelPair, 0, serverChannelObj); 156 env->SetObjectArrayElement(channelPair, 1, clientChannelObj); 157 return channelPair; 158 } 159 160 static void android_view_InputChannel_nativeDispose(JNIEnv* env, jobject obj, jboolean finalized) { 161 NativeInputChannel* nativeInputChannel = 162 android_view_InputChannel_getNativeInputChannel(env, obj); 163 if (nativeInputChannel) { 164 if (finalized) { 165 ALOGW("Input channel object '%s' was finalized without being disposed!", 166 nativeInputChannel->getInputChannel()->getName().string()); 167 } 168 169 nativeInputChannel->invokeAndRemoveDisposeCallback(env, obj); 170 171 android_view_InputChannel_setNativeInputChannel(env, obj, NULL); 172 delete nativeInputChannel; 173 } 174 } 175 176 static void android_view_InputChannel_nativeTransferTo(JNIEnv* env, jobject obj, 177 jobject otherObj) { 178 if (android_view_InputChannel_getNativeInputChannel(env, otherObj) != NULL) { 179 jniThrowException(env, "java/lang/IllegalStateException", 180 "Other object already has a native input channel."); 181 return; 182 } 183 184 NativeInputChannel* nativeInputChannel = 185 android_view_InputChannel_getNativeInputChannel(env, obj); 186 android_view_InputChannel_setNativeInputChannel(env, otherObj, nativeInputChannel); 187 android_view_InputChannel_setNativeInputChannel(env, obj, NULL); 188 } 189 190 static void android_view_InputChannel_nativeReadFromParcel(JNIEnv* env, jobject obj, 191 jobject parcelObj) { 192 if (android_view_InputChannel_getNativeInputChannel(env, obj) != NULL) { 193 jniThrowException(env, "java/lang/IllegalStateException", 194 "This object already has a native input channel."); 195 return; 196 } 197 198 Parcel* parcel = parcelForJavaObject(env, parcelObj); 199 if (parcel) { 200 bool isInitialized = parcel->readInt32(); 201 if (isInitialized) { 202 String8 name = parcel->readString8(); 203 int rawFd = parcel->readFileDescriptor(); 204 int dupFd = dup(rawFd); 205 if (dupFd < 0) { 206 ALOGE("Error %d dup channel fd %d.", errno, rawFd); 207 jniThrowRuntimeException(env, 208 "Could not read input channel file descriptors from parcel."); 209 return; 210 } 211 212 InputChannel* inputChannel = new InputChannel(name, dupFd); 213 NativeInputChannel* nativeInputChannel = new NativeInputChannel(inputChannel); 214 215 android_view_InputChannel_setNativeInputChannel(env, obj, nativeInputChannel); 216 } 217 } 218 } 219 220 static void android_view_InputChannel_nativeWriteToParcel(JNIEnv* env, jobject obj, 221 jobject parcelObj) { 222 Parcel* parcel = parcelForJavaObject(env, parcelObj); 223 if (parcel) { 224 NativeInputChannel* nativeInputChannel = 225 android_view_InputChannel_getNativeInputChannel(env, obj); 226 if (nativeInputChannel) { 227 sp<InputChannel> inputChannel = nativeInputChannel->getInputChannel(); 228 229 parcel->writeInt32(1); 230 parcel->writeString8(inputChannel->getName()); 231 parcel->writeDupFileDescriptor(inputChannel->getFd()); 232 } else { 233 parcel->writeInt32(0); 234 } 235 } 236 } 237 238 static jstring android_view_InputChannel_nativeGetName(JNIEnv* env, jobject obj) { 239 NativeInputChannel* nativeInputChannel = 240 android_view_InputChannel_getNativeInputChannel(env, obj); 241 if (! nativeInputChannel) { 242 return NULL; 243 } 244 245 jstring name = env->NewStringUTF(nativeInputChannel->getInputChannel()->getName().string()); 246 return name; 247 } 248 249 static void android_view_InputChannel_nativeDup(JNIEnv* env, jobject obj, jobject otherObj) { 250 NativeInputChannel* nativeInputChannel = 251 android_view_InputChannel_getNativeInputChannel(env, obj); 252 if (nativeInputChannel) { 253 android_view_InputChannel_setNativeInputChannel(env, otherObj, 254 new NativeInputChannel(nativeInputChannel->getInputChannel()->dup())); 255 } 256 } 257 258 // ---------------------------------------------------------------------------- 259 260 static JNINativeMethod gInputChannelMethods[] = { 261 /* name, signature, funcPtr */ 262 { "nativeOpenInputChannelPair", "(Ljava/lang/String;)[Landroid/view/InputChannel;", 263 (void*)android_view_InputChannel_nativeOpenInputChannelPair }, 264 { "nativeDispose", "(Z)V", 265 (void*)android_view_InputChannel_nativeDispose }, 266 { "nativeTransferTo", "(Landroid/view/InputChannel;)V", 267 (void*)android_view_InputChannel_nativeTransferTo }, 268 { "nativeReadFromParcel", "(Landroid/os/Parcel;)V", 269 (void*)android_view_InputChannel_nativeReadFromParcel }, 270 { "nativeWriteToParcel", "(Landroid/os/Parcel;)V", 271 (void*)android_view_InputChannel_nativeWriteToParcel }, 272 { "nativeGetName", "()Ljava/lang/String;", 273 (void*)android_view_InputChannel_nativeGetName }, 274 { "nativeDup", "(Landroid/view/InputChannel;)V", 275 (void*)android_view_InputChannel_nativeDup }, 276 }; 277 278 #define FIND_CLASS(var, className) \ 279 var = env->FindClass(className); \ 280 LOG_FATAL_IF(! var, "Unable to find class " className); \ 281 var = jclass(env->NewGlobalRef(var)); 282 283 #define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \ 284 var = env->GetMethodID(clazz, methodName, methodDescriptor); \ 285 LOG_FATAL_IF(! var, "Unable to find method " methodName); 286 287 #define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \ 288 var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \ 289 LOG_FATAL_IF(! var, "Unable to find field " fieldName); 290 291 int register_android_view_InputChannel(JNIEnv* env) { 292 int res = jniRegisterNativeMethods(env, "android/view/InputChannel", 293 gInputChannelMethods, NELEM(gInputChannelMethods)); 294 LOG_FATAL_IF(res < 0, "Unable to register native methods."); 295 296 FIND_CLASS(gInputChannelClassInfo.clazz, "android/view/InputChannel"); 297 298 GET_FIELD_ID(gInputChannelClassInfo.mPtr, gInputChannelClassInfo.clazz, 299 "mPtr", "J"); 300 301 GET_METHOD_ID(gInputChannelClassInfo.ctor, gInputChannelClassInfo.clazz, 302 "<init>", "()V"); 303 304 return 0; 305 } 306 307 } // namespace android 308