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 31 namespace android { 32 33 // ---------------------------------------------------------------------------- 34 35 static struct { 36 jclass clazz; 37 38 jfieldID mPtr; // native object attached to the DVM InputChannel 39 jmethodID ctor; 40 } gInputChannelClassInfo; 41 42 // ---------------------------------------------------------------------------- 43 44 class NativeInputChannel { 45 public: 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 54 private: 55 sp<InputChannel> mInputChannel; 56 InputChannelObjDisposeCallback mDisposeCallback; 57 void* mDisposeData; 58 }; 59 60 // ---------------------------------------------------------------------------- 61 62 NativeInputChannel::NativeInputChannel(const sp<InputChannel>& inputChannel) : 63 mInputChannel(inputChannel), mDisposeCallback(NULL) { 64 } 65 66 NativeInputChannel::~NativeInputChannel() { 67 } 68 69 void NativeInputChannel::setDisposeCallback(InputChannelObjDisposeCallback callback, void* data) { 70 mDisposeCallback = callback; 71 mDisposeData = data; 72 } 73 74 void 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 84 static 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 90 static 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 96 sp<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 102 void 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 113 static 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 123 static 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 162 static 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 178 static 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 192 static 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 222 static 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 240 static 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 251 static 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 262 static 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 280 int 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