android_view_InputEventReceiver.cpp revision cbee6d6ede0499fb4a2c00bfc00d5db8d9ed5139
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} gInputEventReceiverClassInfo; 44 45 46class NativeInputEventReceiver : public RefBase { 47public: 48 NativeInputEventReceiver(JNIEnv* env, 49 jobject receiverObj, const sp<InputChannel>& inputChannel, 50 const sp<Looper>& looper); 51 52 status_t initialize(); 53 status_t finishInputEvent(bool handled); 54 static int handleReceiveCallback(int receiveFd, int events, void* data); 55 56protected: 57 virtual ~NativeInputEventReceiver(); 58 59private: 60 jobject mReceiverObjGlobal; 61 InputConsumer mInputConsumer; 62 sp<Looper> mLooper; 63 bool mEventInProgress; 64 PreallocatedInputEventFactory mInputEventFactory; 65 66 const char* getInputChannelName() { 67 return mInputConsumer.getChannel()->getName().string(); 68 } 69}; 70 71 72NativeInputEventReceiver::NativeInputEventReceiver(JNIEnv* env, 73 jobject receiverObj, const sp<InputChannel>& inputChannel, const sp<Looper>& looper) : 74 mReceiverObjGlobal(env->NewGlobalRef(receiverObj)), 75 mInputConsumer(inputChannel), mLooper(looper), mEventInProgress(false) { 76#if DEBUG_DISPATCH_CYCLE 77 ALOGD("channel '%s' ~ Initializing input event receiver.", getInputChannelName()); 78#endif 79} 80 81NativeInputEventReceiver::~NativeInputEventReceiver() { 82#if DEBUG_DISPATCH_CYCLE 83 ALOGD("channel '%s' ~ Disposing input event receiver.", getInputChannelName()); 84#endif 85 86 mLooper->removeFd(mInputConsumer.getChannel()->getFd()); 87 if (mEventInProgress) { 88 mInputConsumer.sendFinishedSignal(false); // ignoring result 89 } 90 91 JNIEnv* env = AndroidRuntime::getJNIEnv(); 92 env->DeleteGlobalRef(mReceiverObjGlobal); 93} 94 95status_t NativeInputEventReceiver::initialize() { 96 int32_t receiveFd = mInputConsumer.getChannel()->getFd(); 97 mLooper->addFd(receiveFd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this); 98 return OK; 99} 100 101status_t NativeInputEventReceiver::finishInputEvent(bool handled) { 102 if (mEventInProgress) { 103#if DEBUG_DISPATCH_CYCLE 104 ALOGD("channel '%s' ~ Finished input event.", getInputChannelName()); 105#endif 106 mEventInProgress = false; 107 108 status_t status = mInputConsumer.sendFinishedSignal(handled); 109 if (status) { 110 ALOGW("Failed to send finished signal on channel '%s'. status=%d", 111 getInputChannelName(), status); 112 } 113 return status; 114 } else { 115 ALOGW("Ignoring attempt to finish input event while no event is in progress."); 116 return OK; 117 } 118} 119 120int NativeInputEventReceiver::handleReceiveCallback(int receiveFd, int events, void* data) { 121 sp<NativeInputEventReceiver> r = static_cast<NativeInputEventReceiver*>(data); 122 123 if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) { 124 ALOGE("channel '%s' ~ Publisher closed input channel or an error occurred. " 125 "events=0x%x", r->getInputChannelName(), events); 126 return 0; // remove the callback 127 } 128 129 if (!(events & ALOOPER_EVENT_INPUT)) { 130 ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event. " 131 "events=0x%x", r->getInputChannelName(), events); 132 return 1; 133 } 134 135 if (r->mEventInProgress) { 136 ALOGW("channel '%s' ~ Publisher sent spurious dispatch signal.", 137 r->getInputChannelName()); 138 return 1; 139 } 140 141 InputEvent* inputEvent; 142 status_t status = r->mInputConsumer.consume(&r->mInputEventFactory, &inputEvent); 143 if (status) { 144 ALOGW("channel '%s' ~ Failed to consume input event. status=%d", 145 r->getInputChannelName(), status); 146 r->mInputConsumer.sendFinishedSignal(false); 147 return 1; 148 } 149 150 JNIEnv* env = AndroidRuntime::getJNIEnv(); 151 jobject inputEventObj; 152 switch (inputEvent->getType()) { 153 case AINPUT_EVENT_TYPE_KEY: 154#if DEBUG_DISPATCH_CYCLE 155 ALOGD("channel '%s' ~ Received key event.", 156 r->getInputChannelName()); 157#endif 158 inputEventObj = android_view_KeyEvent_fromNative(env, 159 static_cast<KeyEvent*>(inputEvent)); 160 break; 161 162 case AINPUT_EVENT_TYPE_MOTION: 163#if DEBUG_DISPATCH_CYCLE 164 ALOGD("channel '%s' ~ Received motion event.", 165 r->getInputChannelName()); 166#endif 167 inputEventObj = android_view_MotionEvent_obtainAsCopy(env, 168 static_cast<MotionEvent*>(inputEvent)); 169 break; 170 171 default: 172 assert(false); // InputConsumer should prevent this from ever happening 173 inputEventObj = NULL; 174 } 175 176 if (!inputEventObj) { 177 ALOGW("channel '%s' ~ Failed to obtain event object.", 178 r->getInputChannelName()); 179 r->mInputConsumer.sendFinishedSignal(false); 180 return 1; 181 } 182 183 r->mEventInProgress = true; 184 185#if DEBUG_DISPATCH_CYCLE 186 ALOGD("channel '%s' ~ Invoking input handler.", r->getInputChannelName()); 187#endif 188 env->CallVoidMethod(r->mReceiverObjGlobal, 189 gInputEventReceiverClassInfo.dispatchInputEvent, inputEventObj); 190#if DEBUG_DISPATCH_CYCLE 191 ALOGD("channel '%s' ~ Returned from input handler.", r->getInputChannelName()); 192#endif 193 194 if (env->ExceptionCheck()) { 195 ALOGE("channel '%s' ~ An exception occurred while dispatching an event.", 196 r->getInputChannelName()); 197 LOGE_EX(env); 198 env->ExceptionClear(); 199 200 if (r->mEventInProgress) { 201 r->mInputConsumer.sendFinishedSignal(false); 202 r->mEventInProgress = false; 203 } 204 } 205 206 env->DeleteLocalRef(inputEventObj); 207 return 1; 208} 209 210 211static jint nativeInit(JNIEnv* env, jclass clazz, jobject receiverObj, 212 jobject inputChannelObj, jobject messageQueueObj) { 213 sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env, 214 inputChannelObj); 215 if (inputChannel == NULL) { 216 jniThrowRuntimeException(env, "InputChannel is not initialized."); 217 return 0; 218 } 219 220 sp<Looper> looper = android_os_MessageQueue_getLooper(env, messageQueueObj); 221 if (looper == NULL) { 222 jniThrowRuntimeException(env, "MessageQueue is not initialized."); 223 return 0; 224 } 225 226 sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env, 227 receiverObj, inputChannel, looper); 228 status_t status = receiver->initialize(); 229 if (status) { 230 String8 message; 231 message.appendFormat("Failed to initialize input event receiver. status=%d", status); 232 jniThrowRuntimeException(env, message.string()); 233 return 0; 234 } 235 236 receiver->incStrong(gInputEventReceiverClassInfo.clazz); // retain a reference for the object 237 return reinterpret_cast<jint>(receiver.get()); 238} 239 240static void nativeDispose(JNIEnv* env, jclass clazz, jint receiverPtr) { 241 sp<NativeInputEventReceiver> receiver = 242 reinterpret_cast<NativeInputEventReceiver*>(receiverPtr); 243 receiver->decStrong(gInputEventReceiverClassInfo.clazz); // drop reference held by the object 244} 245 246static void nativeFinishInputEvent(JNIEnv* env, jclass clazz, jint receiverPtr, jboolean handled) { 247 sp<NativeInputEventReceiver> receiver = 248 reinterpret_cast<NativeInputEventReceiver*>(receiverPtr); 249 status_t status = receiver->finishInputEvent(handled); 250 if (status) { 251 String8 message; 252 message.appendFormat("Failed to finish input event. status=%d", status); 253 jniThrowRuntimeException(env, message.string()); 254 } 255} 256 257 258static JNINativeMethod gMethods[] = { 259 /* name, signature, funcPtr */ 260 { "nativeInit", 261 "(Landroid/view/InputEventReceiver;Landroid/view/InputChannel;Landroid/os/MessageQueue;)I", 262 (void*)nativeInit }, 263 { "nativeDispose", 264 "(I)V", 265 (void*)nativeDispose }, 266 { "nativeFinishInputEvent", "(IZ)V", 267 (void*)nativeFinishInputEvent } 268}; 269 270#define FIND_CLASS(var, className) \ 271 var = env->FindClass(className); \ 272 LOG_FATAL_IF(! var, "Unable to find class " className); \ 273 var = jclass(env->NewGlobalRef(var)); 274 275#define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \ 276 var = env->GetMethodID(clazz, methodName, methodDescriptor); \ 277 LOG_FATAL_IF(! var, "Unable to find method " methodName); 278 279int register_android_view_InputEventReceiver(JNIEnv* env) { 280 int res = jniRegisterNativeMethods(env, "android/view/InputEventReceiver", 281 gMethods, NELEM(gMethods)); 282 LOG_FATAL_IF(res < 0, "Unable to register native methods."); 283 284 FIND_CLASS(gInputEventReceiverClassInfo.clazz, "android/view/InputEventReceiver"); 285 286 GET_METHOD_ID(gInputEventReceiverClassInfo.dispatchInputEvent, 287 gInputEventReceiverClassInfo.clazz, 288 "dispatchInputEvent", "(Landroid/view/InputEvent;)V"); 289 return 0; 290} 291 292} // namespace android 293