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