android_view_InputEventReceiver.cpp revision 9806a2307f3e47e847ad5e62e302eab36d3eb3e4
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 <ui/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<Looper>& looper); 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<Looper> mLooper; 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, const sp<Looper>& looper) : 76 mReceiverObjGlobal(env->NewGlobalRef(receiverObj)), 77 mInputConsumer(inputChannel), mLooper(looper), 78 mBatchedInputEventPending(false) { 79#if DEBUG_DISPATCH_CYCLE 80 ALOGD("channel '%s' ~ Initializing input event receiver.", getInputChannelName()); 81#endif 82} 83 84NativeInputEventReceiver::~NativeInputEventReceiver() { 85#if DEBUG_DISPATCH_CYCLE 86 ALOGD("channel '%s' ~ Disposing input event receiver.", getInputChannelName()); 87#endif 88 89 mLooper->removeFd(mInputConsumer.getChannel()->getFd()); 90 91 JNIEnv* env = AndroidRuntime::getJNIEnv(); 92 env->DeleteGlobalRef(mReceiverObjGlobal); 93} 94 95status_t NativeInputEventReceiver::initialize() { 96 int receiveFd = mInputConsumer.getChannel()->getFd(); 97 mLooper->addFd(receiveFd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this); 98 return OK; 99} 100 101status_t NativeInputEventReceiver::finishInputEvent(uint32_t seq, bool handled) { 102#if DEBUG_DISPATCH_CYCLE 103 ALOGD("channel '%s' ~ Finished input event.", getInputChannelName()); 104#endif 105 106 status_t status = mInputConsumer.sendFinishedSignal(seq, handled); 107 if (status) { 108 ALOGW("Failed to send finished signal on channel '%s'. status=%d", 109 getInputChannelName(), status); 110 } 111 return status; 112} 113 114int NativeInputEventReceiver::handleReceiveCallback(int receiveFd, int events, void* data) { 115 sp<NativeInputEventReceiver> r = static_cast<NativeInputEventReceiver*>(data); 116 117 if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) { 118 ALOGE("channel '%s' ~ Publisher closed input channel or an error occurred. " 119 "events=0x%x", r->getInputChannelName(), events); 120 return 0; // remove the callback 121 } 122 123 if (!(events & ALOOPER_EVENT_INPUT)) { 124 ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event. " 125 "events=0x%x", r->getInputChannelName(), events); 126 return 1; 127 } 128 129 status_t status = r->consumeEvents(false /*consumeBatches*/); 130 return status == OK || status == NO_MEMORY ? 1 : 0; 131} 132 133status_t NativeInputEventReceiver::consumeEvents(bool consumeBatches) { 134#if DEBUG_DISPATCH_CYCLE 135 ALOGD("channel '%s' ~ Consuming input events, consumeBatches=%s.", getInputChannelName(), 136 consumeBatches ? "true" : "false"); 137#endif 138 139 if (consumeBatches) { 140 mBatchedInputEventPending = false; 141 } 142 143 JNIEnv* env = AndroidRuntime::getJNIEnv(); 144 for (;;) { 145 uint32_t seq; 146 InputEvent* inputEvent; 147 status_t status = mInputConsumer.consume(&mInputEventFactory, 148 consumeBatches, &seq, &inputEvent); 149 if (status) { 150 if (status == WOULD_BLOCK) { 151 if (mInputConsumer.hasPendingBatch() && !mBatchedInputEventPending) { 152 // There is a pending batch. Come back later. 153 mBatchedInputEventPending = true; 154#if DEBUG_DISPATCH_CYCLE 155 ALOGD("channel '%s' ~ Dispatching batched input event pending notification.", 156 getInputChannelName()); 157#endif 158 env->CallVoidMethod(mReceiverObjGlobal, 159 gInputEventReceiverClassInfo.dispatchBatchedInputEventPending); 160 161 if (env->ExceptionCheck()) { 162 ALOGE("channel '%s' ~ An exception occurred while dispatching that " 163 "batched input events are pending.", getInputChannelName()); 164 LOGE_EX(env); 165 env->ExceptionClear(); 166 mBatchedInputEventPending = false; // try again later 167 } 168 } 169 return OK; 170 } 171 ALOGE("channel '%s' ~ Failed to consume input event. status=%d", 172 getInputChannelName(), status); 173 return status; 174 } 175 assert(inputEvent); 176 177 jobject inputEventObj; 178 switch (inputEvent->getType()) { 179 case AINPUT_EVENT_TYPE_KEY: 180#if DEBUG_DISPATCH_CYCLE 181 ALOGD("channel '%s' ~ Received key event.", getInputChannelName()); 182#endif 183 inputEventObj = android_view_KeyEvent_fromNative(env, 184 static_cast<KeyEvent*>(inputEvent)); 185 break; 186 187 case AINPUT_EVENT_TYPE_MOTION: 188#if DEBUG_DISPATCH_CYCLE 189 ALOGD("channel '%s' ~ Received motion event.", getInputChannelName()); 190#endif 191 inputEventObj = android_view_MotionEvent_obtainAsCopy(env, 192 static_cast<MotionEvent*>(inputEvent)); 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 return NO_MEMORY; 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 (env->ExceptionCheck()) { 215 ALOGE("channel '%s' ~ An exception occurred while dispatching an event.", 216 getInputChannelName()); 217 LOGE_EX(env); 218 env->ExceptionClear(); 219 220 mInputConsumer.sendFinishedSignal(seq, false); 221 return OK; 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<Looper> looper = android_os_MessageQueue_getLooper(env, messageQueueObj); 237 if (looper == NULL) { 238 jniThrowRuntimeException(env, "MessageQueue is not initialized."); 239 return 0; 240 } 241 242 sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env, 243 receiverObj, inputChannel, looper); 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 sp<NativeInputEventReceiver> receiver = 276 reinterpret_cast<NativeInputEventReceiver*>(receiverPtr); 277 status_t status = receiver->consumeEvents(true /*consumeBatches*/); 278 if (status && status != DEAD_OBJECT) { 279 String8 message; 280 message.appendFormat("Failed to consume batched input event. status=%d", status); 281 jniThrowRuntimeException(env, message.string()); 282 } 283} 284 285 286static JNINativeMethod gMethods[] = { 287 /* name, signature, funcPtr */ 288 { "nativeInit", 289 "(Landroid/view/InputEventReceiver;Landroid/view/InputChannel;Landroid/os/MessageQueue;)I", 290 (void*)nativeInit }, 291 { "nativeDispose", "(I)V", 292 (void*)nativeDispose }, 293 { "nativeFinishInputEvent", "(IIZ)V", 294 (void*)nativeFinishInputEvent }, 295 { "nativeConsumeBatchedInputEvents", "(I)V", 296 (void*)nativeConsumeBatchedInputEvents }, 297}; 298 299#define FIND_CLASS(var, className) \ 300 var = env->FindClass(className); \ 301 LOG_FATAL_IF(! var, "Unable to find class " className); \ 302 var = jclass(env->NewGlobalRef(var)); 303 304#define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \ 305 var = env->GetMethodID(clazz, methodName, methodDescriptor); \ 306 LOG_FATAL_IF(! var, "Unable to find method " methodName); 307 308int register_android_view_InputEventReceiver(JNIEnv* env) { 309 int res = jniRegisterNativeMethods(env, "android/view/InputEventReceiver", 310 gMethods, NELEM(gMethods)); 311 LOG_FATAL_IF(res < 0, "Unable to register native methods."); 312 313 FIND_CLASS(gInputEventReceiverClassInfo.clazz, "android/view/InputEventReceiver"); 314 315 GET_METHOD_ID(gInputEventReceiverClassInfo.dispatchInputEvent, 316 gInputEventReceiverClassInfo.clazz, 317 "dispatchInputEvent", "(ILandroid/view/InputEvent;)V"); 318 GET_METHOD_ID(gInputEventReceiverClassInfo.dispatchBatchedInputEventPending, 319 gInputEventReceiverClassInfo.clazz, 320 "dispatchBatchedInputEventPending", "()V"); 321 return 0; 322} 323 324} // namespace android 325