diff options
| -rw-r--r-- | include/input/Input.h | 14 | ||||
| -rw-r--r-- | include/input/InputTransport.h | 35 | ||||
| -rw-r--r-- | libs/input/InputTransport.cpp | 75 | ||||
| -rw-r--r-- | libs/input/tests/InputPublisherAndConsumer_test.cpp | 96 | ||||
| -rw-r--r-- | libs/input/tests/StructLayout_test.cpp | 7 | ||||
| -rw-r--r-- | services/inputflinger/dispatcher/InputDispatcher.cpp | 16 | ||||
| -rw-r--r-- | services/inputflinger/tests/InputDispatcher_test.cpp | 25 |
7 files changed, 213 insertions, 55 deletions
diff --git a/include/input/Input.h b/include/input/Input.h index f9fe6b9597..bb5ca0ef04 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -266,6 +266,20 @@ enum class MotionClassification : uint8_t { const char* motionClassificationToString(MotionClassification classification); /** + * Portion of FrameMetrics timeline of interest to input code. + */ +enum GraphicsTimeline : size_t { + /** Time when the app sent the buffer to SurfaceFlinger. */ + GPU_COMPLETED_TIME = 0, + + /** Time when the frame was presented on the display */ + PRESENT_TIME = 1, + + /** Total size of the 'GraphicsTimeline' array. Must always be last. */ + SIZE = 2 +}; + +/** * Generator of unique numbers used to identify input events. * * Layout of ID: diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h index 3e5674e4fb..898d1a937c 100644 --- a/include/input/InputTransport.h +++ b/include/input/InputTransport.h @@ -71,6 +71,7 @@ struct InputMessage { FOCUS, CAPTURE, DRAG, + TIMELINE, }; struct Header { @@ -195,6 +196,14 @@ struct InputMessage { inline size_t size() const { return sizeof(Drag); } } drag; + + struct Timeline { + int32_t eventId; + uint32_t empty; + std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline; + + inline size_t size() const { return sizeof(Timeline); } + } timeline; } __attribute__((aligned(8))) body; bool isValid(size_t actualSize) const; @@ -381,10 +390,25 @@ public: nsecs_t consumeTime; }; - /* Receives the finished signal from the consumer in reply to the original dispatch signal. - * If a signal was received, returns a Finished object. + struct Timeline { + int32_t inputEventId; + std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline; + }; + + typedef std::variant<Finished, Timeline> ConsumerResponse; + /* Receive a signal from the consumer in reply to the original dispatch signal. + * If a signal was received, returns a Finished or a Timeline object. + * The InputConsumer should return a Finished object for every InputMessage that it is sent + * to confirm that it has been processed and that the InputConsumer is responsive. + * If several InputMessages are sent to InputConsumer, it's possible to receive Finished + * events out of order for those messages. * - * The returned sequence number is never 0 unless the operation failed. + * The Timeline object is returned whenever the receiving end has processed a graphical frame + * and is returning the timeline of the frame. Not all input events will cause a Timeline + * object to be returned, and there is not guarantee about when it will arrive. + * + * If an object of Finished is returned, the returned sequence number is never 0 unless the + * operation failed. * * Returned error codes: * OK on success. @@ -392,7 +416,7 @@ public: * DEAD_OBJECT if the channel's peer has been closed. * Other errors probably indicate that the channel is broken. */ - android::base::Result<Finished> receiveFinishedSignal(); + android::base::Result<ConsumerResponse> receiveConsumerResponse(); private: std::shared_ptr<InputChannel> mChannel; @@ -448,6 +472,9 @@ public: */ status_t sendFinishedSignal(uint32_t seq, bool handled); + status_t sendTimeline(int32_t inputEventId, + std::array<nsecs_t, GraphicsTimeline::SIZE> timeline); + /* Returns true if there is a deferred event waiting. * * Should be called after calling consume() to determine whether the consumer diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp index c2a3cf1816..f7962e0f09 100644 --- a/libs/input/InputTransport.cpp +++ b/libs/input/InputTransport.cpp @@ -110,6 +110,12 @@ bool InputMessage::isValid(size_t actualSize) const { return true; case Type::DRAG: return true; + case Type::TIMELINE: + const nsecs_t gpuCompletedTime = + body.timeline.graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME]; + const nsecs_t presentTime = + body.timeline.graphicsTimeline[GraphicsTimeline::PRESENT_TIME]; + return presentTime > gpuCompletedTime; } } return false; @@ -129,6 +135,8 @@ size_t InputMessage::size() const { return sizeof(Header) + body.capture.size(); case Type::DRAG: return sizeof(Header) + body.drag.size(); + case Type::TIMELINE: + return sizeof(Header) + body.timeline.size(); } return sizeof(Header); } @@ -260,6 +268,11 @@ void InputMessage::getSanitizedCopy(InputMessage* msg) const { msg->body.drag.isExiting = body.drag.isExiting; break; } + case InputMessage::Type::TIMELINE: { + msg->body.timeline.eventId = body.timeline.eventId; + msg->body.timeline.graphicsTimeline = body.timeline.graphicsTimeline; + break; + } } } @@ -629,7 +642,7 @@ status_t InputPublisher::publishDragEvent(uint32_t seq, int32_t eventId, float x return mChannel->sendMessage(&msg); } -android::base::Result<InputPublisher::Finished> InputPublisher::receiveFinishedSignal() { +android::base::Result<InputPublisher::ConsumerResponse> InputPublisher::receiveConsumerResponse() { if (DEBUG_TRANSPORT_ACTIONS) { ALOGD("channel '%s' publisher ~ %s", mChannel->getName().c_str(), __func__); } @@ -639,16 +652,24 @@ android::base::Result<InputPublisher::Finished> InputPublisher::receiveFinishedS if (result) { return android::base::Error(result); } - if (msg.header.type != InputMessage::Type::FINISHED) { - ALOGE("channel '%s' publisher ~ Received unexpected %s message from consumer", - mChannel->getName().c_str(), NamedEnum::string(msg.header.type).c_str()); - return android::base::Error(UNKNOWN_ERROR); + if (msg.header.type == InputMessage::Type::FINISHED) { + return Finished{ + .seq = msg.header.seq, + .handled = msg.body.finished.handled, + .consumeTime = msg.body.finished.consumeTime, + }; + } + + if (msg.header.type == InputMessage::Type::TIMELINE) { + return Timeline{ + .inputEventId = msg.body.timeline.eventId, + .graphicsTimeline = msg.body.timeline.graphicsTimeline, + }; } - return Finished{ - .seq = msg.header.seq, - .handled = msg.body.finished.handled, - .consumeTime = msg.body.finished.consumeTime, - }; + + ALOGE("channel '%s' publisher ~ Received unexpected %s message from consumer", + mChannel->getName().c_str(), NamedEnum::string(msg.header.type).c_str()); + return android::base::Error(UNKNOWN_ERROR); } // --- InputConsumer --- @@ -785,7 +806,8 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consum break; } - case InputMessage::Type::FINISHED: { + case InputMessage::Type::FINISHED: + case InputMessage::Type::TIMELINE: { LOG_ALWAYS_FATAL("Consumed a %s message, which should never be seen by " "InputConsumer!", NamedEnum::string(mMsg.header.type).c_str()); @@ -1193,6 +1215,24 @@ status_t InputConsumer::sendFinishedSignal(uint32_t seq, bool handled) { return sendUnchainedFinishedSignal(seq, handled); } +status_t InputConsumer::sendTimeline(int32_t inputEventId, + std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline) { + if (DEBUG_TRANSPORT_ACTIONS) { + ALOGD("channel '%s' consumer ~ sendTimeline: inputEventId=%" PRId32 + ", gpuCompletedTime=%" PRId64 ", presentTime=%" PRId64, + mChannel->getName().c_str(), inputEventId, + graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME], + graphicsTimeline[GraphicsTimeline::PRESENT_TIME]); + } + + InputMessage msg; + msg.header.type = InputMessage::Type::TIMELINE; + msg.header.seq = 0; + msg.body.timeline.eventId = inputEventId; + msg.body.timeline.graphicsTimeline = std::move(graphicsTimeline); + return mChannel->sendMessage(&msg); +} + nsecs_t InputConsumer::getConsumeTime(uint32_t seq) const { auto it = mConsumeTimes.find(seq); // Consume time will be missing if either 'finishInputEvent' is called twice, or if it was @@ -1399,6 +1439,19 @@ std::string InputConsumer::dump() const { toString(msg.body.drag.isExiting)); break; } + case InputMessage::Type::TIMELINE: { + const nsecs_t gpuCompletedTime = + msg.body.timeline + .graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME]; + const nsecs_t presentTime = + msg.body.timeline.graphicsTimeline[GraphicsTimeline::PRESENT_TIME]; + out += android::base::StringPrintf("inputEventId=%" PRId32 + ", gpuCompletedTime=%" PRId64 + ", presentTime=%" PRId64, + msg.body.timeline.eventId, gpuCompletedTime, + presentTime); + break; + } } out += "\n"; } diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp index fc31715668..088e00b59c 100644 --- a/libs/input/tests/InputPublisherAndConsumer_test.cpp +++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp @@ -124,13 +124,15 @@ void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() { ASSERT_EQ(OK, status) << "consumer sendFinishedSignal should return OK"; - Result<InputPublisher::Finished> result = mPublisher->receiveFinishedSignal(); - ASSERT_TRUE(result.ok()) << "publisher receiveFinishedSignal should return OK"; - ASSERT_EQ(seq, result->seq) - << "receiveFinishedSignal should have returned the original sequence number"; - ASSERT_TRUE(result->handled) - << "receiveFinishedSignal should have set handled to consumer's reply"; - ASSERT_GE(result->consumeTime, publishTime) + Result<InputPublisher::ConsumerResponse> result = mPublisher->receiveConsumerResponse(); + ASSERT_TRUE(result.ok()) << "receiveConsumerResponse should return OK"; + ASSERT_TRUE(std::holds_alternative<InputPublisher::Finished>(*result)); + const InputPublisher::Finished& finish = std::get<InputPublisher::Finished>(*result); + ASSERT_EQ(seq, finish.seq) + << "receiveConsumerResponse should have returned the original sequence number"; + ASSERT_TRUE(finish.handled) + << "receiveConsumerResponse should have set handled to consumer's reply"; + ASSERT_GE(finish.consumeTime, publishTime) << "finished signal's consume time should be greater than publish time"; } @@ -264,13 +266,15 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { ASSERT_EQ(OK, status) << "consumer sendFinishedSignal should return OK"; - Result<InputPublisher::Finished> result = mPublisher->receiveFinishedSignal(); - ASSERT_TRUE(result.ok()) << "receiveFinishedSignal should return OK"; - ASSERT_EQ(seq, result->seq) - << "receiveFinishedSignal should have returned the original sequence number"; - ASSERT_FALSE(result->handled) - << "receiveFinishedSignal should have set handled to consumer's reply"; - ASSERT_GE(result->consumeTime, publishTime) + Result<InputPublisher::ConsumerResponse> result = mPublisher->receiveConsumerResponse(); + ASSERT_TRUE(result.ok()) << "receiveConsumerResponse should return OK"; + ASSERT_TRUE(std::holds_alternative<InputPublisher::Finished>(*result)); + const InputPublisher::Finished& finish = std::get<InputPublisher::Finished>(*result); + ASSERT_EQ(seq, finish.seq) + << "receiveConsumerResponse should have returned the original sequence number"; + ASSERT_FALSE(finish.handled) + << "receiveConsumerResponse should have set handled to consumer's reply"; + ASSERT_GE(finish.consumeTime, publishTime) << "finished signal's consume time should be greater than publish time"; } @@ -304,14 +308,16 @@ void InputPublisherAndConsumerTest::PublishAndConsumeFocusEvent() { status = mConsumer->sendFinishedSignal(seq, true); ASSERT_EQ(OK, status) << "consumer sendFinishedSignal should return OK"; - Result<InputPublisher::Finished> result = mPublisher->receiveFinishedSignal(); + Result<InputPublisher::ConsumerResponse> result = mPublisher->receiveConsumerResponse(); + ASSERT_TRUE(result.ok()) << "receiveConsumerResponse should return OK"; + ASSERT_TRUE(std::holds_alternative<InputPublisher::Finished>(*result)); + const InputPublisher::Finished& finish = std::get<InputPublisher::Finished>(*result); - ASSERT_TRUE(result.ok()) << "receiveFinishedSignal should return OK"; - ASSERT_EQ(seq, result->seq) - << "receiveFinishedSignal should have returned the original sequence number"; - ASSERT_TRUE(result->handled) - << "receiveFinishedSignal should have set handled to consumer's reply"; - ASSERT_GE(result->consumeTime, publishTime) + ASSERT_EQ(seq, finish.seq) + << "receiveConsumerResponse should have returned the original sequence number"; + ASSERT_TRUE(finish.handled) + << "receiveConsumerResponse should have set handled to consumer's reply"; + ASSERT_GE(finish.consumeTime, publishTime) << "finished signal's consume time should be greater than publish time"; } @@ -343,13 +349,15 @@ void InputPublisherAndConsumerTest::PublishAndConsumeCaptureEvent() { status = mConsumer->sendFinishedSignal(seq, true); ASSERT_EQ(OK, status) << "consumer sendFinishedSignal should return OK"; - android::base::Result<InputPublisher::Finished> result = mPublisher->receiveFinishedSignal(); - ASSERT_TRUE(result.ok()) << "publisher receiveFinishedSignal should return OK"; - ASSERT_EQ(seq, result->seq) - << "receiveFinishedSignal should have returned the original sequence number"; - ASSERT_TRUE(result->handled) - << "receiveFinishedSignal should have set handled to consumer's reply"; - ASSERT_GE(result->consumeTime, publishTime) + Result<InputPublisher::ConsumerResponse> result = mPublisher->receiveConsumerResponse(); + ASSERT_TRUE(result.ok()) << "receiveConsumerResponse should return OK"; + ASSERT_TRUE(std::holds_alternative<InputPublisher::Finished>(*result)); + const InputPublisher::Finished& finish = std::get<InputPublisher::Finished>(*result); + ASSERT_EQ(seq, finish.seq) + << "receiveConsumerResponse should have returned the original sequence number"; + ASSERT_TRUE(finish.handled) + << "receiveConsumerResponse should have set handled to consumer's reply"; + ASSERT_GE(finish.consumeTime, publishTime) << "finished signal's consume time should be greater than publish time"; } @@ -385,16 +393,34 @@ void InputPublisherAndConsumerTest::PublishAndConsumeDragEvent() { status = mConsumer->sendFinishedSignal(seq, true); ASSERT_EQ(OK, status) << "consumer sendFinishedSignal should return OK"; - android::base::Result<InputPublisher::Finished> result = mPublisher->receiveFinishedSignal(); - ASSERT_TRUE(result.ok()) << "publisher receiveFinishedSignal should return OK"; - ASSERT_EQ(seq, result->seq) - << "publisher receiveFinishedSignal should have returned the original sequence number"; - ASSERT_TRUE(result->handled) - << "publisher receiveFinishedSignal should have set handled to consumer's reply"; - ASSERT_GE(result->consumeTime, publishTime) + Result<InputPublisher::ConsumerResponse> result = mPublisher->receiveConsumerResponse(); + ASSERT_TRUE(result.ok()) << "receiveConsumerResponse should return OK"; + ASSERT_TRUE(std::holds_alternative<InputPublisher::Finished>(*result)); + const InputPublisher::Finished& finish = std::get<InputPublisher::Finished>(*result); + ASSERT_EQ(seq, finish.seq) + << "receiveConsumerResponse should have returned the original sequence number"; + ASSERT_TRUE(finish.handled) + << "receiveConsumerResponse should have set handled to consumer's reply"; + ASSERT_GE(finish.consumeTime, publishTime) << "finished signal's consume time should be greater than publish time"; } +TEST_F(InputPublisherAndConsumerTest, SendTimeline) { + const int32_t inputEventId = 20; + std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline; + graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME] = 30; + graphicsTimeline[GraphicsTimeline::PRESENT_TIME] = 40; + status_t status = mConsumer->sendTimeline(inputEventId, graphicsTimeline); + ASSERT_EQ(OK, status); + + Result<InputPublisher::ConsumerResponse> result = mPublisher->receiveConsumerResponse(); + ASSERT_TRUE(result.ok()) << "receiveConsumerResponse should return OK"; + ASSERT_TRUE(std::holds_alternative<InputPublisher::Timeline>(*result)); + const InputPublisher::Timeline& timeline = std::get<InputPublisher::Timeline>(*result); + ASSERT_EQ(inputEventId, timeline.inputEventId); + ASSERT_EQ(graphicsTimeline, timeline.graphicsTimeline); +} + TEST_F(InputPublisherAndConsumerTest, PublishKeyEvent_EndToEnd) { ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent()); } diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp index 3d80b38636..585779e472 100644 --- a/libs/input/tests/StructLayout_test.cpp +++ b/libs/input/tests/StructLayout_test.cpp @@ -96,6 +96,10 @@ void TestInputMessageAlignment() { CHECK_OFFSET(InputMessage::Body::Finished, handled, 0); CHECK_OFFSET(InputMessage::Body::Finished, empty, 1); CHECK_OFFSET(InputMessage::Body::Finished, consumeTime, 8); + + CHECK_OFFSET(InputMessage::Body::Timeline, eventId, 0); + CHECK_OFFSET(InputMessage::Body::Timeline, empty, 4); + CHECK_OFFSET(InputMessage::Body::Timeline, graphicsTimeline, 8); } void TestHeaderSize() { @@ -117,6 +121,9 @@ void TestBodySize() { static_assert(sizeof(InputMessage::Body::Focus) == 8); static_assert(sizeof(InputMessage::Body::Capture) == 8); static_assert(sizeof(InputMessage::Body::Drag) == 16); + // Timeline + static_assert(GraphicsTimeline::SIZE == 2); + static_assert(sizeof(InputMessage::Body::Timeline) == 24); } // --- VerifiedInputEvent --- diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 697739688b..073455a7ff 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -3312,15 +3312,21 @@ int InputDispatcher::handleReceiveCallback(int fd, int events, void* data) { bool gotOne = false; status_t status = OK; for (;;) { - Result<InputPublisher::Finished> result = - connection->inputPublisher.receiveFinishedSignal(); + Result<InputPublisher::ConsumerResponse> result = + connection->inputPublisher.receiveConsumerResponse(); if (!result.ok()) { status = result.error().code(); break; } - const InputPublisher::Finished& finished = *result; - d->finishDispatchCycleLocked(currentTime, connection, finished.seq, - finished.handled, finished.consumeTime); + + if (std::holds_alternative<InputPublisher::Finished>(*result)) { + const InputPublisher::Finished& finish = + std::get<InputPublisher::Finished>(*result); + d->finishDispatchCycleLocked(currentTime, connection, finish.seq, + finish.handled, finish.consumeTime); + } else if (std::holds_alternative<InputPublisher::Timeline>(*result)) { + // TODO(b/167947340): Report this data to LatencyTracker + } gotOne = true; } if (gotOne) { diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 76f6e747db..b62fce4a1c 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -742,6 +742,11 @@ public: ASSERT_EQ(OK, status) << mName.c_str() << ": consumer sendFinishedSignal should return OK."; } + void sendTimeline(int32_t inputEventId, std::array<nsecs_t, GraphicsTimeline::SIZE> timeline) { + const status_t status = mConsumer->sendTimeline(inputEventId, timeline); + ASSERT_EQ(OK, status); + } + void consumeEvent(int32_t expectedEventType, int32_t expectedAction, std::optional<int32_t> expectedDisplayId, std::optional<int32_t> expectedFlags) { @@ -1054,6 +1059,11 @@ public: mInputReceiver->finishEvent(sequenceNum); } + void sendTimeline(int32_t inputEventId, std::array<nsecs_t, GraphicsTimeline::SIZE> timeline) { + ASSERT_NE(mInputReceiver, nullptr) << "Invalid receive event on window with no receiver"; + mInputReceiver->sendTimeline(inputEventId, timeline); + } + InputEvent* consume() { if (mInputReceiver == nullptr) { return nullptr; @@ -1970,6 +1980,21 @@ TEST_F(InputDispatcherTest, PointerCancel_SendCancelWhenSplitTouch) { secondWindow->assertNoEvents(); } +TEST_F(InputDispatcherTest, SendTimeline_DoesNotCrashDispatcher) { + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + + sp<FakeWindowHandle> window = + new FakeWindowHandle(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); + std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline; + graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME] = 2; + graphicsTimeline[GraphicsTimeline::PRESENT_TIME] = 3; + + window->sendTimeline(1 /*inputEventId*/, graphicsTimeline); + window->assertNoEvents(); + mDispatcher->waitForIdle(); +} + class FakeMonitorReceiver { public: FakeMonitorReceiver(const sp<InputDispatcher>& dispatcher, const std::string name, |