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