/** * Waits for events to be available, with optional timeout in milliseconds. * Invokes callbacks for all file descriptors on which an event occurred. * * If the timeout is zero, returns immediately without blocking. * If the timeout is negative, waits indefinitely until an event appears. * * Returns POLL_WAKE if the poll was awoken using wake() before * the timeout expired and no callbacks were invoked and no other file * descriptors were ready. * * Returns POLL_CALLBACK if one or more callbacks were invoked. * * Returns POLL_TIMEOUT if there was no data before the given * timeout expired. * * Returns POLL_ERROR if an error occurred. * * Returns a value >= 0 containing an identifier if its file descriptor has data * and it has no callback function (requiring the caller here to handle it). * In this (and only this) case outFd, outEvents and outData will contain the poll * events and data associated with the fd, otherwise they will be set to NULL. * * This method does not return until it has finished invoking the appropriate callbacks * for all file descriptors that were signalled. */ int pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData);
int DisplayEventDispatcher::handleEvent(int, int events, void*) { if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) { ALOGE("Display event receiver pipe was closed or an error occurred. " "events=0x%x", events); return 0; // remove the callback } ...... dispatchVsync(vsyncTimestamp, vsyncDisplayId, vsyncCount, vsyncEventData); }
java:
1 2 3 4 5 6
// Called from native code. @SuppressWarnings("unused") @UnsupportedAppUsage private void dispatchVsync(long timestampNanos, long physicalDisplayId, int frame) { onVsync(timestampNanos, physicalDisplayId, frame); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
private final class FrameDisplayEventReceiver extends DisplayEventReceiver implements Runnable {
@Override public void onVsync(long timestampNanos, long physicalDisplayId, int frame) { mTimestampNanos = timestampNanos; mFrame = frame; Message msg = Message.obtain(mHandler, this); msg.setAsynchronous(true); mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS); }
int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) { // Allowed return values of this function as documented in LooperCallback::handleEvent constexpr int REMOVE_CALLBACK = 0; constexpr int KEEP_CALLBACK = 1;
if (events & ALOOPER_EVENT_INPUT) { JNIEnv* env = AndroidRuntime::getJNIEnv(); status_t status = consumeEvents(env, false /*consumeBatches*/, -1, nullptr); mMessageQueue->raiseAndClearException(env, "handleReceiveCallback"); return status == OK || status == NO_MEMORY ? KEEP_CALLBACK : REMOVE_CALLBACK; }
status_t status = mInputConsumer.consume(&mInputEventFactory, consumeBatches, frameTime, &seq, &inputEvent); if (status != OK && status != WOULD_BLOCK) { ALOGE("channel '%s' ~ Failed to consume input event. status=%s(%d)", getInputChannelName().c_str(), statusToString(status).c_str(), status); return 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().c_str()); return DEAD_OBJECT; } }
final class WindowInputEventReceiver extends InputEventReceiver { public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) { super(inputChannel, looper); }
@Override public void onInputEvent(InputEvent event) { Trace.traceBegin(Trace.TRACE_TAG_VIEW, "processInputEventForCompatibility"); List<InputEvent> processedEvents; try { processedEvents = mInputCompatProcessor.processInputEventForCompatibility(event); } finally { Trace.traceEnd(Trace.TRACE_TAG_VIEW); } if (processedEvents != null) { if (processedEvents.isEmpty()) { // InputEvent consumed by mInputCompatProcessor finishInputEvent(event, true); } else { for (int i = 0; i < processedEvents.size(); i++) { enqueueInputEvent( processedEvents.get(i), this, QueuedInputEvent.FLAG_MODIFIED_FOR_COMPATIBILITY, true); } } } else { enqueueInputEvent(event, this, 0, true); } }
@Override public void onBatchedInputEventPending(int source) { // mStopped: There will be no more choreographer callbacks if we are stopped, // so we must consume all input immediately to prevent ANR final boolean unbuffered = mUnbufferedInputDispatch || (source & mUnbufferedInputSource) != SOURCE_CLASS_NONE || mStopped; if (unbuffered) { if (mConsumeBatchedInputScheduled) { unscheduleConsumeBatchedInput(); } // Consume event immediately if unbuffered input dispatch has been requested. consumeBatchedInputEvents(-1); return; } scheduleConsumeBatchedInput(); }