Loading core/java/android/view/ViewRootImpl.java +22 −9 Original line number Diff line number Diff line Loading @@ -1201,8 +1201,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); Loading Loading @@ -1414,6 +1413,9 @@ public final class ViewRootImpl implements ViewParent, if (mAttachInfo.mThreadedRenderer != null) { mAttachInfo.mHardwareAccelerated = mAttachInfo.mHardwareAccelerationRequested = true; if (mHardwareRendererObserver != null) { mAttachInfo.mThreadedRenderer.addObserver(mHardwareRendererObserver); } } } } Loading Loading @@ -8118,6 +8120,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); } Loading Loading @@ -8619,18 +8624,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]; Loading @@ -8643,6 +8642,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; Loading core/jni/android_view_InputEventReceiver.cpp +12 −12 Original line number Diff line number Diff line Loading @@ -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"); } Loading Loading @@ -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; } Loading Loading @@ -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; } Loading @@ -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()); } } Loading Loading @@ -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; } Loading tests/Input/src/com/android/test/input/InputEventSenderAndReceiverTest.kt +17 −0 Original line number Diff line number Diff line Loading @@ -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) } } Loading
core/java/android/view/ViewRootImpl.java +22 −9 Original line number Diff line number Diff line Loading @@ -1201,8 +1201,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); Loading Loading @@ -1414,6 +1413,9 @@ public final class ViewRootImpl implements ViewParent, if (mAttachInfo.mThreadedRenderer != null) { mAttachInfo.mHardwareAccelerated = mAttachInfo.mHardwareAccelerationRequested = true; if (mHardwareRendererObserver != null) { mAttachInfo.mThreadedRenderer.addObserver(mHardwareRendererObserver); } } } } Loading Loading @@ -8118,6 +8120,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); } Loading Loading @@ -8619,18 +8624,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]; Loading @@ -8643,6 +8642,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; Loading
core/jni/android_view_InputEventReceiver.cpp +12 −12 Original line number Diff line number Diff line Loading @@ -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"); } Loading Loading @@ -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; } Loading Loading @@ -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; } Loading @@ -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()); } } Loading Loading @@ -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; } Loading
tests/Input/src/com/android/test/input/InputEventSenderAndReceiverTest.kt +17 −0 Original line number Diff line number Diff line Loading @@ -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) } }