android_view_InputChannel.cpp revision 02cafc9f6ec018ae31ca9032be67a2d8d410736a
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 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 LOGW("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 android_view_InputChannel_setNativeInputChannel(env, inputChannelObj, nativeInputChannel); 115 return inputChannelObj; 116} 117 118static 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 146static 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 162static 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 176static 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 parcelAshmemFd = parcel->readFileDescriptor(); 190 int32_t ashmemFd = dup(parcelAshmemFd); 191 if (ashmemFd < 0) { 192 LOGE("Error %d dup ashmem fd %d.", errno, parcelAshmemFd); 193 } 194 int32_t parcelReceivePipeFd = parcel->readFileDescriptor(); 195 int32_t receivePipeFd = dup(parcelReceivePipeFd); 196 if (receivePipeFd < 0) { 197 LOGE("Error %d dup receive pipe fd %d.", errno, parcelReceivePipeFd); 198 } 199 int32_t parcelSendPipeFd = parcel->readFileDescriptor(); 200 int32_t sendPipeFd = dup(parcelSendPipeFd); 201 if (sendPipeFd < 0) { 202 LOGE("Error %d dup send pipe fd %d.", errno, parcelSendPipeFd); 203 } 204 if (ashmemFd < 0 || receivePipeFd < 0 || sendPipeFd < 0) { 205 if (ashmemFd >= 0) ::close(ashmemFd); 206 if (receivePipeFd >= 0) ::close(receivePipeFd); 207 if (sendPipeFd >= 0) ::close(sendPipeFd); 208 jniThrowRuntimeException(env, 209 "Could not read input channel file descriptors from parcel."); 210 return; 211 } 212 213 InputChannel* inputChannel = new InputChannel(name, ashmemFd, 214 receivePipeFd, sendPipeFd); 215 NativeInputChannel* nativeInputChannel = new NativeInputChannel(inputChannel); 216 217 android_view_InputChannel_setNativeInputChannel(env, obj, nativeInputChannel); 218 } 219 } 220} 221 222static 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->getAshmemFd()); 234 parcel->writeDupFileDescriptor(inputChannel->getReceivePipeFd()); 235 parcel->writeDupFileDescriptor(inputChannel->getSendPipeFd()); 236 } else { 237 parcel->writeInt32(0); 238 } 239 } 240} 241 242static jstring android_view_InputChannel_nativeGetName(JNIEnv* env, jobject obj) { 243 NativeInputChannel* nativeInputChannel = 244 android_view_InputChannel_getNativeInputChannel(env, obj); 245 if (! nativeInputChannel) { 246 return NULL; 247 } 248 249 jstring name = env->NewStringUTF(nativeInputChannel->getInputChannel()->getName().string()); 250 return name; 251} 252 253// ---------------------------------------------------------------------------- 254 255static JNINativeMethod gInputChannelMethods[] = { 256 /* name, signature, funcPtr */ 257 { "nativeOpenInputChannelPair", "(Ljava/lang/String;)[Landroid/view/InputChannel;", 258 (void*)android_view_InputChannel_nativeOpenInputChannelPair }, 259 { "nativeDispose", "(Z)V", 260 (void*)android_view_InputChannel_nativeDispose }, 261 { "nativeTransferTo", "(Landroid/view/InputChannel;)V", 262 (void*)android_view_InputChannel_nativeTransferTo }, 263 { "nativeReadFromParcel", "(Landroid/os/Parcel;)V", 264 (void*)android_view_InputChannel_nativeReadFromParcel }, 265 { "nativeWriteToParcel", "(Landroid/os/Parcel;)V", 266 (void*)android_view_InputChannel_nativeWriteToParcel }, 267 { "nativeGetName", "()Ljava/lang/String;", 268 (void*)android_view_InputChannel_nativeGetName }, 269}; 270 271#define FIND_CLASS(var, className) \ 272 var = env->FindClass(className); \ 273 LOG_FATAL_IF(! var, "Unable to find class " className); \ 274 var = jclass(env->NewGlobalRef(var)); 275 276#define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \ 277 var = env->GetMethodID(clazz, methodName, methodDescriptor); \ 278 LOG_FATAL_IF(! var, "Unable to find method " methodName); 279 280#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \ 281 var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \ 282 LOG_FATAL_IF(! var, "Unable to find field " fieldName); 283 284int register_android_view_InputChannel(JNIEnv* env) { 285 int res = jniRegisterNativeMethods(env, "android/view/InputChannel", 286 gInputChannelMethods, NELEM(gInputChannelMethods)); 287 LOG_FATAL_IF(res < 0, "Unable to register native methods."); 288 289 FIND_CLASS(gInputChannelClassInfo.clazz, "android/view/InputChannel"); 290 291 GET_FIELD_ID(gInputChannelClassInfo.mPtr, gInputChannelClassInfo.clazz, 292 "mPtr", "I"); 293 294 GET_METHOD_ID(gInputChannelClassInfo.ctor, gInputChannelClassInfo.clazz, 295 "<init>", "()V"); 296 297 return 0; 298} 299 300} // namespace android 301