android_view_InputEventReceiver.cpp revision 3bdcdd8531781569d501e7023c22e25e2bae0dd1
1/* 2 * Copyright (C) 2011 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 "InputEventReceiver" 18 19//#define LOG_NDEBUG 0 20 21// Log debug messages about the dispatch cycle. 22#define DEBUG_DISPATCH_CYCLE 0 23 24 25#include "JNIHelp.h" 26 27#include <android_runtime/AndroidRuntime.h> 28#include <utils/Log.h> 29#include <utils/Looper.h> 30#include <utils/threads.h> 31#include <androidfw/InputTransport.h> 32#include "android_os_MessageQueue.h" 33#include "android_view_InputChannel.h" 34#include "android_view_KeyEvent.h" 35#include "android_view_MotionEvent.h" 36 37namespace android { 38 39static struct { 40 jclass clazz; 41 42 jmethodID dispatchInputEvent; 43 jmethodID dispatchBatchedInputEventPending; 44} gInputEventReceiverClassInfo; 45 46 47class NativeInputEventReceiver : public RefBase { 48public: 49 NativeInputEventReceiver(JNIEnv* env, 50 jobject receiverObj, const sp<InputChannel>& inputChannel, 51 const sp<MessageQueue>& messageQueue); 52 53 status_t initialize(); 54 status_t finishInputEvent(uint32_t seq, bool handled); 55 status_t consumeEvents(JNIEnv* env, bool consumeBatches); 56 57protected: 58 virtual ~NativeInputEventReceiver(); 59 60private: 61 jobject mReceiverObjGlobal; 62 InputConsumer mInputConsumer; 63 sp<MessageQueue> mMessageQueue; 64 PreallocatedInputEventFactory mInputEventFactory; 65 bool mBatchedInputEventPending; 66 67 const char* getInputChannelName() { 68 return mInputConsumer.getChannel()->getName().string(); 69 } 70 71 static int handleReceiveCallback(int receiveFd, int events, void* data); 72}; 73 74 75NativeInputEventReceiver::NativeInputEventReceiver(JNIEnv* env, 76 jobject receiverObj, const sp<InputChannel>& inputChannel, 77 const sp<MessageQueue>& messageQueue) : 78 mReceiverObjGlobal(env->NewGlobalRef(receiverObj)), 79 mInputConsumer(inputChannel), mMessageQueue(messageQueue), 80 mBatchedInputEventPending(false) { 81#if DEBUG_DISPATCH_CYCLE 82 ALOGD("channel '%s' ~ Initializing input event receiver.", getInputChannelName()); 83#endif 84} 85 86NativeInputEventReceiver::~NativeInputEventReceiver() { 87#if DEBUG_DISPATCH_CYCLE 88 ALOGD("channel '%s' ~ Disposing input event receiver.", getInputChannelName()); 89#endif 90 91 mMessageQueue->getLooper()->removeFd(mInputConsumer.getChannel()->getFd()); 92 93 JNIEnv* env = AndroidRuntime::getJNIEnv(); 94 env->DeleteGlobalRef(mReceiverObjGlobal); 95} 96 97status_t NativeInputEventReceiver::initialize() { 98 int receiveFd = mInputConsumer.getChannel()->getFd(); 99 mMessageQueue->getLooper()->addFd( 100 receiveFd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this); 101 return OK; 102} 103 104status_t NativeInputEventReceiver::finishInputEvent(uint32_t seq, bool handled) { 105#if DEBUG_DISPATCH_CYCLE 106 ALOGD("channel '%s' ~ Finished input event.", getInputChannelName()); 107#endif 108 109 status_t status = mInputConsumer.sendFinishedSignal(seq, handled); 110 if (status) { 111 ALOGW("Failed to send finished signal on channel '%s'. status=%d", 112 getInputChannelName(), status); 113 } 114 return status; 115} 116 117int NativeInputEventReceiver::handleReceiveCallback(int receiveFd, int events, void* data) { 118 sp<NativeInputEventReceiver> r = static_cast<NativeInputEventReceiver*>(data); 119 120 if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) { 121 ALOGE("channel '%s' ~ Publisher closed input channel or an error occurred. " 122 "events=0x%x", r->getInputChannelName(), events); 123 return 0; // remove the callback 124 } 125 126 if (!(events & ALOOPER_EVENT_INPUT)) { 127 ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event. " 128 "events=0x%x", r->getInputChannelName(), events); 129 return 1; 130 } 131 132 JNIEnv* env = AndroidRuntime::getJNIEnv(); 133 status_t status = r->consumeEvents(env, false /*consumeBatches*/); 134 r->mMessageQueue->raiseAndClearException(env, "handleReceiveCallback"); 135 return status == OK || status == NO_MEMORY ? 1 : 0; 136} 137 138status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env, bool consumeBatches) { 139#if DEBUG_DISPATCH_CYCLE 140 ALOGD("channel '%s' ~ Consuming input events, consumeBatches=%s.", getInputChannelName(), 141 consumeBatches ? "true" : "false"); 142#endif 143 144 if (consumeBatches) { 145 mBatchedInputEventPending = false; 146 } 147 148 bool skipCallbacks = false; 149 for (;;) { 150 uint32_t seq; 151 InputEvent* inputEvent; 152 status_t status = mInputConsumer.consume(&mInputEventFactory, 153 consumeBatches, &seq, &inputEvent); 154 if (status) { 155 if (status == WOULD_BLOCK) { 156 if (!skipCallbacks && !mBatchedInputEventPending 157 && mInputConsumer.hasPendingBatch()) { 158 // There is a pending batch. Come back later. 159 mBatchedInputEventPending = true; 160#if DEBUG_DISPATCH_CYCLE 161 ALOGD("channel '%s' ~ Dispatching batched input event pending notification.", 162 getInputChannelName()); 163#endif 164 env->CallVoidMethod(mReceiverObjGlobal, 165 gInputEventReceiverClassInfo.dispatchBatchedInputEventPending); 166 if (env->ExceptionCheck()) { 167 ALOGE("Exception dispatching batched input events."); 168 mBatchedInputEventPending = false; // try again later 169 } 170 } 171 return OK; 172 } 173 ALOGE("channel '%s' ~ Failed to consume input event. status=%d", 174 getInputChannelName(), status); 175 return status; 176 } 177 assert(inputEvent); 178 179 if (!skipCallbacks) { 180 jobject inputEventObj; 181 switch (inputEvent->getType()) { 182 case AINPUT_EVENT_TYPE_KEY: 183#if DEBUG_DISPATCH_CYCLE 184 ALOGD("channel '%s' ~ Received key event.", getInputChannelName()); 185#endif 186 inputEventObj = android_view_KeyEvent_fromNative(env, 187 static_cast<KeyEvent*>(inputEvent)); 188 break; 189 190 case AINPUT_EVENT_TYPE_MOTION: 191#if DEBUG_DISPATCH_CYCLE 192 ALOGD("channel '%s' ~ Received motion event.", getInputChannelName()); 193#endif 194 inputEventObj = android_view_MotionEvent_obtainAsCopy(env, 195 static_cast<MotionEvent*>(inputEvent)); 196 break; 197 198 default: 199 assert(false); // InputConsumer should prevent this from ever happening 200 inputEventObj = NULL; 201 } 202 203 if (inputEventObj) { 204#if DEBUG_DISPATCH_CYCLE 205 ALOGD("channel '%s' ~ Dispatching input event.", getInputChannelName()); 206#endif 207 env->CallVoidMethod(mReceiverObjGlobal, 208 gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj); 209 if (env->ExceptionCheck()) { 210 ALOGE("Exception dispatching input event."); 211 skipCallbacks = true; 212 } 213 } else { 214 ALOGW("channel '%s' ~ Failed to obtain event object.", getInputChannelName()); 215 skipCallbacks = true; 216 } 217 } 218 219 if (skipCallbacks) { 220 mInputConsumer.sendFinishedSignal(seq, false); 221 } 222 } 223} 224 225 226static jint nativeInit(JNIEnv* env, jclass clazz, jobject receiverObj, 227 jobject inputChannelObj, jobject messageQueueObj) { 228 sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env, 229 inputChannelObj); 230 if (inputChannel == NULL) { 231 jniThrowRuntimeException(env, "InputChannel is not initialized."); 232 return 0; 233 } 234 235 sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj); 236 if (messageQueue == NULL) { 237 jniThrowRuntimeException(env, "MessageQueue is not initialized."); 238 return 0; 239 } 240 241 sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env, 242 receiverObj, inputChannel, messageQueue); 243 status_t status = receiver->initialize(); 244 if (status) { 245 String8 message; 246 message.appendFormat("Failed to initialize input event receiver. status=%d", status); 247 jniThrowRuntimeException(env, message.string()); 248 return 0; 249 } 250 251 receiver->incStrong(gInputEventReceiverClassInfo.clazz); // retain a reference for the object 252 return reinterpret_cast<jint>(receiver.get()); 253} 254 255static void nativeDispose(JNIEnv* env, jclass clazz, jint receiverPtr) { 256 sp<NativeInputEventReceiver> receiver = 257 reinterpret_cast<NativeInputEventReceiver*>(receiverPtr); 258 receiver->decStrong(gInputEventReceiverClassInfo.clazz); // drop reference held by the object 259} 260 261static void nativeFinishInputEvent(JNIEnv* env, jclass clazz, jint receiverPtr, 262 jint seq, jboolean handled) { 263 sp<NativeInputEventReceiver> receiver = 264 reinterpret_cast<NativeInputEventReceiver*>(receiverPtr); 265 status_t status = receiver->finishInputEvent(seq, handled); 266 if (status && status != DEAD_OBJECT) { 267 String8 message; 268 message.appendFormat("Failed to finish input event. status=%d", status); 269 jniThrowRuntimeException(env, message.string()); 270 } 271} 272 273static void nativeConsumeBatchedInputEvents(JNIEnv* env, jclass clazz, jint receiverPtr) { 274 sp<NativeInputEventReceiver> receiver = 275 reinterpret_cast<NativeInputEventReceiver*>(receiverPtr); 276 status_t status = receiver->consumeEvents(env, true /*consumeBatches*/); 277 if (status && status != DEAD_OBJECT && !env->ExceptionCheck()) { 278 String8 message; 279 message.appendFormat("Failed to consume batched input event. status=%d", status); 280 jniThrowRuntimeException(env, message.string()); 281 } 282} 283 284 285static JNINativeMethod gMethods[] = { 286 /* name, signature, funcPtr */ 287 { "nativeInit", 288 "(Landroid/view/InputEventReceiver;Landroid/view/InputChannel;Landroid/os/MessageQueue;)I", 289 (void*)nativeInit }, 290 { "nativeDispose", "(I)V", 291 (void*)nativeDispose }, 292 { "nativeFinishInputEvent", "(IIZ)V", 293 (void*)nativeFinishInputEvent }, 294 { "nativeConsumeBatchedInputEvents", "(I)V", 295 (void*)nativeConsumeBatchedInputEvents }, 296}; 297 298#define FIND_CLASS(var, className) \ 299 var = env->FindClass(className); \ 300 LOG_FATAL_IF(! var, "Unable to find class " className); \ 301 var = jclass(env->NewGlobalRef(var)); 302 303#define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \ 304 var = env->GetMethodID(clazz, methodName, methodDescriptor); \ 305 LOG_FATAL_IF(! var, "Unable to find method " methodName); 306 307int register_android_view_InputEventReceiver(JNIEnv* env) { 308 int res = jniRegisterNativeMethods(env, "android/view/InputEventReceiver", 309 gMethods, NELEM(gMethods)); 310 LOG_FATAL_IF(res < 0, "Unable to register native methods."); 311 312 FIND_CLASS(gInputEventReceiverClassInfo.clazz, "android/view/InputEventReceiver"); 313 314 GET_METHOD_ID(gInputEventReceiverClassInfo.dispatchInputEvent, 315 gInputEventReceiverClassInfo.clazz, 316 "dispatchInputEvent", "(ILandroid/view/InputEvent;)V"); 317 GET_METHOD_ID(gInputEventReceiverClassInfo.dispatchBatchedInputEventPending, 318 gInputEventReceiverClassInfo.clazz, 319 "dispatchBatchedInputEventPending", "()V"); 320 return 0; 321} 322 323} // namespace android 324