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