android_view_InputChannel.cpp revision b93a03f841d93498bfea6cc92a22faa34bce1337
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 <androidfw/InputTransport.h> 25#include "android_view_InputChannel.h" 26#include "android_util_Binder.h" 27 28namespace android { 29 30// ---------------------------------------------------------------------------- 31 32static struct { 33 jclass clazz; 34 35 jfieldID mPtr; // native object attached to the DVM InputChannel 36 jmethodID ctor; 37} gInputChannelClassInfo; 38 39// ---------------------------------------------------------------------------- 40 41class NativeInputChannel { 42public: 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 51private: 52 sp<InputChannel> mInputChannel; 53 InputChannelObjDisposeCallback mDisposeCallback; 54 void* mDisposeData; 55}; 56 57// ---------------------------------------------------------------------------- 58 59NativeInputChannel::NativeInputChannel(const sp<InputChannel>& inputChannel) : 60 mInputChannel(inputChannel), mDisposeCallback(NULL) { 61} 62 63NativeInputChannel::~NativeInputChannel() { 64} 65 66void NativeInputChannel::setDisposeCallback(InputChannelObjDisposeCallback callback, void* data) { 67 mDisposeCallback = callback; 68 mDisposeData = data; 69} 70 71void 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 81static 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 87static 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 93sp<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 99void 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 ALOGW("Cannot set dispose callback because input channel object has not been initialized."); 105 } else { 106 nativeInputChannel->setDisposeCallback(callback, data); 107 } 108} 109 110static 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 120static 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 159static 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 ALOGW("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 175static 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 189static 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 int rawFd = parcel->readFileDescriptor(); 203 int dupFd = dup(rawFd); 204 if (dupFd < 0) { 205 ALOGE("Error %d dup channel fd %d.", errno, rawFd); 206 jniThrowRuntimeException(env, 207 "Could not read input channel file descriptors from parcel."); 208 return; 209 } 210 211 InputChannel* inputChannel = new InputChannel(name, dupFd); 212 NativeInputChannel* nativeInputChannel = new NativeInputChannel(inputChannel); 213 214 android_view_InputChannel_setNativeInputChannel(env, obj, nativeInputChannel); 215 } 216 } 217} 218 219static void android_view_InputChannel_nativeWriteToParcel(JNIEnv* env, jobject obj, 220 jobject parcelObj) { 221 Parcel* parcel = parcelForJavaObject(env, parcelObj); 222 if (parcel) { 223 NativeInputChannel* nativeInputChannel = 224 android_view_InputChannel_getNativeInputChannel(env, obj); 225 if (nativeInputChannel) { 226 sp<InputChannel> inputChannel = nativeInputChannel->getInputChannel(); 227 228 parcel->writeInt32(1); 229 parcel->writeString8(inputChannel->getName()); 230 parcel->writeDupFileDescriptor(inputChannel->getFd()); 231 } else { 232 parcel->writeInt32(0); 233 } 234 } 235} 236 237static jstring android_view_InputChannel_nativeGetName(JNIEnv* env, jobject obj) { 238 NativeInputChannel* nativeInputChannel = 239 android_view_InputChannel_getNativeInputChannel(env, obj); 240 if (! nativeInputChannel) { 241 return NULL; 242 } 243 244 jstring name = env->NewStringUTF(nativeInputChannel->getInputChannel()->getName().string()); 245 return name; 246} 247 248// ---------------------------------------------------------------------------- 249 250static JNINativeMethod gInputChannelMethods[] = { 251 /* name, signature, funcPtr */ 252 { "nativeOpenInputChannelPair", "(Ljava/lang/String;)[Landroid/view/InputChannel;", 253 (void*)android_view_InputChannel_nativeOpenInputChannelPair }, 254 { "nativeDispose", "(Z)V", 255 (void*)android_view_InputChannel_nativeDispose }, 256 { "nativeTransferTo", "(Landroid/view/InputChannel;)V", 257 (void*)android_view_InputChannel_nativeTransferTo }, 258 { "nativeReadFromParcel", "(Landroid/os/Parcel;)V", 259 (void*)android_view_InputChannel_nativeReadFromParcel }, 260 { "nativeWriteToParcel", "(Landroid/os/Parcel;)V", 261 (void*)android_view_InputChannel_nativeWriteToParcel }, 262 { "nativeGetName", "()Ljava/lang/String;", 263 (void*)android_view_InputChannel_nativeGetName }, 264}; 265 266#define FIND_CLASS(var, className) \ 267 var = env->FindClass(className); \ 268 LOG_FATAL_IF(! var, "Unable to find class " className); \ 269 var = jclass(env->NewGlobalRef(var)); 270 271#define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \ 272 var = env->GetMethodID(clazz, methodName, methodDescriptor); \ 273 LOG_FATAL_IF(! var, "Unable to find method " methodName); 274 275#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \ 276 var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \ 277 LOG_FATAL_IF(! var, "Unable to find field " fieldName); 278 279int register_android_view_InputChannel(JNIEnv* env) { 280 int res = jniRegisterNativeMethods(env, "android/view/InputChannel", 281 gInputChannelMethods, NELEM(gInputChannelMethods)); 282 LOG_FATAL_IF(res < 0, "Unable to register native methods."); 283 284 FIND_CLASS(gInputChannelClassInfo.clazz, "android/view/InputChannel"); 285 286 GET_FIELD_ID(gInputChannelClassInfo.mPtr, gInputChannelClassInfo.clazz, 287 "mPtr", "I"); 288 289 GET_METHOD_ID(gInputChannelClassInfo.ctor, gInputChannelClassInfo.clazz, 290 "<init>", "()V"); 291 292 return 0; 293} 294 295} // namespace android 296