diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index a06f193255b2..6901087ba9ee 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -1200,8 +1200,7 @@ public final class ViewRootImpl implements ViewParent, Looper.myLooper()); if (mAttachInfo.mThreadedRenderer != null) { - InputMetricsListener listener = - new InputMetricsListener(mInputEventReceiver); + InputMetricsListener listener = new InputMetricsListener(); mHardwareRendererObserver = new HardwareRendererObserver( listener, listener.data, mHandler, true /*waitForPresentTime*/); mAttachInfo.mThreadedRenderer.addObserver(mHardwareRendererObserver); @@ -1408,6 +1407,9 @@ public final class ViewRootImpl implements ViewParent, if (mAttachInfo.mThreadedRenderer != null) { mAttachInfo.mHardwareAccelerated = mAttachInfo.mHardwareAccelerationRequested = true; + if (mHardwareRendererObserver != null) { + mAttachInfo.mThreadedRenderer.addObserver(mHardwareRendererObserver); + } } } } @@ -8110,6 +8112,9 @@ public final class ViewRootImpl implements ViewParent, ThreadedRenderer hardwareRenderer = mAttachInfo.mThreadedRenderer; if (hardwareRenderer != null) { + if (mHardwareRendererObserver != null) { + hardwareRenderer.removeObserver(mHardwareRendererObserver); + } if (mView != null) { hardwareRenderer.destroyHardwareResources(mView); } @@ -8611,18 +8616,12 @@ public final class ViewRootImpl implements ViewParent, super.dispose(); } } - WindowInputEventReceiver mInputEventReceiver; + private WindowInputEventReceiver mInputEventReceiver; final class InputMetricsListener implements HardwareRendererObserver.OnFrameMetricsAvailableListener { public long[] data = new long[FrameMetrics.Index.FRAME_STATS_COUNT]; - private InputEventReceiver mReceiver; - - InputMetricsListener(InputEventReceiver receiver) { - mReceiver = receiver; - } - @Override public void onFrameMetricsAvailable(int dropCountSinceLastInvocation) { final int inputEventId = (int) data[FrameMetrics.Index.INPUT_EVENT_ID]; @@ -8635,6 +8634,20 @@ public final class ViewRootImpl implements ViewParent, // available, we cannot compute end-to-end input latency metrics. return; } + final long gpuCompletedTime = data[FrameMetrics.Index.GPU_COMPLETED]; + if (mInputEventReceiver == null) { + return; + } + if (gpuCompletedTime >= presentTime) { + final double discrepancyMs = (gpuCompletedTime - presentTime) * 1E-6; + final long vsyncId = data[FrameMetrics.Index.FRAME_TIMELINE_VSYNC_ID]; + Log.w(TAG, "Not reporting timeline because gpuCompletedTime is " + discrepancyMs + + "ms ahead of presentTime. FRAME_TIMELINE_VSYNC_ID=" + vsyncId + + ", INPUT_EVENT_ID=" + inputEventId); + // TODO(b/186664409): figure out why this sometimes happens + return; + } + mInputEventReceiver.reportTimeline(inputEventId, gpuCompletedTime, presentTime); } } HardwareRendererObserver mHardwareRendererObserver; diff --git a/core/jni/android_view_InputEventReceiver.cpp b/core/jni/android_view_InputEventReceiver.cpp index 44597cc4c578..a699f912806d 100644 --- a/core/jni/android_view_InputEventReceiver.cpp +++ b/core/jni/android_view_InputEventReceiver.cpp @@ -241,13 +241,13 @@ status_t NativeInputEventReceiver::processOutboundEvents() { } // Some other error. Give up - ALOGW("Failed to send outbound event on channel '%s'. status=%d", - getInputChannelName().c_str(), status); + ALOGW("Failed to send outbound event on channel '%s'. status=%s(%d)", + getInputChannelName().c_str(), statusToString(status).c_str(), status); if (status != DEAD_OBJECT) { JNIEnv* env = AndroidRuntime::getJNIEnv(); std::string message = - android::base::StringPrintf("Failed to send outbound event. status=%d", - status); + android::base::StringPrintf("Failed to send outbound event. status=%s(%d)", + statusToString(status).c_str(), status); jniThrowRuntimeException(env, message.c_str()); mMessageQueue->raiseAndClearException(env, "finishInputEvent"); } @@ -319,8 +319,8 @@ status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env, 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=%d", - getInputChannelName().c_str(), status); + ALOGE("channel '%s' ~ Failed to consume input event. status=%s(%d)", + getInputChannelName().c_str(), statusToString(status).c_str(), status); return status; } @@ -502,9 +502,9 @@ static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak, receiverWeak, inputChannel, messageQueue); status_t status = receiver->initialize(); if (status) { - std::string message = - android::base::StringPrintf("Failed to initialize input event receiver. status=%d", - status); + std::string message = android::base:: + StringPrintf("Failed to initialize input event receiver. status=%s(%d)", + statusToString(status).c_str(), status); jniThrowRuntimeException(env, message.c_str()); return 0; } @@ -531,7 +531,7 @@ static void nativeFinishInputEvent(JNIEnv* env, jclass clazz, jlong receiverPtr, if (status != DEAD_OBJECT) { std::string message = android::base::StringPrintf("Failed to finish input event. status=%s(%d)", - strerror(-status), status); + statusToString(status).c_str(), status); jniThrowRuntimeException(env, message.c_str()); } } @@ -564,8 +564,8 @@ static jboolean nativeConsumeBatchedInputEvents(JNIEnv* env, jclass clazz, jlong &consumedBatch); if (status && status != DEAD_OBJECT && !env->ExceptionCheck()) { std::string message = - android::base::StringPrintf("Failed to consume batched input event. status=%d", - status); + android::base::StringPrintf("Failed to consume batched input event. status=%s(%d)", + statusToString(status).c_str(), status); jniThrowRuntimeException(env, message.c_str()); return JNI_FALSE; } diff --git a/tests/Input/src/com/android/test/input/InputEventSenderAndReceiverTest.kt b/tests/Input/src/com/android/test/input/InputEventSenderAndReceiverTest.kt index fc1d83960351..014efc2b954c 100644 --- a/tests/Input/src/com/android/test/input/InputEventSenderAndReceiverTest.kt +++ b/tests/Input/src/com/android/test/input/InputEventSenderAndReceiverTest.kt @@ -145,4 +145,21 @@ class InputEventSenderAndReceiverTest { val received = mSender.getTimeline() assertEquals(sent, received) } + + // If an invalid timeline is sent, the channel should get closed. This helps surface any + // app-originating bugs early, and forces the work-around to happen in the early stages of the + // event processing. + @Test + fun testSendAndReceiveInvalidTimeline() { + val sent = TestInputEventSender.Timeline( + inputEventId = 1, gpuCompletedTime = 3, presentTime = 2) + mReceiver.reportTimeline(sent.inputEventId, sent.gpuCompletedTime, sent.presentTime) + val received = mSender.getTimeline() + assertEquals(null, received) + // Sender will no longer receive callbacks for this fd, even if receiver sends a valid + // timeline later + mReceiver.reportTimeline(2 /*inputEventId*/, 3 /*gpuCompletedTime*/, 4 /*presentTime*/) + val receivedSecondTimeline = mSender.getTimeline() + assertEquals(null, receivedSecondTimeline) + } }