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