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 android_view_InputChannel_setNativeInputChannel(env, inputChannelObj, nativeInputChannel); 115 return inputChannelObj; 116 } 117 118 static jobjectArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* env, 119 jclass clazz, jstring nameObj) { 120 const char* nameChars = env->GetStringUTFChars(nameObj, NULL); 121 String8 name(nameChars); 122 env->ReleaseStringUTFChars(nameObj, nameChars); 123 124 sp<InputChannel> serverChannel; 125 sp<InputChannel> clientChannel; 126 status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel); 127 128 if (result) { 129 LOGE("Could not open input channel pair. status=%d", result); 130 jniThrowRuntimeException(env, "Could not open input channel pair."); 131 return NULL; 132 } 133 134 // TODO more robust error checking 135 jobject serverChannelObj = android_view_InputChannel_createInputChannel(env, 136 new NativeInputChannel(serverChannel)); 137 jobject clientChannelObj = android_view_InputChannel_createInputChannel(env, 138 new NativeInputChannel(clientChannel)); 139 140 jobjectArray channelPair = env->NewObjectArray(2, gInputChannelClassInfo.clazz, NULL); 141 env->SetObjectArrayElement(channelPair, 0, serverChannelObj); 142 env->SetObjectArrayElement(channelPair, 1, clientChannelObj); 143 return channelPair; 144 } 145 146 static void android_view_InputChannel_nativeDispose(JNIEnv* env, jobject obj, jboolean finalized) { 147 NativeInputChannel* nativeInputChannel = 148 android_view_InputChannel_getNativeInputChannel(env, obj); 149 if (nativeInputChannel) { 150 if (finalized) { 151 LOGW("Input channel object '%s' was finalized without being disposed!", 152 nativeInputChannel->getInputChannel()->getName().string()); 153 } 154 155 nativeInputChannel->invokeAndRemoveDisposeCallback(env, obj); 156 157 android_view_InputChannel_setNativeInputChannel(env, obj, NULL); 158 delete nativeInputChannel; 159 } 160 } 161 162 static void android_view_InputChannel_nativeTransferTo(JNIEnv* env, jobject obj, 163 jobject otherObj) { 164 if (android_view_InputChannel_getInputChannel(env, otherObj) != NULL) { 165 jniThrowException(env, "java/lang/IllegalStateException", 166 "Other object already has a native input channel."); 167 return; 168 } 169 170 NativeInputChannel* nativeInputChannel = 171 android_view_InputChannel_getNativeInputChannel(env, obj); 172 android_view_InputChannel_setNativeInputChannel(env, otherObj, nativeInputChannel); 173 android_view_InputChannel_setNativeInputChannel(env, obj, NULL); 174 } 175 176 static void android_view_InputChannel_nativeReadFromParcel(JNIEnv* env, jobject obj, 177 jobject parcelObj) { 178 if (android_view_InputChannel_getInputChannel(env, obj) != NULL) { 179 jniThrowException(env, "java/lang/IllegalStateException", 180 "This object already has a native input channel."); 181 return; 182 } 183 184 Parcel* parcel = parcelForJavaObject(env, parcelObj); 185 if (parcel) { 186 bool isInitialized = parcel->readInt32(); 187 if (isInitialized) { 188 String8 name = parcel->readString8(); 189 int32_t ashmemFd = dup(parcel->readFileDescriptor()); 190 int32_t receivePipeFd = dup(parcel->readFileDescriptor()); 191 int32_t sendPipeFd = dup(parcel->readFileDescriptor()); 192 if (ashmemFd < 0 || receivePipeFd < 0 || sendPipeFd < 0) { 193 if (ashmemFd >= 0) ::close(ashmemFd); 194 if (receivePipeFd >= 0) ::close(receivePipeFd); 195 if (sendPipeFd >= 0) ::close(sendPipeFd); 196 jniThrowRuntimeException(env, 197 "Could not read input channel file descriptors from parcel."); 198 return; 199 } 200 201 InputChannel* inputChannel = new InputChannel(name, ashmemFd, 202 receivePipeFd, sendPipeFd); 203 NativeInputChannel* nativeInputChannel = new NativeInputChannel(inputChannel); 204 205 android_view_InputChannel_setNativeInputChannel(env, obj, nativeInputChannel); 206 } 207 } 208 } 209 210 static void android_view_InputChannel_nativeWriteToParcel(JNIEnv* env, jobject obj, 211 jobject parcelObj) { 212 Parcel* parcel = parcelForJavaObject(env, parcelObj); 213 if (parcel) { 214 NativeInputChannel* nativeInputChannel = 215 android_view_InputChannel_getNativeInputChannel(env, obj); 216 if (nativeInputChannel) { 217 sp<InputChannel> inputChannel = nativeInputChannel->getInputChannel(); 218 219 parcel->writeInt32(1); 220 parcel->writeString8(inputChannel->getName()); 221 parcel->writeDupFileDescriptor(inputChannel->getAshmemFd()); 222 parcel->writeDupFileDescriptor(inputChannel->getReceivePipeFd()); 223 parcel->writeDupFileDescriptor(inputChannel->getSendPipeFd()); 224 } else { 225 parcel->writeInt32(0); 226 } 227 } 228 } 229 230 static jstring android_view_InputChannel_nativeGetName(JNIEnv* env, jobject obj) { 231 NativeInputChannel* nativeInputChannel = 232 android_view_InputChannel_getNativeInputChannel(env, obj); 233 if (! nativeInputChannel) { 234 return NULL; 235 } 236 237 jstring name = env->NewStringUTF(nativeInputChannel->getInputChannel()->getName().string()); 238 return name; 239 } 240 241 // ---------------------------------------------------------------------------- 242 243 static JNINativeMethod gInputChannelMethods[] = { 244 /* name, signature, funcPtr */ 245 { "nativeOpenInputChannelPair", "(Ljava/lang/String;)[Landroid/view/InputChannel;", 246 (void*)android_view_InputChannel_nativeOpenInputChannelPair }, 247 { "nativeDispose", "(Z)V", 248 (void*)android_view_InputChannel_nativeDispose }, 249 { "nativeTransferTo", "(Landroid/view/InputChannel;)V", 250 (void*)android_view_InputChannel_nativeTransferTo }, 251 { "nativeReadFromParcel", "(Landroid/os/Parcel;)V", 252 (void*)android_view_InputChannel_nativeReadFromParcel }, 253 { "nativeWriteToParcel", "(Landroid/os/Parcel;)V", 254 (void*)android_view_InputChannel_nativeWriteToParcel }, 255 { "nativeGetName", "()Ljava/lang/String;", 256 (void*)android_view_InputChannel_nativeGetName }, 257 }; 258 259 #define FIND_CLASS(var, className) \ 260 var = env->FindClass(className); \ 261 LOG_FATAL_IF(! var, "Unable to find class " className); \ 262 var = jclass(env->NewGlobalRef(var)); 263 264 #define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \ 265 var = env->GetMethodID(clazz, methodName, methodDescriptor); \ 266 LOG_FATAL_IF(! var, "Unable to find method " methodName); 267 268 #define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \ 269 var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \ 270 LOG_FATAL_IF(! var, "Unable to find field " fieldName); 271 272 int register_android_view_InputChannel(JNIEnv* env) { 273 int res = jniRegisterNativeMethods(env, "android/view/InputChannel", 274 gInputChannelMethods, NELEM(gInputChannelMethods)); 275 LOG_FATAL_IF(res < 0, "Unable to register native methods."); 276 277 FIND_CLASS(gInputChannelClassInfo.clazz, "android/view/InputChannel"); 278 279 GET_FIELD_ID(gInputChannelClassInfo.mPtr, gInputChannelClassInfo.clazz, 280 "mPtr", "I"); 281 282 GET_METHOD_ID(gInputChannelClassInfo.ctor, gInputChannelClassInfo.clazz, 283 "<init>", "()V"); 284 285 return 0; 286 } 287 288 } // namespace android 289