android_view_InputEventReceiver.cpp revision 771526c88f5cc4b56a41cb12aa06a28d377a07d5
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, nsecs_t frameTime); 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*/, -1); 134 r->mMessageQueue->raiseAndClearException(env, "handleReceiveCallback"); 135 return status == OK || status == NO_MEMORY ? 1 : 0; 136} 137 138status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env, 139 bool consumeBatches, nsecs_t frameTime) { 140#if DEBUG_DISPATCH_CYCLE 141 ALOGD("channel '%s' ~ Consuming input events, consumeBatches=%s, frameTime=%lld.", 142 getInputChannelName(), consumeBatches ? "true" : "false", frameTime); 143#endif 144 145 if (consumeBatches) { 146 mBatchedInputEventPending = false; 147 } 148 149 bool skipCallbacks = false; 150 for (;;) { 151 uint32_t seq; 152 InputEvent* inputEvent; 153 status_t status = mInputConsumer.consume(&mInputEventFactory, 154 consumeBatches, frameTime, &seq, &inputEvent); 155 if (status) { 156 if (status == WOULD_BLOCK) { 157 if (!skipCallbacks && !mBatchedInputEventPending 158 && mInputConsumer.hasPendingBatch()) { 159 // There is a pending batch. Come back later. 160 mBatchedInputEventPending = true; 161#if DEBUG_DISPATCH_CYCLE 162 ALOGD("channel '%s' ~ Dispatching batched input event pending notification.", 163 getInputChannelName()); 164#endif 165 env->CallVoidMethod(mReceiverObjGlobal, 166 gInputEventReceiverClassInfo.dispatchBatchedInputEventPending); 167 if (env->ExceptionCheck()) { 168 ALOGE("Exception dispatching batched input events."); 169 mBatchedInputEventPending = false; // try again later 170 } 171 } 172 return OK; 173 } 174 ALOGE("channel '%s' ~ Failed to consume input event. status=%d", 175 getInputChannelName(), status); 176 return status; 177 } 178 assert(inputEvent); 179 180 if (!skipCallbacks) { 181 jobject inputEventObj; 182 switch (inputEvent->getType()) { 183 case AINPUT_EVENT_TYPE_KEY: 184#if DEBUG_DISPATCH_CYCLE 185 ALOGD("channel '%s' ~ Received key event.", getInputChannelName()); 186#endif 187 inputEventObj = android_view_KeyEvent_fromNative(env, 188 static_cast<KeyEvent*>(inputEvent)); 189 break; 190 191 case AINPUT_EVENT_TYPE_MOTION: 192#if DEBUG_DISPATCH_CYCLE 193 ALOGD("channel '%s' ~ Received motion event.", getInputChannelName()); 194#endif 195 inputEventObj = android_view_MotionEvent_obtainAsCopy(env, 196 static_cast<MotionEvent*>(inputEvent)); 197 break; 198 199 default: 200 assert(false); // InputConsumer should prevent this from ever happening 201 inputEventObj = NULL; 202 } 203 204 if (inputEventObj) { 205#if DEBUG_DISPATCH_CYCLE 206 ALOGD("channel '%s' ~ Dispatching input event.", getInputChannelName()); 207#endif 208 env->CallVoidMethod(mReceiverObjGlobal, 209 gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj); 210 if (env->ExceptionCheck()) { 211 ALOGE("Exception dispatching input event."); 212 skipCallbacks = true; 213 } 214 } else { 215 ALOGW("channel '%s' ~ Failed to obtain event object.", getInputChannelName()); 216 skipCallbacks = true; 217 } 218 } 219 220 if (skipCallbacks) { 221 mInputConsumer.sendFinishedSignal(seq, false); 222 } 223 } 224} 225 226 227static jint nativeInit(JNIEnv* env, jclass clazz, jobject receiverObj, 228 jobject inputChannelObj, jobject messageQueueObj) { 229 sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env, 230 inputChannelObj); 231 if (inputChannel == NULL) { 232 jniThrowRuntimeException(env, "InputChannel is not initialized."); 233 return 0; 234 } 235 236 sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj); 237 if (messageQueue == NULL) { 238 jniThrowRuntimeException(env, "MessageQueue is not initialized."); 239 return 0; 240 } 241 242 sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env, 243 receiverObj, inputChannel, messageQueue); 244 status_t status = receiver->initialize(); 245 if (status) { 246 String8 message; 247 message.appendFormat("Failed to initialize input event receiver. status=%d", status); 248 jniThrowRuntimeException(env, message.string()); 249 return 0; 250 } 251 252 receiver->incStrong(gInputEventReceiverClassInfo.clazz); // retain a reference for the object 253 return reinterpret_cast<jint>(receiver.get()); 254} 255 256static void nativeDispose(JNIEnv* env, jclass clazz, jint receiverPtr) { 257 sp<NativeInputEventReceiver> receiver = 258 reinterpret_cast<NativeInputEventReceiver*>(receiverPtr); 259 receiver->decStrong(gInputEventReceiverClassInfo.clazz); // drop reference held by the object 260} 261 262static void nativeFinishInputEvent(JNIEnv* env, jclass clazz, jint receiverPtr, 263 jint seq, jboolean handled) { 264 sp<NativeInputEventReceiver> receiver = 265 reinterpret_cast<NativeInputEventReceiver*>(receiverPtr); 266 status_t status = receiver->finishInputEvent(seq, handled); 267 if (status && status != DEAD_OBJECT) { 268 String8 message; 269 message.appendFormat("Failed to finish input event. status=%d", status); 270 jniThrowRuntimeException(env, message.string()); 271 } 272} 273 274static void nativeConsumeBatchedInputEvents(JNIEnv* env, jclass clazz, jint receiverPtr, 275 jlong frameTimeNanos) { 276 sp<NativeInputEventReceiver> receiver = 277 reinterpret_cast<NativeInputEventReceiver*>(receiverPtr); 278 status_t status = receiver->consumeEvents(env, true /*consumeBatches*/, frameTimeNanos); 279 if (status && status != DEAD_OBJECT && !env->ExceptionCheck()) { 280 String8 message; 281 message.appendFormat("Failed to consume batched input event. status=%d", status); 282 jniThrowRuntimeException(env, message.string()); 283 } 284} 285 286 287static JNINativeMethod gMethods[] = { 288 /* name, signature, funcPtr */ 289 { "nativeInit", 290 "(Landroid/view/InputEventReceiver;Landroid/view/InputChannel;Landroid/os/MessageQueue;)I", 291 (void*)nativeInit }, 292 { "nativeDispose", "(I)V", 293 (void*)nativeDispose }, 294 { "nativeFinishInputEvent", "(IIZ)V", 295 (void*)nativeFinishInputEvent }, 296 { "nativeConsumeBatchedInputEvents", "(IJ)V", 297 (void*)nativeConsumeBatchedInputEvents }, 298}; 299 300#define FIND_CLASS(var, className) \ 301 var = env->FindClass(className); \ 302 LOG_FATAL_IF(! var, "Unable to find class " className); \ 303 var = jclass(env->NewGlobalRef(var)); 304 305#define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \ 306 var = env->GetMethodID(clazz, methodName, methodDescriptor); \ 307 LOG_FATAL_IF(! var, "Unable to find method " methodName); 308 309int register_android_view_InputEventReceiver(JNIEnv* env) { 310 int res = jniRegisterNativeMethods(env, "android/view/InputEventReceiver", 311 gMethods, NELEM(gMethods)); 312 LOG_FATAL_IF(res < 0, "Unable to register native methods."); 313 314 FIND_CLASS(gInputEventReceiverClassInfo.clazz, "android/view/InputEventReceiver"); 315 316 GET_METHOD_ID(gInputEventReceiverClassInfo.dispatchInputEvent, 317 gInputEventReceiverClassInfo.clazz, 318 "dispatchInputEvent", "(ILandroid/view/InputEvent;)V"); 319 GET_METHOD_ID(gInputEventReceiverClassInfo.dispatchBatchedInputEventPending, 320 gInputEventReceiverClassInfo.clazz, 321 "dispatchBatchedInputEventPending", "()V"); 322 return 0; 323} 324 325} // namespace android 326