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 37 namespace android { 38 39 static struct { 40 jclass clazz; 41 42 jmethodID dispatchInputEvent; 43 jmethodID dispatchBatchedInputEventPending; 44 } gInputEventReceiverClassInfo; 45 46 47 class NativeInputEventReceiver : public LooperCallback { 48 public: 49 NativeInputEventReceiver(JNIEnv* env, 50 jobject receiverObj, const sp<InputChannel>& inputChannel, 51 const sp<MessageQueue>& messageQueue); 52 53 status_t initialize(); 54 void dispose(); 55 status_t finishInputEvent(uint32_t seq, bool handled); 56 status_t consumeEvents(JNIEnv* env, bool consumeBatches, nsecs_t frameTime); 57 58 protected: 59 virtual ~NativeInputEventReceiver(); 60 61 private: 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 virtual int handleEvent(int receiveFd, int events, void* data); 73 }; 74 75 76 NativeInputEventReceiver::NativeInputEventReceiver(JNIEnv* env, 77 jobject receiverObj, const sp<InputChannel>& inputChannel, 78 const sp<MessageQueue>& messageQueue) : 79 mReceiverObjGlobal(env->NewGlobalRef(receiverObj)), 80 mInputConsumer(inputChannel), mMessageQueue(messageQueue), 81 mBatchedInputEventPending(false) { 82 #if DEBUG_DISPATCH_CYCLE 83 ALOGD("channel '%s' ~ Initializing input event receiver.", getInputChannelName()); 84 #endif 85 } 86 87 NativeInputEventReceiver::~NativeInputEventReceiver() { 88 JNIEnv* env = AndroidRuntime::getJNIEnv(); 89 env->DeleteGlobalRef(mReceiverObjGlobal); 90 } 91 92 status_t NativeInputEventReceiver::initialize() { 93 int receiveFd = mInputConsumer.getChannel()->getFd(); 94 mMessageQueue->getLooper()->addFd(receiveFd, 0, ALOOPER_EVENT_INPUT, this, NULL); 95 return OK; 96 } 97 98 void NativeInputEventReceiver::dispose() { 99 #if DEBUG_DISPATCH_CYCLE 100 ALOGD("channel '%s' ~ Disposing input event receiver.", getInputChannelName()); 101 #endif 102 103 mMessageQueue->getLooper()->removeFd(mInputConsumer.getChannel()->getFd()); 104 } 105 106 status_t NativeInputEventReceiver::finishInputEvent(uint32_t seq, bool handled) { 107 #if DEBUG_DISPATCH_CYCLE 108 ALOGD("channel '%s' ~ Finished input event.", getInputChannelName()); 109 #endif 110 111 status_t status = mInputConsumer.sendFinishedSignal(seq, handled); 112 if (status) { 113 ALOGW("Failed to send finished signal on channel '%s'. status=%d", 114 getInputChannelName(), status); 115 } 116 return status; 117 } 118 119 int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) { 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", 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", getInputChannelName(), events); 129 return 1; 130 } 131 132 JNIEnv* env = AndroidRuntime::getJNIEnv(); 133 status_t status = consumeEvents(env, false /*consumeBatches*/, -1); 134 mMessageQueue->raiseAndClearException(env, "handleReceiveCallback"); 135 return status == OK || status == NO_MEMORY ? 1 : 0; 136 } 137 138 status_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 227 static 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 256 static void nativeDispose(JNIEnv* env, jclass clazz, jint receiverPtr) { 257 sp<NativeInputEventReceiver> receiver = 258 reinterpret_cast<NativeInputEventReceiver*>(receiverPtr); 259 receiver->dispose(); 260 receiver->decStrong(gInputEventReceiverClassInfo.clazz); // drop reference held by the object 261 } 262 263 static void nativeFinishInputEvent(JNIEnv* env, jclass clazz, jint receiverPtr, 264 jint seq, jboolean handled) { 265 sp<NativeInputEventReceiver> receiver = 266 reinterpret_cast<NativeInputEventReceiver*>(receiverPtr); 267 status_t status = receiver->finishInputEvent(seq, handled); 268 if (status && status != DEAD_OBJECT) { 269 String8 message; 270 message.appendFormat("Failed to finish input event. status=%d", status); 271 jniThrowRuntimeException(env, message.string()); 272 } 273 } 274 275 static void nativeConsumeBatchedInputEvents(JNIEnv* env, jclass clazz, jint receiverPtr, 276 jlong frameTimeNanos) { 277 sp<NativeInputEventReceiver> receiver = 278 reinterpret_cast<NativeInputEventReceiver*>(receiverPtr); 279 status_t status = receiver->consumeEvents(env, true /*consumeBatches*/, frameTimeNanos); 280 if (status && status != DEAD_OBJECT && !env->ExceptionCheck()) { 281 String8 message; 282 message.appendFormat("Failed to consume batched input event. status=%d", status); 283 jniThrowRuntimeException(env, message.string()); 284 } 285 } 286 287 288 static JNINativeMethod gMethods[] = { 289 /* name, signature, funcPtr */ 290 { "nativeInit", 291 "(Landroid/view/InputEventReceiver;Landroid/view/InputChannel;Landroid/os/MessageQueue;)I", 292 (void*)nativeInit }, 293 { "nativeDispose", "(I)V", 294 (void*)nativeDispose }, 295 { "nativeFinishInputEvent", "(IIZ)V", 296 (void*)nativeFinishInputEvent }, 297 { "nativeConsumeBatchedInputEvents", "(IJ)V", 298 (void*)nativeConsumeBatchedInputEvents }, 299 }; 300 301 #define FIND_CLASS(var, className) \ 302 var = env->FindClass(className); \ 303 LOG_FATAL_IF(! var, "Unable to find class " className); \ 304 var = jclass(env->NewGlobalRef(var)); 305 306 #define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \ 307 var = env->GetMethodID(clazz, methodName, methodDescriptor); \ 308 LOG_FATAL_IF(! var, "Unable to find method " methodName); 309 310 int register_android_view_InputEventReceiver(JNIEnv* env) { 311 int res = jniRegisterNativeMethods(env, "android/view/InputEventReceiver", 312 gMethods, NELEM(gMethods)); 313 LOG_FATAL_IF(res < 0, "Unable to register native methods."); 314 315 FIND_CLASS(gInputEventReceiverClassInfo.clazz, "android/view/InputEventReceiver"); 316 317 GET_METHOD_ID(gInputEventReceiverClassInfo.dispatchInputEvent, 318 gInputEventReceiverClassInfo.clazz, 319 "dispatchInputEvent", "(ILandroid/view/InputEvent;)V"); 320 GET_METHOD_ID(gInputEventReceiverClassInfo.dispatchBatchedInputEventPending, 321 gInputEventReceiverClassInfo.clazz, 322 "dispatchBatchedInputEventPending", "()V"); 323 return 0; 324 } 325 326 } // namespace android 327