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