status_t NativeInputEventReceiver::finishInputEvent(uint32_t seq, bool handled) { if (kDebugDispatchCycle) { ALOGD("channel '%s' ~ Finished input event.", getInputChannelName()); } status_t status = mInputConsumer.sendFinishedSignal(seq, handled); if (status) { if (status == WOULD_BLOCK) { if (kDebugDispatchCycle) { ALOGD("channel '%s' ~ Could not send finished signal immediately. " "Enqueued for later.", getInputChannelName()); } Finish finish; finish.seq = seq; finish.handled = handled; mFinishQueue.add(finish); if (mFinishQueue.size() == 1) { setFdEvents(ALOOPER_EVENT_INPUT | ALOOPER_EVENT_OUTPUT); } return OK; } ALOGW("Failed to send finished signal on channel '%s'. status=%d", getInputChannelName(), status); } return status; }
status_t NativeInputEventSender::sendMotionEvent(uint32_t seq, const MotionEvent* event) { if (kDebugDispatchCycle) { ALOGD("channel '%s' ~ Sending motion event, seq=%u.", getInputChannelName(), seq); } uint32_t publishedSeq; for (size_t i = 0; i <= event->getHistorySize(); i++) { publishedSeq = mNextPublishedSeq++; status_t status = mInputPublisher.publishMotionEvent(publishedSeq, event->getDeviceId(), event->getSource(), event->getAction(), event->getActionButton(), event->getFlags(), event->getEdgeFlags(), event->getMetaState(), event->getButtonState(), event->getXOffset(), event->getYOffset(), event->getXPrecision(), event->getYPrecision(), event->getDownTime(), event->getHistoricalEventTime(i), event->getPointerCount(), event->getPointerProperties(), event->getHistoricalRawPointerCoords(0, i)); if (status) { ALOGW("Failed to send motion event sample on channel '%s'. status=%d", getInputChannelName(), status); return status; } } mPublishedSeqMap.add(publishedSeq, seq); return OK; }
int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) { if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) { // This error typically occurs when the publisher has closed the input channel // as part of removing a window or finishing an IME session, in which case // the consumer will soon be disposed as well. if (kDebugDispatchCycle) { ALOGD("channel '%s' ~ Publisher closed input channel or an error occurred. " "events=0x%x", getInputChannelName(), events); } return 0; // remove the callback } if (events & ALOOPER_EVENT_INPUT) { JNIEnv* env = AndroidRuntime::getJNIEnv(); status_t status = consumeEvents(env, false /*consumeBatches*/, -1, NULL); mMessageQueue->raiseAndClearException(env, "handleReceiveCallback"); return status == OK || status == NO_MEMORY ? 1 : 0; } if (events & ALOOPER_EVENT_OUTPUT) { for (size_t i = 0; i < mFinishQueue.size(); i++) { const Finish& finish = mFinishQueue.itemAt(i); status_t status = mInputConsumer.sendFinishedSignal(finish.seq, finish.handled); if (status) { mFinishQueue.removeItemsAt(0, i); if (status == WOULD_BLOCK) { if (kDebugDispatchCycle) { ALOGD("channel '%s' ~ Sent %zu queued finish events; %zu left.", getInputChannelName(), i, mFinishQueue.size()); } return 1; // keep the callback, try again later } ALOGW("Failed to send finished signal on channel '%s'. status=%d", getInputChannelName(), status); if (status != DEAD_OBJECT) { JNIEnv* env = AndroidRuntime::getJNIEnv(); String8 message; message.appendFormat("Failed to finish input event. status=%d", status); jniThrowRuntimeException(env, message.string()); mMessageQueue->raiseAndClearException(env, "finishInputEvent"); } return 0; // remove the callback } } if (kDebugDispatchCycle) { ALOGD("channel '%s' ~ Sent %zu queued finish events; none left.", getInputChannelName(), mFinishQueue.size()); } mFinishQueue.clear(); setFdEvents(ALOOPER_EVENT_INPUT); return 1; } ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event. " "events=0x%x", getInputChannelName(), events); return 1; }
status_t NativeInputEventSender::receiveFinishedSignals(JNIEnv* env) { if (kDebugDispatchCycle) { ALOGD("channel '%s' ~ Receiving finished signals.", getInputChannelName()); } ScopedLocalRef<jobject> senderObj(env, NULL); bool skipCallbacks = false; for (;;) { uint32_t publishedSeq; bool handled; status_t status = mInputPublisher.receiveFinishedSignal(&publishedSeq, &handled); if (status) { if (status == WOULD_BLOCK) { return OK; } ALOGE("channel '%s' ~ Failed to consume finished signals. status=%d", getInputChannelName(), status); return status; } ssize_t index = mPublishedSeqMap.indexOfKey(publishedSeq); if (index >= 0) { uint32_t seq = mPublishedSeqMap.valueAt(index); mPublishedSeqMap.removeItemsAt(index); if (kDebugDispatchCycle) { ALOGD("channel '%s' ~ Received finished signal, seq=%u, handled=%s, " "pendingEvents=%zu.", getInputChannelName(), seq, handled ? "true" : "false", mPublishedSeqMap.size()); } if (!skipCallbacks) { if (!senderObj.get()) { senderObj.reset(jniGetReferent(env, mSenderWeakGlobal)); if (!senderObj.get()) { ALOGW("channel '%s' ~ Sender object was finalized " "without being disposed.", getInputChannelName()); return DEAD_OBJECT; } } env->CallVoidMethod(senderObj.get(), gInputEventSenderClassInfo.dispatchInputEventFinished, jint(seq), jboolean(handled)); if (env->ExceptionCheck()) { ALOGE("Exception dispatching finished signal."); skipCallbacks = true; } } } } }
void NativeInputEventReceiver::dispose() { if (kDebugDispatchCycle) { ALOGD("channel '%s' ~ Disposing input event receiver.", getInputChannelName()); } setFdEvents(0); }
void NativeInputEventSender::dispose() { if (kDebugDispatchCycle) { ALOGD("channel '%s' ~ Disposing input event sender.", getInputChannelName()); } mMessageQueue->getLooper()->removeFd(mInputPublisher.getChannel()->getFd()); }
status_t NativeInputEventSender::sendKeyEvent(uint32_t seq, const KeyEvent* event) { if (kDebugDispatchCycle) { ALOGD("channel '%s' ~ Sending key event, seq=%u.", getInputChannelName(), seq); } uint32_t publishedSeq = mNextPublishedSeq++; status_t status = mInputPublisher.publishKeyEvent(publishedSeq, event->getDeviceId(), event->getSource(), event->getAction(), event->getFlags(), event->getKeyCode(), event->getScanCode(), event->getMetaState(), event->getRepeatCount(), event->getDownTime(), event->getEventTime()); if (status) { ALOGW("Failed to send key event on channel '%s'. status=%d", getInputChannelName(), status); return status; } mPublishedSeqMap.add(publishedSeq, seq); return OK; }
NativeInputEventReceiver::NativeInputEventReceiver(JNIEnv* env, jobject receiverWeak, const sp<InputChannel>& inputChannel, const sp<MessageQueue>& messageQueue) : mReceiverWeakGlobal(env->NewGlobalRef(receiverWeak)), mInputConsumer(inputChannel), mMessageQueue(messageQueue), mBatchedInputEventPending(false), mFdEvents(0) { if (kDebugDispatchCycle) { ALOGD("channel '%s' ~ Initializing input event receiver.", getInputChannelName()); } }
NativeInputEventSender::NativeInputEventSender(JNIEnv* env, jobject senderWeak, const sp<InputChannel>& inputChannel, const sp<MessageQueue>& messageQueue) : mSenderWeakGlobal(env->NewGlobalRef(senderWeak)), mInputPublisher(inputChannel), mMessageQueue(messageQueue), mNextPublishedSeq(1) { if (kDebugDispatchCycle) { ALOGD("channel '%s' ~ Initializing input event sender.", getInputChannelName()); } }
int NativeInputEventSender::handleEvent(int receiveFd, int events, void* data) { if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) { // This error typically occurs when the consumer has closed the input channel // as part of finishing an IME session, in which case the publisher will // soon be disposed as well. if (kDebugDispatchCycle) { ALOGD("channel '%s' ~ Consumer closed input channel or an error occurred. " "events=0x%x", getInputChannelName(), events); } return 0; // remove the callback } if (!(events & ALOOPER_EVENT_INPUT)) { ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event. " "events=0x%x", getInputChannelName(), events); return 1; } JNIEnv* env = AndroidRuntime::getJNIEnv(); status_t status = receiveFinishedSignals(env); mMessageQueue->raiseAndClearException(env, "handleReceiveCallback"); return status == OK || status == NO_MEMORY ? 1 : 0; }
status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env, bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) { if (kDebugDispatchCycle) { ALOGD("channel '%s' ~ Consuming input events, consumeBatches=%s, frameTime=%lld.", getInputChannelName(), consumeBatches ? "true" : "false", (long long)frameTime); } if (consumeBatches) { mBatchedInputEventPending = false; } if (outConsumedBatch) { *outConsumedBatch = false; } ScopedLocalRef<jobject> receiverObj(env, NULL); bool skipCallbacks = false; for (;;) { uint32_t seq; InputEvent* inputEvent; status_t status = mInputConsumer.consume(&mInputEventFactory, consumeBatches, frameTime, &seq, &inputEvent); if (status) { if (status == WOULD_BLOCK) { if (!skipCallbacks && !mBatchedInputEventPending && mInputConsumer.hasPendingBatch()) { // There is a pending batch. Come back later. if (!receiverObj.get()) { receiverObj.reset(jniGetReferent(env, mReceiverWeakGlobal)); if (!receiverObj.get()) { ALOGW("channel '%s' ~ Receiver object was finalized " "without being disposed.", getInputChannelName()); return DEAD_OBJECT; } } mBatchedInputEventPending = true; if (kDebugDispatchCycle) { ALOGD("channel '%s' ~ Dispatching batched input event pending notification.", getInputChannelName()); } env->CallVoidMethod(receiverObj.get(), gInputEventReceiverClassInfo.dispatchBatchedInputEventPending); if (env->ExceptionCheck()) { ALOGE("Exception dispatching batched input events."); mBatchedInputEventPending = false; // try again later } } return OK; } ALOGE("channel '%s' ~ Failed to consume input event. status=%d", getInputChannelName(), status); return status; } assert(inputEvent); if (!skipCallbacks) { if (!receiverObj.get()) { receiverObj.reset(jniGetReferent(env, mReceiverWeakGlobal)); if (!receiverObj.get()) { ALOGW("channel '%s' ~ Receiver object was finalized " "without being disposed.", getInputChannelName()); return DEAD_OBJECT; } } jobject inputEventObj; switch (inputEvent->getType()) { case AINPUT_EVENT_TYPE_KEY: if (kDebugDispatchCycle) { ALOGD("channel '%s' ~ Received key event.", getInputChannelName()); } inputEventObj = android_view_KeyEvent_fromNative(env, static_cast<KeyEvent*>(inputEvent)); break; case AINPUT_EVENT_TYPE_MOTION: { if (kDebugDispatchCycle) { ALOGD("channel '%s' ~ Received motion event.", getInputChannelName()); } MotionEvent* motionEvent = static_cast<MotionEvent*>(inputEvent); if ((motionEvent->getAction() & AMOTION_EVENT_ACTION_MOVE) && outConsumedBatch) { *outConsumedBatch = true; } inputEventObj = android_view_MotionEvent_obtainAsCopy(env, motionEvent); break; } default: assert(false); // InputConsumer should prevent this from ever happening inputEventObj = NULL; } if (inputEventObj) { if (kDebugDispatchCycle) { ALOGD("channel '%s' ~ Dispatching input event.", getInputChannelName()); } env->CallVoidMethod(receiverObj.get(), gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj); if (env->ExceptionCheck()) { ALOGE("Exception dispatching input event."); skipCallbacks = true; } env->DeleteLocalRef(inputEventObj); } else { ALOGW("channel '%s' ~ Failed to obtain event object.", getInputChannelName()); skipCallbacks = true; } } if (skipCallbacks) { mInputConsumer.sendFinishedSignal(seq, false); } } }