From 183c44ce4db3f30912a6233f3567ee7990de6063 Mon Sep 17 00:00:00 2001 From: Marissa Wall Date: Mon, 8 Apr 2019 12:58:50 -0700 Subject: ASurfaceControl: support framebuffer devices There are Android devices that use a hwc2onfbadapter that allows them to ship framebuffer devices but still pass VTS and appear to the system as if they have hwc2.x*. Unfortunately, these devices do not support a present fence. This patch updates the documentation to let developers know that not all devices will return a present fence. Test: ASurfaceControlTest Bug: 129880031 Change-Id: I81dd21dc088b1d2d73d1bdb9c46216ef2e171063 --- include/android/surface_control.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/android/surface_control.h b/include/android/surface_control.h index ef2ad9998c..abb8368069 100644 --- a/include/android/surface_control.h +++ b/include/android/surface_control.h @@ -130,7 +130,7 @@ int64_t ASurfaceTransactionStats_getLatchTime(ASurfaceTransactionStats* surface_ /** * Returns a sync fence that signals when the transaction has been presented. * The recipient of the callback takes ownership of the fence and is responsible for closing - * it. + * it. If a device does not support present fences, a -1 will be returned. */ int ASurfaceTransactionStats_getPresentFenceFd(ASurfaceTransactionStats* surface_transaction_stats) __INTRODUCED_IN(29); -- cgit v1.2.3-59-g8ed1b From e4a1cabd61895eeda1968eabeb19b9c5ad74b430 Mon Sep 17 00:00:00 2001 From: Dan Albert Date: Fri, 14 Jun 2019 14:01:20 -0700 Subject: Fix bad doxygen tags in font headers. Test: Compared to other NDK headers. Bug: None Change-Id: If2ff22d1502ec8541e57aa26a5a42461abf9fdc7 --- include/android/font.h | 2 +- include/android/font_matcher.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/android/font.h b/include/android/font.h index 435a573f51..8001ee1938 100644 --- a/include/android/font.h +++ b/include/android/font.h @@ -16,7 +16,7 @@ /** * @addtogroup Font - * { + * @{ */ /** diff --git a/include/android/font_matcher.h b/include/android/font_matcher.h index e286a4c812..0b8f892b9b 100644 --- a/include/android/font_matcher.h +++ b/include/android/font_matcher.h @@ -16,7 +16,7 @@ /** * @addtogroup Font - * { + * @{ */ /** -- cgit v1.2.3-59-g8ed1b From 12abc195bac9feedcc1447855c878f9b31a9e7c8 Mon Sep 17 00:00:00 2001 From: Dan Albert Date: Tue, 18 Jun 2019 15:13:05 -0700 Subject: Fix another doxygen issue. Test: Eyeballs Bug: None Change-Id: I0eb5c82498a532c88eb13fa72b41c720c6d8537e --- include/android/system_fonts.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/android/system_fonts.h b/include/android/system_fonts.h index dde9055c7a..f0485a1871 100644 --- a/include/android/system_fonts.h +++ b/include/android/system_fonts.h @@ -16,7 +16,7 @@ /** * @addtogroup Font - * { + * @{ */ /** -- cgit v1.2.3-59-g8ed1b From 00f511d329924824b1961e9472c3a06683fc2216 Mon Sep 17 00:00:00 2001 From: Garfield Tan Date: Wed, 12 Jun 2019 16:55:40 -0700 Subject: Dispatch mouse events to window under the cursor. This CL adds cursor positions to NotifyMotionArgs, MotionEntry, InputMessage motion body and MotionEvent. Bug: 134788085 Test: The window under the cursor always responds to the gesture. Test: atest inputflinger_tests Test: atest libinput_tests Change-Id: I8ea460ed8738ffc3a5e997215685889cc1e1f2fe --- include/input/Input.h | 48 ++-- include/input/InputTransport.h | 31 +-- libs/input/Input.cpp | 55 +++-- libs/input/InputTransport.cpp | 62 ++---- libs/input/tests/InputEvent_test.cpp | 52 ++++- .../input/tests/InputPublisherAndConsumer_test.cpp | 40 ++-- libs/input/tests/StructLayout_test.cpp | 6 +- libs/input/tests/VelocityTracker_test.cpp | 13 +- services/inputflinger/InputDispatcher.cpp | 240 +++++++++++--------- services/inputflinger/InputDispatcher.h | 20 +- services/inputflinger/InputListener.cpp | 100 +++++---- services/inputflinger/InputReader.cpp | 248 +++++++++++---------- services/inputflinger/include/InputListener.h | 22 +- .../tests/InputClassifierConverter_test.cpp | 15 +- .../inputflinger/tests/InputClassifier_test.cpp | 15 +- .../inputflinger/tests/InputDispatcher_test.cpp | 143 ++++++++---- 16 files changed, 650 insertions(+), 460 deletions(-) (limited to 'include') diff --git a/include/input/Input.h b/include/input/Input.h index 805957a5ca..a97624658c 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -24,12 +24,14 @@ */ #include +#include #include #include #include #include #include -#include + +#include /* * Additional private constants not defined in ndk/ui/input.h. @@ -246,6 +248,13 @@ enum class MotionClassification : uint8_t { */ const char* motionClassificationToString(MotionClassification classification); +/** + * Invalid value for cursor position. Used for non-mouse events, tests and injected events. Don't + * use it for direct comparison with any other value, because NaN isn't equal to itself according to + * IEEE 754. Use isnan() instead to check if a cursor position is valid. + */ +constexpr float AMOTION_EVENT_INVALID_CURSOR_POSITION = std::numeric_limits::quiet_NaN(); + /* * Pointer coordinate data. */ @@ -459,6 +468,14 @@ public: inline float getYPrecision() const { return mYPrecision; } + inline float getRawXCursorPosition() const { return mXCursorPosition; } + + float getXCursorPosition() const; + + inline float getRawYCursorPosition() const { return mYCursorPosition; } + + float getYCursorPosition() const; + inline nsecs_t getDownTime() const { return mDownTime; } inline void setDownTime(nsecs_t downTime) { mDownTime = downTime; } @@ -600,26 +617,13 @@ public: ssize_t findPointerIndex(int32_t pointerId) const; - void initialize( - int32_t deviceId, - int32_t source, - int32_t displayId, - int32_t action, - int32_t actionButton, - int32_t flags, - int32_t edgeFlags, - int32_t metaState, - int32_t buttonState, - MotionClassification classification, - float xOffset, - float yOffset, - float xPrecision, - float yPrecision, - nsecs_t downTime, - nsecs_t eventTime, - size_t pointerCount, - const PointerProperties* pointerProperties, - const PointerCoords* pointerCoords); + void initialize(int32_t deviceId, int32_t source, int32_t displayId, int32_t action, + int32_t actionButton, int32_t flags, int32_t edgeFlags, int32_t metaState, + int32_t buttonState, MotionClassification classification, float xOffset, + float yOffset, float xPrecision, float yPrecision, float mXCursorPosition, + float mYCursorPosition, nsecs_t downTime, nsecs_t eventTime, + size_t pointerCount, const PointerProperties* pointerProperties, + const PointerCoords* pointerCoords); void copyFrom(const MotionEvent* other, bool keepHistory); @@ -669,6 +673,8 @@ protected: float mYOffset; float mXPrecision; float mYPrecision; + float mXCursorPosition; + float mYCursorPosition; nsecs_t mDownTime; Vector mPointerProperties; Vector mSampleEventTimes; diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h index 63606e5911..df23f613c8 100644 --- a/include/input/InputTransport.h +++ b/include/input/InputTransport.h @@ -113,6 +113,8 @@ struct InputMessage { float yOffset; float xPrecision; float yPrecision; + float xCursorPosition; + float yCursorPosition; uint32_t pointerCount; uint32_t empty3; // Note that PointerCoords requires 8 byte alignment. @@ -261,27 +263,14 @@ public: * Returns BAD_VALUE if seq is 0 or if pointerCount is less than 1 or greater than MAX_POINTERS. * Other errors probably indicate that the channel is broken. */ - status_t publishMotionEvent( - uint32_t seq, - int32_t deviceId, - int32_t source, - int32_t displayId, - int32_t action, - int32_t actionButton, - int32_t flags, - int32_t edgeFlags, - int32_t metaState, - int32_t buttonState, - MotionClassification classification, - float xOffset, - float yOffset, - float xPrecision, - float yPrecision, - nsecs_t downTime, - nsecs_t eventTime, - uint32_t pointerCount, - const PointerProperties* pointerProperties, - const PointerCoords* pointerCoords); + status_t publishMotionEvent(uint32_t seq, int32_t deviceId, int32_t source, int32_t displayId, + int32_t action, int32_t actionButton, int32_t flags, + int32_t edgeFlags, int32_t metaState, int32_t buttonState, + MotionClassification classification, float xOffset, float yOffset, + float xPrecision, float yPrecision, float xCursorPosition, + float yCursorPosition, nsecs_t downTime, nsecs_t eventTime, + uint32_t pointerCount, const PointerProperties* pointerProperties, + const PointerCoords* pointerCoords); /* Receives the finished signal from the consumer in reply to the original dispatch signal. * If a signal was received, returns the message sequence number, diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index 9fd25f9cb7..3266b0740d 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -235,26 +235,14 @@ void PointerProperties::copyFrom(const PointerProperties& other) { // --- MotionEvent --- -void MotionEvent::initialize( - int32_t deviceId, - int32_t source, - int32_t displayId, - int32_t action, - int32_t actionButton, - int32_t flags, - int32_t edgeFlags, - int32_t metaState, - int32_t buttonState, - MotionClassification classification, - float xOffset, - float yOffset, - float xPrecision, - float yPrecision, - nsecs_t downTime, - nsecs_t eventTime, - size_t pointerCount, - const PointerProperties* pointerProperties, - const PointerCoords* pointerCoords) { +void MotionEvent::initialize(int32_t deviceId, int32_t source, int32_t displayId, int32_t action, + int32_t actionButton, int32_t flags, int32_t edgeFlags, + int32_t metaState, int32_t buttonState, + MotionClassification classification, float xOffset, float yOffset, + float xPrecision, float yPrecision, float xCursorPosition, + float yCursorPosition, nsecs_t downTime, nsecs_t eventTime, + size_t pointerCount, const PointerProperties* pointerProperties, + const PointerCoords* pointerCoords) { InputEvent::initialize(deviceId, source, displayId); mAction = action; mActionButton = actionButton; @@ -267,6 +255,8 @@ void MotionEvent::initialize( mYOffset = yOffset; mXPrecision = xPrecision; mYPrecision = yPrecision; + mXCursorPosition = xCursorPosition; + mYCursorPosition = yCursorPosition; mDownTime = downTime; mPointerProperties.clear(); mPointerProperties.appendArray(pointerProperties, pointerCount); @@ -288,6 +278,8 @@ void MotionEvent::copyFrom(const MotionEvent* other, bool keepHistory) { mYOffset = other->mYOffset; mXPrecision = other->mXPrecision; mYPrecision = other->mYPrecision; + mXCursorPosition = other->mXCursorPosition; + mYCursorPosition = other->mYCursorPosition; mDownTime = other->mDownTime; mPointerProperties = other->mPointerProperties; @@ -312,6 +304,16 @@ void MotionEvent::addSample( mSamplePointerCoords.appendArray(pointerCoords, getPointerCount()); } +float MotionEvent::getXCursorPosition() const { + const float rawX = getRawXCursorPosition(); + return rawX + mXOffset; +} + +float MotionEvent::getYCursorPosition() const { + const float rawY = getRawYCursorPosition(); + return rawY + mYOffset; +} + const PointerCoords* MotionEvent::getRawPointerCoords(size_t pointerIndex) const { return &mSamplePointerCoords[getHistorySize() * getPointerCount() + pointerIndex]; } @@ -431,6 +433,15 @@ void MotionEvent::transform(const float matrix[9]) { float originX, originY; transformPoint(matrix, 0, 0, &originX, &originY); + // Apply the transformation to cursor position. + if (!isnan(mXCursorPosition) && !isnan(mYCursorPosition)) { + float x = mXCursorPosition + oldXOffset; + float y = mYCursorPosition + oldYOffset; + transformPoint(matrix, x, y, &x, &y); + mXCursorPosition = x - mXOffset; + mYCursorPosition = y - mYOffset; + } + // Apply the transformation to all samples. size_t numSamples = mSamplePointerCoords.size(); for (size_t i = 0; i < numSamples; i++) { @@ -470,6 +481,8 @@ status_t MotionEvent::readFromParcel(Parcel* parcel) { mYOffset = parcel->readFloat(); mXPrecision = parcel->readFloat(); mYPrecision = parcel->readFloat(); + mXCursorPosition = parcel->readFloat(); + mYCursorPosition = parcel->readFloat(); mDownTime = parcel->readInt64(); mPointerProperties.clear(); @@ -521,6 +534,8 @@ status_t MotionEvent::writeToParcel(Parcel* parcel) const { parcel->writeFloat(mYOffset); parcel->writeFloat(mXPrecision); parcel->writeFloat(mYPrecision); + parcel->writeFloat(mXCursorPosition); + parcel->writeFloat(mYCursorPosition); parcel->writeInt64(mDownTime); for (size_t i = 0; i < pointerCount; i++) { diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp index d02cb8ea46..904a6feb03 100644 --- a/libs/input/InputTransport.cpp +++ b/libs/input/InputTransport.cpp @@ -191,6 +191,10 @@ void InputMessage::getSanitizedCopy(InputMessage* msg) const { msg->body.motion.xPrecision = body.motion.xPrecision; // float yPrecision msg->body.motion.yPrecision = body.motion.yPrecision; + // float xCursorPosition + msg->body.motion.xCursorPosition = body.motion.xCursorPosition; + // float yCursorPosition + msg->body.motion.yCursorPosition = body.motion.yCursorPosition; // uint32_t pointerCount msg->body.motion.pointerCount = body.motion.pointerCount; //struct Pointer pointers[MAX_POINTERS] @@ -465,26 +469,12 @@ status_t InputPublisher::publishKeyEvent( } status_t InputPublisher::publishMotionEvent( - uint32_t seq, - int32_t deviceId, - int32_t source, - int32_t displayId, - int32_t action, - int32_t actionButton, - int32_t flags, - int32_t edgeFlags, - int32_t metaState, - int32_t buttonState, - MotionClassification classification, - float xOffset, - float yOffset, - float xPrecision, - float yPrecision, - nsecs_t downTime, - nsecs_t eventTime, - uint32_t pointerCount, - const PointerProperties* pointerProperties, - const PointerCoords* pointerCoords) { + uint32_t seq, int32_t deviceId, int32_t source, int32_t displayId, int32_t action, + int32_t actionButton, int32_t flags, int32_t edgeFlags, int32_t metaState, + int32_t buttonState, MotionClassification classification, float xOffset, float yOffset, + float xPrecision, float yPrecision, float xCursorPosition, float yCursorPosition, + nsecs_t downTime, nsecs_t eventTime, uint32_t pointerCount, + const PointerProperties* pointerProperties, const PointerCoords* pointerCoords) { if (ATRACE_ENABLED()) { std::string message = StringPrintf( "publishMotionEvent(inputChannel=%s, action=%" PRId32 ")", @@ -532,6 +522,8 @@ status_t InputPublisher::publishMotionEvent( msg.body.motion.yOffset = yOffset; msg.body.motion.xPrecision = xPrecision; msg.body.motion.yPrecision = yPrecision; + msg.body.motion.xCursorPosition = xCursorPosition; + msg.body.motion.yCursorPosition = yCursorPosition; msg.body.motion.downTime = downTime; msg.body.motion.eventTime = eventTime; msg.body.motion.pointerCount = pointerCount; @@ -1135,26 +1127,16 @@ void InputConsumer::initializeMotionEvent(MotionEvent* event, const InputMessage pointerCoords[i].copyFrom(msg->body.motion.pointers[i].coords); } - event->initialize( - msg->body.motion.deviceId, - msg->body.motion.source, - msg->body.motion.displayId, - msg->body.motion.action, - msg->body.motion.actionButton, - msg->body.motion.flags, - msg->body.motion.edgeFlags, - msg->body.motion.metaState, - msg->body.motion.buttonState, - msg->body.motion.classification, - msg->body.motion.xOffset, - msg->body.motion.yOffset, - msg->body.motion.xPrecision, - msg->body.motion.yPrecision, - msg->body.motion.downTime, - msg->body.motion.eventTime, - pointerCount, - pointerProperties, - pointerCoords); + event->initialize(msg->body.motion.deviceId, msg->body.motion.source, + msg->body.motion.displayId, msg->body.motion.action, + msg->body.motion.actionButton, msg->body.motion.flags, + msg->body.motion.edgeFlags, msg->body.motion.metaState, + msg->body.motion.buttonState, msg->body.motion.classification, + msg->body.motion.xOffset, msg->body.motion.yOffset, + msg->body.motion.xPrecision, msg->body.motion.yPrecision, + msg->body.motion.xCursorPosition, msg->body.motion.yCursorPosition, + msg->body.motion.downTime, msg->body.motion.eventTime, pointerCount, + pointerProperties, pointerCoords); } void InputConsumer::addSample(MotionEvent* event, const InputMessage* msg) { diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp index 2b75c82bb1..ec34f3e652 100644 --- a/libs/input/tests/InputEvent_test.cpp +++ b/libs/input/tests/InputEvent_test.cpp @@ -255,11 +255,11 @@ void MotionEventTest::initializeEventWithHistory(MotionEvent* event) { pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 27); pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 28); event->initialize(2, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, AMOTION_EVENT_ACTION_MOVE, 0, - AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, - AMOTION_EVENT_EDGE_FLAG_TOP, AMETA_ALT_ON, AMOTION_EVENT_BUTTON_PRIMARY, - MotionClassification::NONE, X_OFFSET, Y_OFFSET, 2.0f, 2.1f, - ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME, - 2, pointerProperties, pointerCoords); + AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, AMOTION_EVENT_EDGE_FLAG_TOP, + AMETA_ALT_ON, AMOTION_EVENT_BUTTON_PRIMARY, MotionClassification::NONE, + X_OFFSET, Y_OFFSET, 2.0f, 2.1f, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_DOWN_TIME, + ARBITRARY_EVENT_TIME, 2, pointerProperties, pointerCoords); pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 110); pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 111); @@ -571,10 +571,11 @@ TEST_F(MotionEventTest, Transform) { } MotionEvent event; event.initialize(0 /*deviceId*/, AINPUT_SOURCE_UNKNOWN, DISPLAY_ID, AMOTION_EVENT_ACTION_MOVE, - 0 /*actionButton*/, 0 /*flags*/, AMOTION_EVENT_EDGE_FLAG_NONE, - AMETA_NONE, 0 /*buttonState*/, MotionClassification::NONE, - 0 /*xOffset*/, 0 /*yOffset*/, 0 /*xPrecision*/, 0 /*yPrecision*/, - 0 /*downTime*/, 0 /*eventTime*/, pointerCount, pointerProperties, pointerCoords); + 0 /*actionButton*/, 0 /*flags*/, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, + 0 /*buttonState*/, MotionClassification::NONE, 0 /*xOffset*/, 0 /*yOffset*/, + 0 /*xPrecision*/, 0 /*yPrecision*/, 3 + RADIUS /*xCursorPosition*/, + 2 /*yCursorPosition*/, 0 /*downTime*/, 0 /*eventTime*/, pointerCount, + pointerProperties, pointerCoords); float originalRawX = 0 + 3; float originalRawY = -RADIUS + 2; @@ -602,6 +603,10 @@ TEST_F(MotionEventTest, Transform) { ASSERT_NEAR(tanf(angle), tanf(event.getOrientation(i)), 0.1); } + // Check cursor positions. + ASSERT_NEAR(sinf(PI_180 * (90 + ROTATION)) * RADIUS, event.getXCursorPosition(), 0.001); + ASSERT_NEAR(-cosf(PI_180 * (90 + ROTATION)) * RADIUS, event.getYCursorPosition(), 0.001); + // Applying the transformation should preserve the raw X and Y of the first point. ASSERT_NEAR(originalRawX, event.getRawX(0), 0.001); ASSERT_NEAR(originalRawY, event.getRawY(0), 0.001); @@ -626,11 +631,34 @@ TEST_F(MotionEventTest, Initialize_SetsClassification) { for (MotionClassification classification : classifications) { event.initialize(0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, - AMOTION_EVENT_ACTION_DOWN, 0, 0, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0, - classification, 0, 0, 0, 0, 0 /*downTime*/, 0 /*eventTime*/, - pointerCount, pointerProperties, pointerCoords); + AMOTION_EVENT_ACTION_DOWN, 0, 0, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, + 0, classification, 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, 0 /*downTime*/, 0 /*eventTime*/, + pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(classification, event.getClassification()); } } +TEST_F(MotionEventTest, Initialize_SetsCursorPosition) { + MotionEvent event; + constexpr size_t pointerCount = 1; + PointerProperties pointerProperties[pointerCount]; + PointerCoords pointerCoords[pointerCount]; + for (size_t i = 0; i < pointerCount; i++) { + pointerProperties[i].clear(); + pointerProperties[i].id = i; + pointerCoords[i].clear(); + } + + event.initialize(0 /*deviceId*/, AINPUT_SOURCE_MOUSE, DISPLAY_ID, AMOTION_EVENT_ACTION_DOWN, 0, + 0, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0, MotionClassification::NONE, 0, + 0, 0, 0, 280 /*xCursorPosition*/, 540 /*yCursorPosition*/, 0 /*downTime*/, + 0 /*eventTime*/, pointerCount, pointerProperties, pointerCoords); + event.offsetLocation(20, 60); + ASSERT_EQ(280, event.getRawXCursorPosition()); + ASSERT_EQ(540, event.getRawYCursorPosition()); + ASSERT_EQ(300, event.getXCursorPosition()); + ASSERT_EQ(600, event.getYCursorPosition()); +} + } // namespace android diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp index f2cd1be33f..a362f3281d 100644 --- a/libs/input/tests/InputPublisherAndConsumer_test.cpp +++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp @@ -146,6 +146,8 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { constexpr float yOffset = -20; constexpr float xPrecision = 0.25; constexpr float yPrecision = 0.5; + constexpr float xCursorPosition = 1.3; + constexpr float yCursorPosition = 50.6; constexpr nsecs_t downTime = 3; constexpr size_t pointerCount = 3; constexpr nsecs_t eventTime = 4; @@ -168,10 +170,12 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 3.5 * i); } - status = mPublisher->publishMotionEvent(seq, deviceId, source, displayId, action, actionButton, - flags, edgeFlags, metaState, buttonState, classification, - xOffset, yOffset, xPrecision, yPrecision, downTime, eventTime, pointerCount, - pointerProperties, pointerCoords); + status = + mPublisher->publishMotionEvent(seq, deviceId, source, displayId, action, actionButton, + flags, edgeFlags, metaState, buttonState, classification, + xOffset, yOffset, xPrecision, yPrecision, + xCursorPosition, yCursorPosition, downTime, eventTime, + pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(OK, status) << "publisher publishMotionEvent should return OK"; @@ -199,6 +203,10 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { EXPECT_EQ(classification, motionEvent->getClassification()); EXPECT_EQ(xPrecision, motionEvent->getXPrecision()); EXPECT_EQ(yPrecision, motionEvent->getYPrecision()); + EXPECT_EQ(xCursorPosition, motionEvent->getRawXCursorPosition()); + EXPECT_EQ(yCursorPosition, motionEvent->getRawYCursorPosition()); + EXPECT_EQ(xCursorPosition + xOffset, motionEvent->getXCursorPosition()); + EXPECT_EQ(yCursorPosition + yOffset, motionEvent->getYCursorPosition()); EXPECT_EQ(downTime, motionEvent->getDownTime()); EXPECT_EQ(eventTime, motionEvent->getEventTime()); EXPECT_EQ(pointerCount, motionEvent->getPointerCount()); @@ -266,9 +274,11 @@ TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenSequenceNumberIsZer pointerCoords[i].clear(); } - status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - MotionClassification::NONE, 0, 0, 0, 0, 0, 0, - pointerCount, pointerProperties, pointerCoords); + status = + mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, MotionClassification::NONE, + 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0, + pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(BAD_VALUE, status) << "publisher publishMotionEvent should return BAD_VALUE"; } @@ -279,9 +289,11 @@ TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenPointerCountLessTha PointerProperties pointerProperties[pointerCount]; PointerCoords pointerCoords[pointerCount]; - status = mPublisher->publishMotionEvent(1, 0, 0, 0, 0, 0, 0, 0, 0, 0, - MotionClassification::NONE, 0, 0, 0, 0, 0, 0, - pointerCount, pointerProperties, pointerCoords); + status = + mPublisher->publishMotionEvent(1, 0, 0, 0, 0, 0, 0, 0, 0, 0, MotionClassification::NONE, + 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0, + pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(BAD_VALUE, status) << "publisher publishMotionEvent should return BAD_VALUE"; } @@ -297,9 +309,11 @@ TEST_F(InputPublisherAndConsumerTest, pointerCoords[i].clear(); } - status = mPublisher->publishMotionEvent(1, 0, 0, 0, 0, 0, 0, 0, 0, 0, - MotionClassification::NONE, 0, 0, 0, 0, 0, 0, - pointerCount, pointerProperties, pointerCoords); + status = + mPublisher->publishMotionEvent(1, 0, 0, 0, 0, 0, 0, 0, 0, 0, MotionClassification::NONE, + 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0, + pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(BAD_VALUE, status) << "publisher publishMotionEvent should return BAD_VALUE"; } diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp index 62023fb328..8d8cf06c91 100644 --- a/libs/input/tests/StructLayout_test.cpp +++ b/libs/input/tests/StructLayout_test.cpp @@ -64,8 +64,10 @@ void TestInputMessageAlignment() { CHECK_OFFSET(InputMessage::Body::Motion, yOffset, 68); CHECK_OFFSET(InputMessage::Body::Motion, xPrecision, 72); CHECK_OFFSET(InputMessage::Body::Motion, yPrecision, 76); - CHECK_OFFSET(InputMessage::Body::Motion, pointerCount, 80); - CHECK_OFFSET(InputMessage::Body::Motion, pointers, 88); + CHECK_OFFSET(InputMessage::Body::Motion, xCursorPosition, 80); + CHECK_OFFSET(InputMessage::Body::Motion, yCursorPosition, 84); + CHECK_OFFSET(InputMessage::Body::Motion, pointerCount, 88); + CHECK_OFFSET(InputMessage::Body::Motion, pointers, 96); CHECK_OFFSET(InputMessage::Body::Finished, seq, 0); CHECK_OFFSET(InputMessage::Body::Finished, handled, 4); diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp index 368446ff4d..968e2fa6bc 100644 --- a/libs/input/tests/VelocityTracker_test.cpp +++ b/libs/input/tests/VelocityTracker_test.cpp @@ -176,12 +176,13 @@ static std::vector createMotionEventStream( EXPECT_EQ(pointerIndex, pointerCount); MotionEvent event; - event.initialize(0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, - action, 0 /*actionButton*/, 0 /*flags*/, - AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/, - MotionClassification::NONE, - 0 /*xOffset*/, 0 /*yOffset*/, 0 /*xPrecision*/, 0 /*yPrecision*/, - 0 /*downTime*/, entry.eventTime.count(), pointerCount, properties, coords); + event.initialize(0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, action, + 0 /*actionButton*/, 0 /*flags*/, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, + 0 /*buttonState*/, MotionClassification::NONE, 0 /*xOffset*/, + 0 /*yOffset*/, 0 /*xPrecision*/, 0 /*yPrecision*/, + AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, 0 /*downTime*/, + entry.eventTime.count(), pointerCount, properties, coords); events.emplace_back(event); } diff --git a/services/inputflinger/InputDispatcher.cpp b/services/inputflinger/InputDispatcher.cpp index c2ff4c9629..b32309990f 100644 --- a/services/inputflinger/InputDispatcher.cpp +++ b/services/inputflinger/InputDispatcher.cpp @@ -262,14 +262,18 @@ static T getValueByKey(std::unordered_map& map, U key) { // --- InputDispatcher --- -InputDispatcher::InputDispatcher(const sp& policy) : - mPolicy(policy), - mPendingEvent(nullptr), mLastDropReason(DROP_REASON_NOT_DROPPED), - mAppSwitchSawKeyDown(false), mAppSwitchDueTime(LONG_LONG_MAX), - mNextUnblockedEvent(nullptr), - mDispatchEnabled(false), mDispatchFrozen(false), mInputFilterEnabled(false), - mFocusedDisplayId(ADISPLAY_ID_DEFAULT), - mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE) { +InputDispatcher::InputDispatcher(const sp& policy) + : mPolicy(policy), + mPendingEvent(nullptr), + mLastDropReason(DROP_REASON_NOT_DROPPED), + mAppSwitchSawKeyDown(false), + mAppSwitchDueTime(LONG_LONG_MAX), + mNextUnblockedEvent(nullptr), + mDispatchEnabled(false), + mDispatchFrozen(false), + mInputFilterEnabled(false), + mFocusedDisplayId(ADISPLAY_ID_DEFAULT), + mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE) { mLooper = new Looper(false); mReporter = createInputReporter(); @@ -1326,6 +1330,7 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, bool newGesture = (maskedAction == AMOTION_EVENT_ACTION_DOWN || maskedAction == AMOTION_EVENT_ACTION_SCROLL || isHoverAction); + const bool isFromMouse = entry->source == AINPUT_SOURCE_MOUSE; bool wrongDevice = false; if (newGesture) { bool down = maskedAction == AMOTION_EVENT_ACTION_DOWN; @@ -1361,11 +1366,17 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, if (newGesture || (isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN)) { /* Case 1: New splittable pointer going down, or need target for hover or scroll. */ + int32_t x; + int32_t y; int32_t pointerIndex = getMotionEventActionPointerIndex(action); - int32_t x = int32_t(entry->pointerCoords[pointerIndex]. - getAxisValue(AMOTION_EVENT_AXIS_X)); - int32_t y = int32_t(entry->pointerCoords[pointerIndex]. - getAxisValue(AMOTION_EVENT_AXIS_Y)); + // Always dispatch mouse events to cursor position. + if (isFromMouse) { + x = int32_t(entry->xCursorPosition); + y = int32_t(entry->yCursorPosition); + } else { + x = int32_t(entry->pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_X)); + y = int32_t(entry->pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_Y)); + } bool isDown = maskedAction == AMOTION_EVENT_ACTION_DOWN; sp newTouchedWindowHandle = findTouchedWindowAtLocked( displayId, x, y, isDown /*addOutsideTargets*/, true /*addPortalWindows*/); @@ -2256,15 +2267,21 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, } // Publish the motion event. - status = connection->inputPublisher.publishMotionEvent(dispatchEntry->seq, - motionEntry->deviceId, motionEntry->source, motionEntry->displayId, - dispatchEntry->resolvedAction, motionEntry->actionButton, - dispatchEntry->resolvedFlags, motionEntry->edgeFlags, - motionEntry->metaState, motionEntry->buttonState, motionEntry->classification, - xOffset, yOffset, motionEntry->xPrecision, motionEntry->yPrecision, - motionEntry->downTime, motionEntry->eventTime, - motionEntry->pointerCount, motionEntry->pointerProperties, - usingCoords); + status = + connection->inputPublisher + .publishMotionEvent(dispatchEntry->seq, motionEntry->deviceId, + motionEntry->source, motionEntry->displayId, + dispatchEntry->resolvedAction, + motionEntry->actionButton, + dispatchEntry->resolvedFlags, + motionEntry->edgeFlags, motionEntry->metaState, + motionEntry->buttonState, + motionEntry->classification, xOffset, yOffset, + motionEntry->xPrecision, motionEntry->yPrecision, + motionEntry->xCursorPosition, + motionEntry->yCursorPosition, motionEntry->downTime, + motionEntry->eventTime, motionEntry->pointerCount, + motionEntry->pointerProperties, usingCoords); break; } @@ -2590,24 +2607,17 @@ InputDispatcher::splitMotionEvent(const MotionEntry* originalMotionEntry, BitSet } } - MotionEntry* splitMotionEntry = new MotionEntry( - originalMotionEntry->sequenceNum, - originalMotionEntry->eventTime, - originalMotionEntry->deviceId, - originalMotionEntry->source, - originalMotionEntry->displayId, - originalMotionEntry->policyFlags, - action, - originalMotionEntry->actionButton, - originalMotionEntry->flags, - originalMotionEntry->metaState, - originalMotionEntry->buttonState, - originalMotionEntry->classification, - originalMotionEntry->edgeFlags, - originalMotionEntry->xPrecision, - originalMotionEntry->yPrecision, - originalMotionEntry->downTime, - splitPointerCount, splitPointerProperties, splitPointerCoords, 0, 0); + MotionEntry* splitMotionEntry = + new MotionEntry(originalMotionEntry->sequenceNum, originalMotionEntry->eventTime, + originalMotionEntry->deviceId, originalMotionEntry->source, + originalMotionEntry->displayId, originalMotionEntry->policyFlags, + action, originalMotionEntry->actionButton, originalMotionEntry->flags, + originalMotionEntry->metaState, originalMotionEntry->buttonState, + originalMotionEntry->classification, originalMotionEntry->edgeFlags, + originalMotionEntry->xPrecision, originalMotionEntry->yPrecision, + originalMotionEntry->xCursorPosition, + originalMotionEntry->yCursorPosition, originalMotionEntry->downTime, + splitPointerCount, splitPointerProperties, splitPointerCoords, 0, 0); if (originalMotionEntry->injectionState) { splitMotionEntry->injectionState = originalMotionEntry->injectionState; @@ -2753,12 +2763,14 @@ bool InputDispatcher::shouldSendKeyToInputFilterLocked(const NotifyKeyArgs* args void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) { #if DEBUG_INBOUND_EVENT_DETAILS ALOGD("notifyMotion - eventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32 - ", policyFlags=0x%x, " - "action=0x%x, actionButton=0x%x, flags=0x%x, metaState=0x%x, buttonState=0x%x," - "edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, downTime=%" PRId64, - args->eventTime, args->deviceId, args->source, args->displayId, args->policyFlags, - args->action, args->actionButton, args->flags, args->metaState, args->buttonState, - args->edgeFlags, args->xPrecision, args->yPrecision, args->downTime); + ", policyFlags=0x%x, " + "action=0x%x, actionButton=0x%x, flags=0x%x, metaState=0x%x, buttonState=0x%x, " + "edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, xCursorPosition=%f, " + "mYCursorPosition=%f, downTime=%" PRId64, + args->eventTime, args->deviceId, args->source, args->displayId, args->policyFlags, + args->action, args->actionButton, args->flags, args->metaState, args->buttonState, + args->edgeFlags, args->xPrecision, args->yPrecision, arg->xCursorPosition, + args->yCursorPosition, args->downTime); for (uint32_t i = 0; i < args->pointerCount; i++) { ALOGD(" Pointer %d: id=%d, toolType=%d, " "x=%f, y=%f, pressure=%f, size=%f, " @@ -2800,12 +2812,12 @@ void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) { mLock.unlock(); MotionEvent event; - event.initialize(args->deviceId, args->source, args->displayId, - args->action, args->actionButton, - args->flags, args->edgeFlags, args->metaState, args->buttonState, - args->classification, 0, 0, args->xPrecision, args->yPrecision, - args->downTime, args->eventTime, - args->pointerCount, args->pointerProperties, args->pointerCoords); + event.initialize(args->deviceId, args->source, args->displayId, args->action, + args->actionButton, args->flags, args->edgeFlags, args->metaState, + args->buttonState, args->classification, 0, 0, args->xPrecision, + args->yPrecision, args->xCursorPosition, args->yCursorPosition, + args->downTime, args->eventTime, args->pointerCount, + args->pointerProperties, args->pointerCoords); policyFlags |= POLICY_FLAG_FILTERED; if (!mPolicy->filterInputEvent(&event, policyFlags)) { @@ -2816,12 +2828,14 @@ void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) { } // Just enqueue a new motion event. - MotionEntry* newEntry = new MotionEntry(args->sequenceNum, args->eventTime, - args->deviceId, args->source, args->displayId, policyFlags, - args->action, args->actionButton, args->flags, - args->metaState, args->buttonState, args->classification, - args->edgeFlags, args->xPrecision, args->yPrecision, args->downTime, - args->pointerCount, args->pointerProperties, args->pointerCoords, 0, 0); + MotionEntry* newEntry = + new MotionEntry(args->sequenceNum, args->eventTime, args->deviceId, args->source, + args->displayId, policyFlags, args->action, args->actionButton, + args->flags, args->metaState, args->buttonState, + args->classification, args->edgeFlags, args->xPrecision, + args->yPrecision, args->xCursorPosition, args->yCursorPosition, + args->downTime, args->pointerCount, args->pointerProperties, + args->pointerCoords, 0, 0); needWake = enqueueInboundEventLocked(newEntry); mLock.unlock(); @@ -2952,31 +2966,34 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, mLock.lock(); const nsecs_t* sampleEventTimes = motionEvent->getSampleEventTimes(); const PointerCoords* samplePointerCoords = motionEvent->getSamplePointerCoords(); - firstInjectedEntry = new MotionEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, *sampleEventTimes, - motionEvent->getDeviceId(), motionEvent->getSource(), motionEvent->getDisplayId(), - policyFlags, - action, actionButton, motionEvent->getFlags(), - motionEvent->getMetaState(), motionEvent->getButtonState(), - motionEvent->getClassification(), motionEvent->getEdgeFlags(), - motionEvent->getXPrecision(), motionEvent->getYPrecision(), - motionEvent->getDownTime(), - uint32_t(pointerCount), pointerProperties, samplePointerCoords, - motionEvent->getXOffset(), motionEvent->getYOffset()); + firstInjectedEntry = + new MotionEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, *sampleEventTimes, + motionEvent->getDeviceId(), motionEvent->getSource(), + motionEvent->getDisplayId(), policyFlags, action, actionButton, + motionEvent->getFlags(), motionEvent->getMetaState(), + motionEvent->getButtonState(), motionEvent->getClassification(), + motionEvent->getEdgeFlags(), motionEvent->getXPrecision(), + motionEvent->getYPrecision(), motionEvent->getRawXCursorPosition(), + motionEvent->getRawYCursorPosition(), motionEvent->getDownTime(), + uint32_t(pointerCount), pointerProperties, samplePointerCoords, + motionEvent->getXOffset(), motionEvent->getYOffset()); lastInjectedEntry = firstInjectedEntry; for (size_t i = motionEvent->getHistorySize(); i > 0; i--) { sampleEventTimes += 1; samplePointerCoords += pointerCount; - MotionEntry* nextInjectedEntry = new MotionEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, - *sampleEventTimes, - motionEvent->getDeviceId(), motionEvent->getSource(), - motionEvent->getDisplayId(), policyFlags, - action, actionButton, motionEvent->getFlags(), - motionEvent->getMetaState(), motionEvent->getButtonState(), - motionEvent->getClassification(), motionEvent->getEdgeFlags(), - motionEvent->getXPrecision(), motionEvent->getYPrecision(), - motionEvent->getDownTime(), - uint32_t(pointerCount), pointerProperties, samplePointerCoords, - motionEvent->getXOffset(), motionEvent->getYOffset()); + MotionEntry* nextInjectedEntry = + new MotionEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, *sampleEventTimes, + motionEvent->getDeviceId(), motionEvent->getSource(), + motionEvent->getDisplayId(), policyFlags, action, actionButton, + motionEvent->getFlags(), motionEvent->getMetaState(), + motionEvent->getButtonState(), motionEvent->getClassification(), + motionEvent->getEdgeFlags(), motionEvent->getXPrecision(), + motionEvent->getYPrecision(), + motionEvent->getRawXCursorPosition(), + motionEvent->getRawYCursorPosition(), + motionEvent->getDownTime(), uint32_t(pointerCount), + pointerProperties, samplePointerCoords, + motionEvent->getXOffset(), motionEvent->getYOffset()); lastInjectedEntry->next = nextInjectedEntry; lastInjectedEntry = nextInjectedEntry; } @@ -4633,21 +4650,32 @@ void InputDispatcher::KeyEntry::recycle() { // --- InputDispatcher::MotionEntry --- -InputDispatcher::MotionEntry::MotionEntry(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId, - uint32_t source, int32_t displayId, uint32_t policyFlags, int32_t action, - int32_t actionButton, +InputDispatcher::MotionEntry::MotionEntry( + uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId, uint32_t source, + int32_t displayId, uint32_t policyFlags, int32_t action, int32_t actionButton, int32_t flags, int32_t metaState, int32_t buttonState, MotionClassification classification, - int32_t edgeFlags, float xPrecision, float yPrecision, nsecs_t downTime, - uint32_t pointerCount, + int32_t edgeFlags, float xPrecision, float yPrecision, float xCursorPosition, + float yCursorPosition, nsecs_t downTime, uint32_t pointerCount, const PointerProperties* pointerProperties, const PointerCoords* pointerCoords, - float xOffset, float yOffset) : - EventEntry(sequenceNum, TYPE_MOTION, eventTime, policyFlags), + float xOffset, float yOffset) + : EventEntry(sequenceNum, TYPE_MOTION, eventTime, policyFlags), eventTime(eventTime), - deviceId(deviceId), source(source), displayId(displayId), action(action), - actionButton(actionButton), flags(flags), metaState(metaState), buttonState(buttonState), - classification(classification), edgeFlags(edgeFlags), - xPrecision(xPrecision), yPrecision(yPrecision), - downTime(downTime), pointerCount(pointerCount) { + deviceId(deviceId), + source(source), + displayId(displayId), + action(action), + actionButton(actionButton), + flags(flags), + metaState(metaState), + buttonState(buttonState), + classification(classification), + edgeFlags(edgeFlags), + xPrecision(xPrecision), + yPrecision(yPrecision), + xCursorPosition(xCursorPosition), + yCursorPosition(yCursorPosition), + downTime(downTime), + pointerCount(pointerCount) { for (uint32_t i = 0; i < pointerCount; i++) { this->pointerProperties[i].copyFrom(pointerProperties[i]); this->pointerCoords[i].copyFrom(pointerCoords[i]); @@ -4662,11 +4690,14 @@ InputDispatcher::MotionEntry::~MotionEntry() { void InputDispatcher::MotionEntry::appendDescription(std::string& msg) const { msg += StringPrintf("MotionEvent(deviceId=%d, source=0x%08x, displayId=%" PRId32 - ", action=%s, actionButton=0x%08x, flags=0x%08x, metaState=0x%08x, buttonState=0x%08x, " - "classification=%s, edgeFlags=0x%08x, xPrecision=%.1f, yPrecision=%.1f, pointers=[", - deviceId, source, displayId, motionActionToString(action).c_str(), actionButton, flags, - metaState, buttonState, motionClassificationToString(classification), edgeFlags, - xPrecision, yPrecision); + ", action=%s, actionButton=0x%08x, flags=0x%08x, metaState=0x%08x, " + "buttonState=0x%08x, " + "classification=%s, edgeFlags=0x%08x, xPrecision=%.1f, yPrecision=%.1f, " + "xCursorPosition=%0.1f, yCursorPosition=%0.1f, pointers=[", + deviceId, source, displayId, motionActionToString(action).c_str(), + actionButton, flags, metaState, buttonState, + motionClassificationToString(classification), edgeFlags, xPrecision, + yPrecision, xCursorPosition, yCursorPosition); for (uint32_t i = 0; i < pointerCount; i++) { if (i) { @@ -4936,6 +4967,8 @@ void InputDispatcher::InputState::addMotionMemento(const MotionEntry* entry, memento.flags = flags; memento.xPrecision = entry->xPrecision; memento.yPrecision = entry->yPrecision; + memento.xCursorPosition = entry->xCursorPosition; + memento.yCursorPosition = entry->yCursorPosition; memento.downTime = entry->downTime; memento.setPointers(entry); memento.hovering = hovering; @@ -4966,13 +4999,16 @@ void InputDispatcher::InputState::synthesizeCancelationEvents(nsecs_t currentTim if (shouldCancelMotion(memento, options)) { const int32_t action = memento.hovering ? AMOTION_EVENT_ACTION_HOVER_EXIT : AMOTION_EVENT_ACTION_CANCEL; - outEvents.push_back(new MotionEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, currentTime, - memento.deviceId, memento.source, memento.displayId, memento.policyFlags, - action, 0 /*actionButton*/, memento.flags, AMETA_NONE, 0 /*buttonState*/, - MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, - memento.xPrecision, memento.yPrecision, memento.downTime, - memento.pointerCount, memento.pointerProperties, memento.pointerCoords, - 0 /*xOffset*/, 0 /*yOffset*/)); + outEvents.push_back( + new MotionEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, currentTime, memento.deviceId, + memento.source, memento.displayId, memento.policyFlags, action, + 0 /*actionButton*/, memento.flags, AMETA_NONE, + 0 /*buttonState*/, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, memento.xPrecision, + memento.yPrecision, memento.xCursorPosition, + memento.yCursorPosition, memento.downTime, memento.pointerCount, + memento.pointerProperties, memento.pointerCoords, 0 /*xOffset*/, + 0 /*yOffset*/)); } } } diff --git a/services/inputflinger/InputDispatcher.h b/services/inputflinger/InputDispatcher.h index 753b748884..46dd9bd273 100644 --- a/services/inputflinger/InputDispatcher.h +++ b/services/inputflinger/InputDispatcher.h @@ -570,19 +570,21 @@ private: int32_t edgeFlags; float xPrecision; float yPrecision; + float xCursorPosition; + float yCursorPosition; nsecs_t downTime; uint32_t pointerCount; PointerProperties pointerProperties[MAX_POINTERS]; PointerCoords pointerCoords[MAX_POINTERS]; - MotionEntry(uint32_t sequenceNum, nsecs_t eventTime, - int32_t deviceId, uint32_t source, int32_t displayId, uint32_t policyFlags, - int32_t action, int32_t actionButton, int32_t flags, - int32_t metaState, int32_t buttonState, MotionClassification classification, - int32_t edgeFlags, float xPrecision, float yPrecision, - nsecs_t downTime, uint32_t pointerCount, - const PointerProperties* pointerProperties, const PointerCoords* pointerCoords, - float xOffset, float yOffset); + MotionEntry(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId, uint32_t source, + int32_t displayId, uint32_t policyFlags, int32_t action, int32_t actionButton, + int32_t flags, int32_t metaState, int32_t buttonState, + MotionClassification classification, int32_t edgeFlags, float xPrecision, + float yPrecision, float xCursorPosition, float yCursorPosition, + nsecs_t downTime, uint32_t pointerCount, + const PointerProperties* pointerProperties, const PointerCoords* pointerCoords, + float xOffset, float yOffset); virtual void appendDescription(std::string& msg) const; protected: @@ -830,6 +832,8 @@ private: int32_t flags; float xPrecision; float yPrecision; + float xCursorPosition; + float yCursorPosition; nsecs_t downTime; uint32_t pointerCount; PointerProperties pointerProperties[MAX_POINTERS]; diff --git a/services/inputflinger/InputListener.cpp b/services/inputflinger/InputListener.cpp index 423b69cff3..0498e87732 100644 --- a/services/inputflinger/InputListener.cpp +++ b/services/inputflinger/InputListener.cpp @@ -21,6 +21,7 @@ #include "InputListener.h" #include +#include namespace android { @@ -87,21 +88,33 @@ void NotifyKeyArgs::notify(const sp& listener) const { // --- NotifyMotionArgs --- -NotifyMotionArgs::NotifyMotionArgs(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId, - uint32_t source, int32_t displayId, uint32_t policyFlags, - int32_t action, int32_t actionButton, int32_t flags, int32_t metaState, - int32_t buttonState, MotionClassification classification, +NotifyMotionArgs::NotifyMotionArgs( + uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId, uint32_t source, + int32_t displayId, uint32_t policyFlags, int32_t action, int32_t actionButton, + int32_t flags, int32_t metaState, int32_t buttonState, MotionClassification classification, int32_t edgeFlags, uint32_t deviceTimestamp, uint32_t pointerCount, const PointerProperties* pointerProperties, const PointerCoords* pointerCoords, - float xPrecision, float yPrecision, nsecs_t downTime, - const std::vector& videoFrames) : - NotifyArgs(sequenceNum, eventTime), deviceId(deviceId), source(source), - displayId(displayId), policyFlags(policyFlags), - action(action), actionButton(actionButton), - flags(flags), metaState(metaState), buttonState(buttonState), - classification(classification), edgeFlags(edgeFlags), deviceTimestamp(deviceTimestamp), + float xPrecision, float yPrecision, float xCursorPosition, float yCursorPosition, + nsecs_t downTime, const std::vector& videoFrames) + : NotifyArgs(sequenceNum, eventTime), + deviceId(deviceId), + source(source), + displayId(displayId), + policyFlags(policyFlags), + action(action), + actionButton(actionButton), + flags(flags), + metaState(metaState), + buttonState(buttonState), + classification(classification), + edgeFlags(edgeFlags), + deviceTimestamp(deviceTimestamp), pointerCount(pointerCount), - xPrecision(xPrecision), yPrecision(yPrecision), downTime(downTime), + xPrecision(xPrecision), + yPrecision(yPrecision), + xCursorPosition(xCursorPosition), + yCursorPosition(yCursorPosition), + downTime(downTime), videoFrames(videoFrames) { for (uint32_t i = 0; i < pointerCount; i++) { this->pointerProperties[i].copyFrom(pointerProperties[i]); @@ -109,14 +122,26 @@ NotifyMotionArgs::NotifyMotionArgs(uint32_t sequenceNum, nsecs_t eventTime, int3 } } -NotifyMotionArgs::NotifyMotionArgs(const NotifyMotionArgs& other) : - NotifyArgs(other.sequenceNum, other.eventTime), deviceId(other.deviceId), - source(other.source), displayId(other.displayId), policyFlags(other.policyFlags), - action(other.action), actionButton(other.actionButton), flags(other.flags), - metaState(other.metaState), buttonState(other.buttonState), - classification(other.classification), edgeFlags(other.edgeFlags), - deviceTimestamp(other.deviceTimestamp), pointerCount(other.pointerCount), - xPrecision(other.xPrecision), yPrecision(other.yPrecision), downTime(other.downTime), +NotifyMotionArgs::NotifyMotionArgs(const NotifyMotionArgs& other) + : NotifyArgs(other.sequenceNum, other.eventTime), + deviceId(other.deviceId), + source(other.source), + displayId(other.displayId), + policyFlags(other.policyFlags), + action(other.action), + actionButton(other.actionButton), + flags(other.flags), + metaState(other.metaState), + buttonState(other.buttonState), + classification(other.classification), + edgeFlags(other.edgeFlags), + deviceTimestamp(other.deviceTimestamp), + pointerCount(other.pointerCount), + xPrecision(other.xPrecision), + yPrecision(other.yPrecision), + xCursorPosition(other.xCursorPosition), + yCursorPosition(other.yCursorPosition), + downTime(other.downTime), videoFrames(other.videoFrames) { for (uint32_t i = 0; i < pointerCount; i++) { pointerProperties[i].copyFrom(other.pointerProperties[i]); @@ -124,28 +149,23 @@ NotifyMotionArgs::NotifyMotionArgs(const NotifyMotionArgs& other) : } } +static inline bool isCursorPositionEqual(float lhs, float rhs) { + return (isnan(lhs) && isnan(rhs)) || lhs == rhs; +} + bool NotifyMotionArgs::operator==(const NotifyMotionArgs& rhs) const { - bool equal = - sequenceNum == rhs.sequenceNum - && eventTime == rhs.eventTime - && deviceId == rhs.deviceId - && source == rhs.source - && displayId == rhs.displayId - && policyFlags == rhs.policyFlags - && action == rhs.action - && actionButton == rhs.actionButton - && flags == rhs.flags - && metaState == rhs.metaState - && buttonState == rhs.buttonState - && classification == rhs.classification - && edgeFlags == rhs.edgeFlags - && deviceTimestamp == rhs.deviceTimestamp - && pointerCount == rhs.pointerCount + bool equal = sequenceNum == rhs.sequenceNum && eventTime == rhs.eventTime && + deviceId == rhs.deviceId && source == rhs.source && displayId == rhs.displayId && + policyFlags == rhs.policyFlags && action == rhs.action && + actionButton == rhs.actionButton && flags == rhs.flags && metaState == rhs.metaState && + buttonState == rhs.buttonState && classification == rhs.classification && + edgeFlags == rhs.edgeFlags && deviceTimestamp == rhs.deviceTimestamp && + pointerCount == rhs.pointerCount // PointerProperties and PointerCoords are compared separately below - && xPrecision == rhs.xPrecision - && yPrecision == rhs.yPrecision - && downTime == rhs.downTime - && videoFrames == rhs.videoFrames; + && xPrecision == rhs.xPrecision && yPrecision == rhs.yPrecision && + isCursorPositionEqual(xCursorPosition, rhs.xCursorPosition) && + isCursorPositionEqual(yCursorPosition, rhs.yCursorPosition) && + downTime == rhs.downTime && videoFrames == rhs.videoFrames; if (!equal) { return false; } diff --git a/services/inputflinger/InputReader.cpp b/services/inputflinger/InputReader.cpp index a45b8a56ce..9e5990964c 100644 --- a/services/inputflinger/InputReader.cpp +++ b/services/inputflinger/InputReader.cpp @@ -2782,6 +2782,8 @@ void CursorInputMapper::sync(nsecs_t when) { mPointerVelocityControl.move(when, &deltaX, &deltaY); int32_t displayId; + float xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION; + float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION; if (mSource == AINPUT_SOURCE_MOUSE) { if (moved || scrolled || buttonsChanged) { mPointerController->setPresentation( @@ -2798,10 +2800,9 @@ void CursorInputMapper::sync(nsecs_t when) { mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE); } - float x, y; - mPointerController->getPosition(&x, &y); - pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x); - pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y); + mPointerController->getPosition(&xCursorPosition, &yCursorPosition); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition); pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, deltaX); pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, deltaY); displayId = mPointerController->getDisplayId(); @@ -2845,21 +2846,25 @@ void CursorInputMapper::sync(nsecs_t when) { int32_t actionButton = BitSet32::valueForBit(released.clearFirstMarkedBit()); buttonState &= ~actionButton; NotifyMotionArgs releaseArgs(mContext->getNextSequenceNum(), when, getDeviceId(), - mSource, displayId, policyFlags, - AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, 0, - metaState, buttonState, - MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, - /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords, - mXPrecision, mYPrecision, downTime, /* videoFrames */ {}); + mSource, displayId, policyFlags, + AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, 0, + metaState, buttonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, + /* deviceTimestamp */ 0, 1, &pointerProperties, + &pointerCoords, mXPrecision, mYPrecision, + xCursorPosition, yCursorPosition, downTime, + /* videoFrames */ {}); getListener()->notifyMotion(&releaseArgs); } } NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, - displayId, policyFlags, motionEventAction, 0, 0, metaState, currentButtonState, - MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, - /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords, - mXPrecision, mYPrecision, downTime, /* videoFrames */ {}); + displayId, policyFlags, motionEventAction, 0, 0, metaState, + currentButtonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, + /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords, + mXPrecision, mYPrecision, xCursorPosition, yCursorPosition, downTime, + /* videoFrames */ {}); getListener()->notifyMotion(&args); if (buttonsPressed) { @@ -2868,11 +2873,14 @@ void CursorInputMapper::sync(nsecs_t when) { int32_t actionButton = BitSet32::valueForBit(pressed.clearFirstMarkedBit()); buttonState |= actionButton; NotifyMotionArgs pressArgs(mContext->getNextSequenceNum(), when, getDeviceId(), - mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_BUTTON_PRESS, - actionButton, 0, metaState, buttonState, - MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, - /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords, - mXPrecision, mYPrecision, downTime, /* videoFrames */ {}); + mSource, displayId, policyFlags, + AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton, 0, + metaState, buttonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, + /* deviceTimestamp */ 0, 1, &pointerProperties, + &pointerCoords, mXPrecision, mYPrecision, + xCursorPosition, yCursorPosition, downTime, + /* videoFrames */ {}); getListener()->notifyMotion(&pressArgs); } } @@ -2882,12 +2890,14 @@ void CursorInputMapper::sync(nsecs_t when) { // Send hover move after UP to tell the application that the mouse is hovering now. if (motionEventAction == AMOTION_EVENT_ACTION_UP && (mSource == AINPUT_SOURCE_MOUSE)) { - NotifyMotionArgs hoverArgs(mContext->getNextSequenceNum(), when, getDeviceId(), - mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, - metaState, currentButtonState, - MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, - /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords, - mXPrecision, mYPrecision, downTime, /* videoFrames */ {}); + NotifyMotionArgs hoverArgs(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, + 0, metaState, currentButtonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, + /* deviceTimestamp */ 0, 1, &pointerProperties, + &pointerCoords, mXPrecision, mYPrecision, xCursorPosition, + yCursorPosition, downTime, + /* videoFrames */ {}); getListener()->notifyMotion(&hoverArgs); } @@ -2897,11 +2907,14 @@ void CursorInputMapper::sync(nsecs_t when) { pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll); NotifyMotionArgs scrollArgs(mContext->getNextSequenceNum(), when, getDeviceId(), - mSource, displayId, policyFlags, - AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, currentButtonState, - MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, - /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords, - mXPrecision, mYPrecision, downTime, /* videoFrames */ {}); + mSource, displayId, policyFlags, + AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, + currentButtonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, + /* deviceTimestamp */ 0, 1, &pointerProperties, + &pointerCoords, mXPrecision, mYPrecision, xCursorPosition, + yCursorPosition, downTime, + /* videoFrames */ {}); getListener()->notifyMotion(&scrollArgs); } } @@ -3041,12 +3054,13 @@ void RotaryEncoderInputMapper::sync(nsecs_t when) { int32_t metaState = mContext->getGlobalMetaState(); pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_SCROLL, scroll * mScalingFactor); - NotifyMotionArgs scrollArgs(mContext->getNextSequenceNum(), when, getDeviceId(), - mSource, displayId, policyFlags, - AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, /* buttonState */ 0, - MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, - /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords, - 0, 0, 0, /* videoFrames */ {}); + NotifyMotionArgs scrollArgs(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0, + metaState, /* buttonState */ 0, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, + /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords, + 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {}); getListener()->notifyMotion(&scrollArgs); } @@ -5450,12 +5464,12 @@ void TouchInputMapper::dispatchPointerGestures(nsecs_t when, uint32_t policyFlag pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y); const int32_t displayId = mPointerController->getDisplayId(); - NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), - mSource, displayId, policyFlags, - AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, - metaState, buttonState, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, - /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords, - 0, 0, mPointerGesture.downTime, /* videoFrames */ {}); + NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, + metaState, buttonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, + /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords, 0, 0, + x, y, mPointerGesture.downTime, /* videoFrames */ {}); getListener()->notifyMotion(&args); } @@ -6360,29 +6374,31 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, int32_t metaState = getContext()->getGlobalMetaState(); int32_t displayId = mViewport.displayId; - if (mPointerController != nullptr) { - if (down || hovering) { - mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER); - mPointerController->clearSpots(); - mPointerController->setButtonState(mCurrentRawState.buttonState); - mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE); - } else if (!down && !hovering && (mPointerSimple.down || mPointerSimple.hovering)) { - mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); - } - displayId = mPointerController->getDisplayId(); + if (down || hovering) { + mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER); + mPointerController->clearSpots(); + mPointerController->setButtonState(mCurrentRawState.buttonState); + mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE); + } else if (!down && !hovering && (mPointerSimple.down || mPointerSimple.hovering)) { + mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); } + displayId = mPointerController->getDisplayId(); + + float xCursorPosition; + float yCursorPosition; + mPointerController->getPosition(&xCursorPosition, &yCursorPosition); if (mPointerSimple.down && !down) { mPointerSimple.down = false; // Send up. - NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), - mSource, displayId, policyFlags, - AMOTION_EVENT_ACTION_UP, 0, 0, metaState, mLastRawState.buttonState, - MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, - 1, &mPointerSimple.lastProperties, &mPointerSimple.lastCoords, - mOrientedXPrecision, mOrientedYPrecision, - mPointerSimple.downTime, /* videoFrames */ {}); + NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + displayId, policyFlags, AMOTION_EVENT_ACTION_UP, 0, 0, metaState, + mLastRawState.buttonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, 1, + &mPointerSimple.lastProperties, &mPointerSimple.lastCoords, + mOrientedXPrecision, mOrientedYPrecision, xCursorPosition, + yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {}); getListener()->notifyMotion(&args); } @@ -6390,13 +6406,13 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, mPointerSimple.hovering = false; // Send hover exit. - NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), - mSource, displayId, policyFlags, - AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0, metaState, mLastRawState.buttonState, - MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, - 1, &mPointerSimple.lastProperties, &mPointerSimple.lastCoords, - mOrientedXPrecision, mOrientedYPrecision, - mPointerSimple.downTime, /* videoFrames */ {}); + NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0, + metaState, mLastRawState.buttonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, 1, + &mPointerSimple.lastProperties, &mPointerSimple.lastCoords, + mOrientedXPrecision, mOrientedYPrecision, xCursorPosition, + yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {}); getListener()->notifyMotion(&args); } @@ -6406,25 +6422,25 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, mPointerSimple.downTime = when; // Send down. - NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), - mSource, displayId, policyFlags, - AMOTION_EVENT_ACTION_DOWN, 0, 0, metaState, mCurrentRawState.buttonState, - MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, - /* deviceTimestamp */ 0, - 1, &mPointerSimple.currentProperties, &mPointerSimple.currentCoords, - mOrientedXPrecision, mOrientedYPrecision, - mPointerSimple.downTime, /* videoFrames */ {}); + NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + displayId, policyFlags, AMOTION_EVENT_ACTION_DOWN, 0, 0, + metaState, mCurrentRawState.buttonState, + MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, + /* deviceTimestamp */ 0, 1, &mPointerSimple.currentProperties, + &mPointerSimple.currentCoords, mOrientedXPrecision, + mOrientedYPrecision, xCursorPosition, yCursorPosition, + mPointerSimple.downTime, /* videoFrames */ {}); getListener()->notifyMotion(&args); } // Send move. - NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), - mSource, displayId, policyFlags, - AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, mCurrentRawState.buttonState, - MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, - 1, &mPointerSimple.currentProperties, &mPointerSimple.currentCoords, - mOrientedXPrecision, mOrientedYPrecision, - mPointerSimple.downTime, /* videoFrames */ {}); + NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + displayId, policyFlags, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, + mCurrentRawState.buttonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, 1, + &mPointerSimple.currentProperties, &mPointerSimple.currentCoords, + mOrientedXPrecision, mOrientedYPrecision, xCursorPosition, + yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {}); getListener()->notifyMotion(&args); } @@ -6433,26 +6449,25 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, mPointerSimple.hovering = true; // Send hover enter. - NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), - mSource, displayId, policyFlags, - AMOTION_EVENT_ACTION_HOVER_ENTER, 0, 0, metaState, - mCurrentRawState.buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, - 1, &mPointerSimple.currentProperties, &mPointerSimple.currentCoords, - mOrientedXPrecision, mOrientedYPrecision, - mPointerSimple.downTime, /* videoFrames */ {}); + NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_ENTER, 0, 0, + metaState, mCurrentRawState.buttonState, + MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, + /* deviceTimestamp */ 0, 1, &mPointerSimple.currentProperties, + &mPointerSimple.currentCoords, mOrientedXPrecision, + mOrientedYPrecision, xCursorPosition, yCursorPosition, + mPointerSimple.downTime, /* videoFrames */ {}); getListener()->notifyMotion(&args); } // Send hover move. - NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), - mSource, displayId, policyFlags, - AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState, - mCurrentRawState.buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, - 1, &mPointerSimple.currentProperties, &mPointerSimple.currentCoords, - mOrientedXPrecision, mOrientedYPrecision, - mPointerSimple.downTime, /* videoFrames */ {}); + NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, + metaState, mCurrentRawState.buttonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, 1, + &mPointerSimple.currentProperties, &mPointerSimple.currentCoords, + mOrientedXPrecision, mOrientedYPrecision, xCursorPosition, + yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {}); getListener()->notifyMotion(&args); } @@ -6468,13 +6483,13 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll); pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll); - NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), - mSource, displayId, policyFlags, - AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, mCurrentRawState.buttonState, - MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, - 1, &mPointerSimple.currentProperties, &pointerCoords, - mOrientedXPrecision, mOrientedYPrecision, - mPointerSimple.downTime, /* videoFrames */ {}); + NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, + mCurrentRawState.buttonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, 1, + &mPointerSimple.currentProperties, &pointerCoords, + mOrientedXPrecision, mOrientedYPrecision, xCursorPosition, + yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {}); getListener()->notifyMotion(&args); } @@ -6531,16 +6546,21 @@ void TouchInputMapper::dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32 ALOG_ASSERT(false); } } + float xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION; + float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION; + if (mDeviceMode == DEVICE_MODE_POINTER) { + mPointerController->getPosition(&xCursorPosition, &yCursorPosition); + } const int32_t displayId = getAssociatedDisplay().value_or(ADISPLAY_ID_NONE); const int32_t deviceId = getDeviceId(); std::vector frames = mDevice->getEventHub()->getVideoFrames(deviceId); std::for_each(frames.begin(), frames.end(), [this](TouchVideoFrame& frame) { frame.rotate(this->mSurfaceOrientation); }); - NotifyMotionArgs args(mContext->getNextSequenceNum(), when, deviceId, - source, displayId, policyFlags, - action, actionButton, flags, metaState, buttonState, MotionClassification::NONE, - edgeFlags, deviceTimestamp, pointerCount, pointerProperties, pointerCoords, - xPrecision, yPrecision, downTime, std::move(frames)); + NotifyMotionArgs args(mContext->getNextSequenceNum(), when, deviceId, source, displayId, + policyFlags, action, actionButton, flags, metaState, buttonState, + MotionClassification::NONE, edgeFlags, deviceTimestamp, pointerCount, + pointerProperties, pointerCoords, xPrecision, yPrecision, xCursorPosition, + yCursorPosition, downTime, std::move(frames)); getListener()->notifyMotion(&args); } @@ -7462,10 +7482,12 @@ void JoystickInputMapper::sync(nsecs_t when, bool force) { uint32_t policyFlags = 0; NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), - AINPUT_SOURCE_JOYSTICK, ADISPLAY_ID_NONE, policyFlags, - AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, 1, - &pointerProperties, &pointerCoords, 0, 0, 0, /* videoFrames */ {}); + AINPUT_SOURCE_JOYSTICK, ADISPLAY_ID_NONE, policyFlags, + AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, buttonState, + MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, + /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {}); getListener()->notifyMotion(&args); } diff --git a/services/inputflinger/include/InputListener.h b/services/inputflinger/include/InputListener.h index b51dcb6cad..57c894bf8b 100644 --- a/services/inputflinger/include/InputListener.h +++ b/services/inputflinger/include/InputListener.h @@ -119,19 +119,27 @@ struct NotifyMotionArgs : public NotifyArgs { PointerCoords pointerCoords[MAX_POINTERS]; float xPrecision; float yPrecision; + /** + * Mouse cursor position when this event is reported relative to the origin of the specified + * display. Only valid if this is a mouse event (originates from a mouse or from a trackpad in + * gestures enabled mode. + */ + float xCursorPosition; + float yCursorPosition; nsecs_t downTime; std::vector videoFrames; inline NotifyMotionArgs() { } NotifyMotionArgs(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId, uint32_t source, - int32_t displayId, uint32_t policyFlags, - int32_t action, int32_t actionButton, int32_t flags, - int32_t metaState, int32_t buttonState, MotionClassification classification, - int32_t edgeFlags, uint32_t deviceTimestamp, uint32_t pointerCount, - const PointerProperties* pointerProperties, const PointerCoords* pointerCoords, - float xPrecision, float yPrecision, nsecs_t downTime, - const std::vector& videoFrames); + int32_t displayId, uint32_t policyFlags, int32_t action, int32_t actionButton, + int32_t flags, int32_t metaState, int32_t buttonState, + MotionClassification classification, int32_t edgeFlags, + uint32_t deviceTimestamp, uint32_t pointerCount, + const PointerProperties* pointerProperties, const PointerCoords* pointerCoords, + float xPrecision, float yPrecision, float xCursorPosition, + float yCursorPosition, nsecs_t downTime, + const std::vector& videoFrames); NotifyMotionArgs(const NotifyMotionArgs& other); diff --git a/services/inputflinger/tests/InputClassifierConverter_test.cpp b/services/inputflinger/tests/InputClassifierConverter_test.cpp index 813b69edbb..ba1c7c9284 100644 --- a/services/inputflinger/tests/InputClassifierConverter_test.cpp +++ b/services/inputflinger/tests/InputClassifierConverter_test.cpp @@ -38,12 +38,15 @@ static NotifyMotionArgs generateBasicMotionArgs() { coords.setAxisValue(AMOTION_EVENT_AXIS_Y, 2); coords.setAxisValue(AMOTION_EVENT_AXIS_SIZE, 0.5); static constexpr nsecs_t downTime = 2; - NotifyMotionArgs motionArgs(1/*sequenceNum*/, downTime/*eventTime*/, 3/*deviceId*/, - AINPUT_SOURCE_ANY, ADISPLAY_ID_DEFAULT, 4/*policyFlags*/, AMOTION_EVENT_ACTION_DOWN, - 0/*actionButton*/, 0/*flags*/, AMETA_NONE, 0/*buttonState*/, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 5/*deviceTimestamp*/, - 1/*pointerCount*/, &properties, &coords, 0/*xPrecision*/, 0/*yPrecision*/, - downTime, {}/*videoFrames*/); + NotifyMotionArgs motionArgs(1 /*sequenceNum*/, downTime /*eventTime*/, 3 /*deviceId*/, + AINPUT_SOURCE_ANY, ADISPLAY_ID_DEFAULT, 4 /*policyFlags*/, + AMOTION_EVENT_ACTION_DOWN, 0 /*actionButton*/, 0 /*flags*/, + AMETA_NONE, 0 /*buttonState*/, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, 5 /*deviceTimestamp*/, + 1 /*pointerCount*/, &properties, &coords, 0 /*xPrecision*/, + 0 /*yPrecision*/, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, downTime, + {} /*videoFrames*/); return motionArgs; } diff --git a/services/inputflinger/tests/InputClassifier_test.cpp b/services/inputflinger/tests/InputClassifier_test.cpp index 7cc17a2215..9bc4282d9c 100644 --- a/services/inputflinger/tests/InputClassifier_test.cpp +++ b/services/inputflinger/tests/InputClassifier_test.cpp @@ -38,12 +38,15 @@ static NotifyMotionArgs generateBasicMotionArgs() { coords.setAxisValue(AMOTION_EVENT_AXIS_X, 1); coords.setAxisValue(AMOTION_EVENT_AXIS_Y, 1); static constexpr nsecs_t downTime = 2; - NotifyMotionArgs motionArgs(1/*sequenceNum*/, downTime/*eventTime*/, 3/*deviceId*/, - AINPUT_SOURCE_ANY, ADISPLAY_ID_DEFAULT, 4/*policyFlags*/, AMOTION_EVENT_ACTION_DOWN, - 0/*actionButton*/, 0/*flags*/, AMETA_NONE, 0/*buttonState*/, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 5/*deviceTimestamp*/, - 1/*pointerCount*/, &properties, &coords, 0/*xPrecision*/, 0/*yPrecision*/, - downTime, {}/*videoFrames*/); + NotifyMotionArgs motionArgs(1 /*sequenceNum*/, downTime /*eventTime*/, 3 /*deviceId*/, + AINPUT_SOURCE_ANY, ADISPLAY_ID_DEFAULT, 4 /*policyFlags*/, + AMOTION_EVENT_ACTION_DOWN, 0 /*actionButton*/, 0 /*flags*/, + AMETA_NONE, 0 /*buttonState*/, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, 5 /*deviceTimestamp*/, + 1 /*pointerCount*/, &properties, &coords, 0 /*xPrecision*/, + 0 /*yPrecision*/, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, downTime, + {} /*videoFrames*/); return motionArgs; } diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 9fe6481ca0..c28a6214d6 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -249,8 +249,10 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { // Rejects undefined motion actions. event.initialize(DEVICE_ID, source, DISPLAY_ID, - /*action*/ -1, 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0, - ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); + /*action*/ -1, 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, + ARBITRARY_TIME, ARBITRARY_TIME, + /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) @@ -258,18 +260,24 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { // Rejects pointer down with invalid index. event.initialize(DEVICE_ID, source, DISPLAY_ID, - AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0, - ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); + AMOTION_EVENT_ACTION_POINTER_DOWN | + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, + ARBITRARY_TIME, ARBITRARY_TIME, + /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) << "Should reject motion events with pointer down index too large."; event.initialize(DEVICE_ID, source, DISPLAY_ID, - AMOTION_EVENT_ACTION_POINTER_DOWN | (~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0, - ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); + AMOTION_EVENT_ACTION_POINTER_DOWN | + (~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, + ARBITRARY_TIME, ARBITRARY_TIME, + /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) @@ -277,36 +285,45 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { // Rejects pointer up with invalid index. event.initialize(DEVICE_ID, source, DISPLAY_ID, - AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0, - ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); + AMOTION_EVENT_ACTION_POINTER_UP | + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, + ARBITRARY_TIME, ARBITRARY_TIME, + /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) << "Should reject motion events with pointer up index too large."; event.initialize(DEVICE_ID, source, DISPLAY_ID, - AMOTION_EVENT_ACTION_POINTER_UP | (~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0, - ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); + AMOTION_EVENT_ACTION_POINTER_UP | + (~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, + ARBITRARY_TIME, ARBITRARY_TIME, + /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) << "Should reject motion events with pointer up index too small."; // Rejects motion events with invalid number of pointers. - event.initialize(DEVICE_ID, source, DISPLAY_ID, - AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0, - ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 0, pointerProperties, pointerCoords); + event.initialize(DEVICE_ID, source, DISPLAY_ID, AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, + metaState, 0, classification, 0, 0, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, + ARBITRARY_TIME, ARBITRARY_TIME, + /*pointerCount*/ 0, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) << "Should reject motion events with 0 pointers."; - event.initialize(DEVICE_ID, source, DISPLAY_ID, - AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0, - ARBITRARY_TIME, ARBITRARY_TIME, - /*pointerCount*/ MAX_POINTERS + 1, pointerProperties, pointerCoords); + event.initialize(DEVICE_ID, source, DISPLAY_ID, AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, + metaState, 0, classification, 0, 0, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, + ARBITRARY_TIME, ARBITRARY_TIME, + /*pointerCount*/ MAX_POINTERS + 1, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) @@ -314,18 +331,22 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { // Rejects motion events with invalid pointer ids. pointerProperties[0].id = -1; - event.initialize(DEVICE_ID, source, DISPLAY_ID, - AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0, - ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); + event.initialize(DEVICE_ID, source, DISPLAY_ID, AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, + metaState, 0, classification, 0, 0, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, + ARBITRARY_TIME, ARBITRARY_TIME, + /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) << "Should reject motion events with pointer ids less than 0."; pointerProperties[0].id = MAX_POINTER_ID + 1; - event.initialize(DEVICE_ID, source, DISPLAY_ID, - AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0, - ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); + event.initialize(DEVICE_ID, source, DISPLAY_ID, AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, + metaState, 0, classification, 0, 0, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, + ARBITRARY_TIME, ARBITRARY_TIME, + /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) @@ -334,9 +355,11 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { // Rejects motion events with duplicate pointer ids. pointerProperties[0].id = 1; pointerProperties[1].id = 1; - event.initialize(DEVICE_ID, source, DISPLAY_ID, - AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0, - ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 2, pointerProperties, pointerCoords); + event.initialize(DEVICE_ID, source, DISPLAY_ID, AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, + metaState, 0, classification, 0, 0, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, + ARBITRARY_TIME, ARBITRARY_TIME, + /*pointerCount*/ 2, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) @@ -463,6 +486,7 @@ public: mInfo.frameRight = mFrame.right; mInfo.frameBottom = mFrame.bottom; mInfo.globalScaleFactor = 1.0; + mInfo.touchableRegion.clear(); mInfo.addTouchableRegion(mFrame); mInfo.visible = true; mInfo.canReceiveKeys = true; @@ -521,8 +545,10 @@ static int32_t injectKeyDown(const sp& dispatcher, INJECT_EVENT_TIMEOUT, POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER); } -static int32_t injectMotionDown(const sp& dispatcher, int32_t source, - int32_t displayId, int32_t x = 100, int32_t y = 200) { +static int32_t injectMotionEvent(const sp& dispatcher, int32_t action, + int32_t source, int32_t displayId, int32_t x, int32_t y, + int32_t xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION, + int32_t yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION) { MotionEvent event; PointerProperties pointerProperties[1]; PointerCoords pointerCoords[1]; @@ -537,12 +563,11 @@ static int32_t injectMotionDown(const sp& dispatcher, int32_t s nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); // Define a valid motion down event. - event.initialize(DEVICE_ID, source, displayId, - AMOTION_EVENT_ACTION_DOWN, /* actionButton */ 0, /* flags */ 0, /* edgeFlags */ 0, - AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE, - /* xOffset */ 0, /* yOffset */ 0, /* xPrecision */ 0, - /* yPrecision */ 0, currentTime, currentTime, /*pointerCount*/ 1, pointerProperties, - pointerCoords); + event.initialize(DEVICE_ID, source, displayId, action, /* actionButton */ 0, /* flags */ 0, + /* edgeFlags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE, + /* xOffset */ 0, /* yOffset */ 0, /* xPrecision */ 0, + /* yPrecision */ 0, xCursorPosition, yCursorPosition, currentTime, currentTime, + /*pointerCount*/ 1, pointerProperties, pointerCoords); // Inject event until dispatch out. return dispatcher->injectInputEvent( @@ -551,6 +576,11 @@ static int32_t injectMotionDown(const sp& dispatcher, int32_t s INJECT_EVENT_TIMEOUT, POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER); } +static int32_t injectMotionDown(const sp& dispatcher, int32_t source, + int32_t displayId, int32_t x = 100, int32_t y = 200) { + return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_DOWN, source, displayId, x, y); +} + static NotifyKeyArgs generateKeyArgs(int32_t action, int32_t displayId = ADISPLAY_ID_NONE) { nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); // Define a valid key event. @@ -576,11 +606,12 @@ static NotifyMotionArgs generateMotionArgs(int32_t action, int32_t source, int32 nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); // Define a valid motion event. NotifyMotionArgs args(/* sequenceNum */ 0, currentTime, DEVICE_ID, source, displayId, - POLICY_FLAG_PASS_TO_USER, action, /* actionButton */ 0, /* flags */ 0, - AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, 1, pointerProperties, - pointerCoords, /* xPrecision */ 0, /* yPrecision */ 0, currentTime, - /* videoFrames */ {}); + POLICY_FLAG_PASS_TO_USER, action, /* actionButton */ 0, /* flags */ 0, + AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, 1, + pointerProperties, pointerCoords, /* xPrecision */ 0, /* yPrecision */ 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, currentTime, /* videoFrames */ {}); return args; } @@ -704,6 +735,32 @@ TEST_F(InputDispatcherTest, SetInputWindow_InputWindowInfo) { windowSecond->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE); } +TEST_F(InputDispatcherTest, DispatchMouseEventsUnderCursor) { + sp application = new FakeApplicationHandle(); + + sp windowLeft = + new FakeWindowHandle(application, mDispatcher, "Left", ADISPLAY_ID_DEFAULT); + windowLeft->setFrame(Rect(0, 0, 600, 800)); + windowLeft->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL); + sp windowRight = + new FakeWindowHandle(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT); + windowRight->setFrame(Rect(600, 0, 1200, 800)); + windowRight->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL); + + mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); + + std::vector> inputWindowHandles{windowLeft, windowRight}; + mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT); + + // Inject an event with coordinate in the area of right window, with mouse cursor in the area of + // left window. This event should be dispatched to the left window. + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, + injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE, + ADISPLAY_ID_DEFAULT, 610, 400, 599, 400)); + windowLeft->consumeEvent(AINPUT_EVENT_TYPE_MOTION, ADISPLAY_ID_DEFAULT); + windowRight->assertNoEvents(); +} + /* Test InputDispatcher for MultiDisplay */ class InputDispatcherFocusOnTwoDisplaysTest : public InputDispatcherTest { public: -- cgit v1.2.3-59-g8ed1b From ab0ab9c57c8fa9d8c9648734fea74ee010e28e8c Mon Sep 17 00:00:00 2001 From: Garfield Tan Date: Wed, 10 Jul 2019 18:58:28 -0700 Subject: Address comments from a previous change. The original change is 00f511d329924824b1961e9472c3a06683fc2216. Bug: 134788085 Test: atest libinput_tests Change-Id: I1f3326067f94fe6a09850f4389483e60fa57a8d4 --- include/input/Input.h | 3 +++ libs/input/Input.cpp | 3 +-- libs/input/tests/InputEvent_test.cpp | 10 +++++++--- services/inputflinger/InputDispatcher.cpp | 2 +- 4 files changed, 12 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/input/Input.h b/include/input/Input.h index a97624658c..ad8c233577 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -24,6 +24,7 @@ */ #include +#include #include #include #include @@ -476,6 +477,8 @@ public: float getYCursorPosition() const; + static inline bool isValidCursorPosition(float x, float y) { return !isnan(x) && !isnan(y); } + inline nsecs_t getDownTime() const { return mDownTime; } inline void setDownTime(nsecs_t downTime) { mDownTime = downTime; } diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index 3266b0740d..dc4978b836 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -17,7 +17,6 @@ #define LOG_TAG "Input" //#define LOG_NDEBUG 0 -#include #include #include @@ -434,7 +433,7 @@ void MotionEvent::transform(const float matrix[9]) { transformPoint(matrix, 0, 0, &originX, &originY); // Apply the transformation to cursor position. - if (!isnan(mXCursorPosition) && !isnan(mYCursorPosition)) { + if (isValidCursorPosition(mXCursorPosition, mYCursorPosition)) { float x = mXCursorPosition + oldXOffset; float y = mYCursorPosition + oldYOffset; transformPoint(matrix, x, y, &x, &y); diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp index ec34f3e652..b879de6a74 100644 --- a/libs/input/tests/InputEvent_test.cpp +++ b/libs/input/tests/InputEvent_test.cpp @@ -603,9 +603,13 @@ TEST_F(MotionEventTest, Transform) { ASSERT_NEAR(tanf(angle), tanf(event.getOrientation(i)), 0.1); } - // Check cursor positions. - ASSERT_NEAR(sinf(PI_180 * (90 + ROTATION)) * RADIUS, event.getXCursorPosition(), 0.001); - ASSERT_NEAR(-cosf(PI_180 * (90 + ROTATION)) * RADIUS, event.getYCursorPosition(), 0.001); + // Check cursor positions. The original cursor position is at (3 + RADIUS, 2), where the center + // of the circle is (3, 2), so the cursor position is to the right of the center of the circle. + // The choice of triangular functions in this test defines the angle of rotation clockwise + // relative to the y-axis. Therefore the cursor position's angle is 90 degrees. Here we swap the + // triangular function so that we don't have to add the 90 degrees. + ASSERT_NEAR(cosf(PI_180 * ROTATION) * RADIUS, event.getXCursorPosition(), 0.001); + ASSERT_NEAR(sinf(PI_180 * ROTATION) * RADIUS, event.getYCursorPosition(), 0.001); // Applying the transformation should preserve the raw X and Y of the first point. ASSERT_NEAR(originalRawX, event.getRawX(0), 0.001); diff --git a/services/inputflinger/InputDispatcher.cpp b/services/inputflinger/InputDispatcher.cpp index be1370747c..1d8c365702 100644 --- a/services/inputflinger/InputDispatcher.cpp +++ b/services/inputflinger/InputDispatcher.cpp @@ -2766,7 +2766,7 @@ void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) { ", policyFlags=0x%x, " "action=0x%x, actionButton=0x%x, flags=0x%x, metaState=0x%x, buttonState=0x%x, " "edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, xCursorPosition=%f, " - "mYCursorPosition=%f, downTime=%" PRId64, + "yCursorPosition=%f, downTime=%" PRId64, args->eventTime, args->deviceId, args->source, args->displayId, args->policyFlags, args->action, args->actionButton, args->flags, args->metaState, args->buttonState, args->edgeFlags, args->xPrecision, args->yPrecision, arg->xCursorPosition, -- cgit v1.2.3-59-g8ed1b From 937bb83a143631fcb25f0962aa95c1f850fdf023 Mon Sep 17 00:00:00 2001 From: Garfield Tan Date: Thu, 25 Jul 2019 17:48:31 -0700 Subject: Add setCursorPosition. When source is updated we need to update their values. Also renamed mX/YCursorPosition to mRawX/YCursorPosition to indicate it stores raw values (w/o applying offset). Bug: 134788085 Test: atest libinput_tests Change-Id: I1533d79a7542291974ff572d3aeaf9924e3f0751 --- include/input/Input.h | 14 ++++++++------ libs/input/Input.cpp | 35 ++++++++++++++++++++--------------- libs/input/tests/InputEvent_test.cpp | 10 ++++++++++ 3 files changed, 38 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/include/input/Input.h b/include/input/Input.h index ad8c233577..cbd1a412bf 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -469,14 +469,16 @@ public: inline float getYPrecision() const { return mYPrecision; } - inline float getRawXCursorPosition() const { return mXCursorPosition; } + inline float getRawXCursorPosition() const { return mRawXCursorPosition; } float getXCursorPosition() const; - inline float getRawYCursorPosition() const { return mYCursorPosition; } + inline float getRawYCursorPosition() const { return mRawYCursorPosition; } float getYCursorPosition() const; + void setCursorPosition(float x, float y); + static inline bool isValidCursorPosition(float x, float y) { return !isnan(x) && !isnan(y); } inline nsecs_t getDownTime() const { return mDownTime; } @@ -623,8 +625,8 @@ public: void initialize(int32_t deviceId, int32_t source, int32_t displayId, int32_t action, int32_t actionButton, int32_t flags, int32_t edgeFlags, int32_t metaState, int32_t buttonState, MotionClassification classification, float xOffset, - float yOffset, float xPrecision, float yPrecision, float mXCursorPosition, - float mYCursorPosition, nsecs_t downTime, nsecs_t eventTime, + float yOffset, float xPrecision, float yPrecision, float rawXCursorPosition, + float rawYCursorPosition, nsecs_t downTime, nsecs_t eventTime, size_t pointerCount, const PointerProperties* pointerProperties, const PointerCoords* pointerCoords); @@ -676,8 +678,8 @@ protected: float mYOffset; float mXPrecision; float mYPrecision; - float mXCursorPosition; - float mYCursorPosition; + float mRawXCursorPosition; + float mRawYCursorPosition; nsecs_t mDownTime; Vector mPointerProperties; Vector mSampleEventTimes; diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index dc4978b836..34b305e548 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -238,8 +238,8 @@ void MotionEvent::initialize(int32_t deviceId, int32_t source, int32_t displayId int32_t actionButton, int32_t flags, int32_t edgeFlags, int32_t metaState, int32_t buttonState, MotionClassification classification, float xOffset, float yOffset, - float xPrecision, float yPrecision, float xCursorPosition, - float yCursorPosition, nsecs_t downTime, nsecs_t eventTime, + float xPrecision, float yPrecision, float rawXCursorPosition, + float rawYCursorPosition, nsecs_t downTime, nsecs_t eventTime, size_t pointerCount, const PointerProperties* pointerProperties, const PointerCoords* pointerCoords) { InputEvent::initialize(deviceId, source, displayId); @@ -254,8 +254,8 @@ void MotionEvent::initialize(int32_t deviceId, int32_t source, int32_t displayId mYOffset = yOffset; mXPrecision = xPrecision; mYPrecision = yPrecision; - mXCursorPosition = xCursorPosition; - mYCursorPosition = yCursorPosition; + mRawXCursorPosition = rawXCursorPosition; + mRawYCursorPosition = rawYCursorPosition; mDownTime = downTime; mPointerProperties.clear(); mPointerProperties.appendArray(pointerProperties, pointerCount); @@ -277,8 +277,8 @@ void MotionEvent::copyFrom(const MotionEvent* other, bool keepHistory) { mYOffset = other->mYOffset; mXPrecision = other->mXPrecision; mYPrecision = other->mYPrecision; - mXCursorPosition = other->mXCursorPosition; - mYCursorPosition = other->mYCursorPosition; + mRawXCursorPosition = other->mRawXCursorPosition; + mRawYCursorPosition = other->mRawYCursorPosition; mDownTime = other->mDownTime; mPointerProperties = other->mPointerProperties; @@ -313,6 +313,11 @@ float MotionEvent::getYCursorPosition() const { return rawY + mYOffset; } +void MotionEvent::setCursorPosition(float x, float y) { + mRawXCursorPosition = x - mXOffset; + mRawYCursorPosition = y - mYOffset; +} + const PointerCoords* MotionEvent::getRawPointerCoords(size_t pointerIndex) const { return &mSamplePointerCoords[getHistorySize() * getPointerCount() + pointerIndex]; } @@ -433,12 +438,12 @@ void MotionEvent::transform(const float matrix[9]) { transformPoint(matrix, 0, 0, &originX, &originY); // Apply the transformation to cursor position. - if (isValidCursorPosition(mXCursorPosition, mYCursorPosition)) { - float x = mXCursorPosition + oldXOffset; - float y = mYCursorPosition + oldYOffset; + if (isValidCursorPosition(mRawXCursorPosition, mRawYCursorPosition)) { + float x = mRawXCursorPosition + oldXOffset; + float y = mRawYCursorPosition + oldYOffset; transformPoint(matrix, x, y, &x, &y); - mXCursorPosition = x - mXOffset; - mYCursorPosition = y - mYOffset; + mRawXCursorPosition = x - mXOffset; + mRawYCursorPosition = y - mYOffset; } // Apply the transformation to all samples. @@ -480,8 +485,8 @@ status_t MotionEvent::readFromParcel(Parcel* parcel) { mYOffset = parcel->readFloat(); mXPrecision = parcel->readFloat(); mYPrecision = parcel->readFloat(); - mXCursorPosition = parcel->readFloat(); - mYCursorPosition = parcel->readFloat(); + mRawXCursorPosition = parcel->readFloat(); + mRawYCursorPosition = parcel->readFloat(); mDownTime = parcel->readInt64(); mPointerProperties.clear(); @@ -533,8 +538,8 @@ status_t MotionEvent::writeToParcel(Parcel* parcel) const { parcel->writeFloat(mYOffset); parcel->writeFloat(mXPrecision); parcel->writeFloat(mYPrecision); - parcel->writeFloat(mXCursorPosition); - parcel->writeFloat(mYCursorPosition); + parcel->writeFloat(mRawXCursorPosition); + parcel->writeFloat(mRawYCursorPosition); parcel->writeInt64(mDownTime); for (size_t i = 0; i < pointerCount; i++) { diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp index b879de6a74..b90857c99c 100644 --- a/libs/input/tests/InputEvent_test.cpp +++ b/libs/input/tests/InputEvent_test.cpp @@ -665,4 +665,14 @@ TEST_F(MotionEventTest, Initialize_SetsCursorPosition) { ASSERT_EQ(600, event.getYCursorPosition()); } +TEST_F(MotionEventTest, SetCursorPosition) { + MotionEvent event; + initializeEventWithHistory(&event); + event.setSource(AINPUT_SOURCE_MOUSE); + + event.setCursorPosition(3, 4); + ASSERT_EQ(3, event.getXCursorPosition()); + ASSERT_EQ(4, event.getYCursorPosition()); +} + } // namespace android -- cgit v1.2.3-59-g8ed1b From 20e67fa8a171f7899c754bb7663f5e534d64c322 Mon Sep 17 00:00:00 2001 From: Dillon Cower Date: Tue, 30 Jul 2019 15:39:54 -0700 Subject: Fix typo: chroreographer -> choreographer. --- include/android/choreographer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/android/choreographer.h b/include/android/choreographer.h index 44883cc498..1b589bca72 100644 --- a/include/android/choreographer.h +++ b/include/android/choreographer.h @@ -83,7 +83,7 @@ void AChoreographer_postFrameCallbackDelayed(AChoreographer* choreographer, * Power a callback to be run on the next frame. The data pointer provided will * be passed to the callback function when it's called. */ -void AChoreographer_postFrameCallback64(AChoreographer* chroreographer, +void AChoreographer_postFrameCallback64(AChoreographer* choreographer, AChoreographer_frameCallback64 callback, void* data) __INTRODUCED_IN(29); /** -- cgit v1.2.3-59-g8ed1b From 8384682fd25b54921fd74288788f2a9299dd8dba Mon Sep 17 00:00:00 2001 From: Atif Niyaz Date: Thu, 18 Jul 2019 15:17:40 -0700 Subject: Seperate LatencyStatistics from InputReader Test: atest LatencyStatisticsTest Change-Id: I22a39cd5bef7fa9180bc1ee1fd9478a2cf872e83 --- include/input/LatencyStatistics.h | 59 +++++++++++++++++++ libs/input/Android.bp | 1 + libs/input/LatencyStatistics.cpp | 90 +++++++++++++++++++++++++++++ libs/input/tests/Android.bp | 1 + libs/input/tests/LatencyStatistics_test.cpp | 72 +++++++++++++++++++++++ services/inputflinger/InputReader.cpp | 21 +++---- services/inputflinger/InputReader.h | 72 ++--------------------- 7 files changed, 237 insertions(+), 79 deletions(-) create mode 100644 include/input/LatencyStatistics.h create mode 100644 libs/input/LatencyStatistics.cpp create mode 100644 libs/input/tests/LatencyStatistics_test.cpp (limited to 'include') diff --git a/include/input/LatencyStatistics.h b/include/input/LatencyStatistics.h new file mode 100644 index 0000000000..bd86266901 --- /dev/null +++ b/include/input/LatencyStatistics.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUT_STATISTICS_H +#define _UI_INPUT_STATISTICS_H + +#include + +#include + +namespace android { + +class LatencyStatistics { +private: + /* Minimum sample recorded */ + float mMin; + /* Maximum sample recorded */ + float mMax; + /* Sum of all samples recorded */ + float mSum; + /* Sum of all the squares of samples recorded */ + float mSum2; + /* Count of all samples recorded */ + size_t mCount; + /* The last time statistics were reported */ + std::chrono::steady_clock::time_point mLastReportTime; + /* Statistics Report Frequency */ + const std::chrono::seconds mReportPeriod; + +public: + LatencyStatistics(std::chrono::seconds period); + + void addValue(float); + void reset(); + bool shouldReport(); + + float getMean(); + float getMin(); + float getMax(); + float getStDev(); + size_t getCount(); +}; + +} // namespace android + +#endif // _UI_INPUT_STATISTICS_H diff --git a/libs/input/Android.bp b/libs/input/Android.bp index 2d788119cd..17138506e5 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -48,6 +48,7 @@ cc_library { "InputTransport.cpp", "InputWindow.cpp", "ISetInputWindowsListener.cpp", + "LatencyStatistics.cpp", "VelocityControl.cpp", "VelocityTracker.cpp", ], diff --git a/libs/input/LatencyStatistics.cpp b/libs/input/LatencyStatistics.cpp new file mode 100644 index 0000000000..e343578e00 --- /dev/null +++ b/libs/input/LatencyStatistics.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +#include +#include + +namespace android { + +LatencyStatistics::LatencyStatistics(std::chrono::seconds period) : mReportPeriod(period) { + reset(); +} + +/** + * Add a raw value to the statistics + */ +void LatencyStatistics::addValue(float value) { + if (value < mMin) { + mMin = value; + } + if (value > mMax) { + mMax = value; + } + mSum += value; + mSum2 += value * value; + mCount++; +} + +/** + * Get the mean. Should not be called if no samples have been added. + */ +float LatencyStatistics::getMean() { + return mSum / mCount; +} + +/** + * Get the standard deviation. Should not be called if no samples have been added. + */ +float LatencyStatistics::getStDev() { + float mean = getMean(); + return sqrt(mSum2 / mCount - mean * mean); +} + +float LatencyStatistics::getMin() { + return mMin; +} + +float LatencyStatistics::getMax() { + return mMax; +} + +size_t LatencyStatistics::getCount() { + return mCount; +} + +/** + * Reset internal state. The variable 'when' is the time when the data collection started. + * Call this to start a new data collection window. + */ +void LatencyStatistics::reset() { + mMax = std::numeric_limits::lowest(); + mMin = std::numeric_limits::max(); + mSum = 0; + mSum2 = 0; + mCount = 0; + mLastReportTime = std::chrono::steady_clock::now(); +} + +bool LatencyStatistics::shouldReport() { + std::chrono::duration timeSinceReport = std::chrono::steady_clock::now() - mLastReportTime; + return mCount != 0 && timeSinceReport > mReportPeriod; +} + +} // namespace android diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp index ade931e01a..c1c35e1b89 100644 --- a/libs/input/tests/Android.bp +++ b/libs/input/tests/Android.bp @@ -7,6 +7,7 @@ cc_test { "InputEvent_test.cpp", "InputPublisherAndConsumer_test.cpp", "InputWindow_test.cpp", + "LatencyStatistics_test.cpp", "TouchVideoFrame_test.cpp", "VelocityTracker_test.cpp", ], diff --git a/libs/input/tests/LatencyStatistics_test.cpp b/libs/input/tests/LatencyStatistics_test.cpp new file mode 100644 index 0000000000..6d1cab4187 --- /dev/null +++ b/libs/input/tests/LatencyStatistics_test.cpp @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +namespace android { +namespace test { + +TEST(LatencyStatisticsTest, ResetStats) { + LatencyStatistics stats{5min}; + stats.addValue(5.0); + stats.addValue(19.3); + stats.addValue(20); + stats.reset(); + + ASSERT_EQ(stats.getCount(), 0u); + ASSERT_EQ(std::isnan(stats.getStDev()), true); + ASSERT_EQ(std::isnan(stats.getMean()), true); +} + +TEST(LatencyStatisticsTest, AddStatsValue) { + LatencyStatistics stats{5min}; + stats.addValue(5.0); + + ASSERT_EQ(stats.getMin(), 5.0); + ASSERT_EQ(stats.getMax(), 5.0); + ASSERT_EQ(stats.getCount(), 1u); + ASSERT_EQ(stats.getMean(), 5.0); + ASSERT_EQ(stats.getStDev(), 0.0); +} + +TEST(LatencyStatisticsTest, AddMultipleStatsValue) { + LatencyStatistics stats{5min}; + stats.addValue(4.0); + stats.addValue(6.0); + stats.addValue(8.0); + stats.addValue(10.0); + + float stdev = stats.getStDev(); + + ASSERT_EQ(stats.getMin(), 4.0); + ASSERT_EQ(stats.getMax(), 10.0); + ASSERT_EQ(stats.getCount(), 4u); + ASSERT_EQ(stats.getMean(), 7.0); + ASSERT_EQ(stdev * stdev, 5.0); +} + +TEST(LatencyStatisticsTest, ShouldReportStats) { + LatencyStatistics stats{0min}; + stats.addValue(5.0); + + ASSERT_EQ(stats.shouldReport(), true); +} + +} // namespace test +} // namespace android \ No newline at end of file diff --git a/services/inputflinger/InputReader.cpp b/services/inputflinger/InputReader.cpp index eee49d5e2a..df5dcafec9 100644 --- a/services/inputflinger/InputReader.cpp +++ b/services/inputflinger/InputReader.cpp @@ -85,9 +85,6 @@ static constexpr nsecs_t TOUCH_DATA_TIMEOUT = ms2ns(20); // data. static constexpr nsecs_t STYLUS_DATA_LATENCY = ms2ns(10); -// How often to report input event statistics -static constexpr nsecs_t STATISTICS_REPORT_FREQUENCY = seconds_to_nanoseconds(5 * 60); - // --- Static Functions --- template @@ -4314,16 +4311,14 @@ void TouchInputMapper::clearStylusDataPendingFlags() { } void TouchInputMapper::reportEventForStatistics(nsecs_t evdevTime) { - nsecs_t now = systemTime(CLOCK_MONOTONIC); - nsecs_t latency = now - evdevTime; - mStatistics.addValue(nanoseconds_to_microseconds(latency)); - nsecs_t timeSinceLastReport = now - mStatistics.lastReportTime; - if (timeSinceLastReport > STATISTICS_REPORT_FREQUENCY) { - android::util::stats_write(android::util::TOUCH_EVENT_REPORTED, - mStatistics.min, mStatistics.max, - mStatistics.mean(), mStatistics.stdev(), mStatistics.count); - mStatistics.reset(now); - } + if (mStatistics.shouldReport()) { + android::util::stats_write(android::util::TOUCH_EVENT_REPORTED, mStatistics.getMin(), + mStatistics.getMax(), mStatistics.getMean(), + mStatistics.getStDev(), mStatistics.getCount()); + mStatistics.reset(); + } + nsecs_t latency = nanoseconds_to_microseconds(systemTime(CLOCK_MONOTONIC) - evdevTime); + mStatistics.addValue(latency); } void TouchInputMapper::process(const RawEvent* rawEvent) { diff --git a/services/inputflinger/InputReader.h b/services/inputflinger/InputReader.h index 0c08e7da38..33763b6cdf 100644 --- a/services/inputflinger/InputReader.h +++ b/services/inputflinger/InputReader.h @@ -24,14 +24,15 @@ #include #include +#include #include #include #include -#include +#include #include +#include #include #include -#include #include #include @@ -569,69 +570,6 @@ struct CookedPointerData { } }; -/** - * Basic statistics information. - * Keep track of min, max, average, and standard deviation of the received samples. - * Used to report latency information about input events. - */ -struct LatencyStatistics { - float min; - float max; - // Sum of all samples - float sum; - // Sum of squares of all samples - float sum2; - // The number of samples - size_t count; - // The last time statistics were reported. - nsecs_t lastReportTime; - - LatencyStatistics() { - reset(systemTime(SYSTEM_TIME_MONOTONIC)); - } - - inline void addValue(float x) { - if (x < min) { - min = x; - } - if (x > max) { - max = x; - } - sum += x; - sum2 += x * x; - count++; - } - - // Get the average value. Should not be called if no samples have been added. - inline float mean() { - if (count == 0) { - return 0; - } - return sum / count; - } - - // Get the standard deviation. Should not be called if no samples have been added. - inline float stdev() { - if (count == 0) { - return 0; - } - float average = mean(); - return sqrt(sum2 / count - average * average); - } - - /** - * Reset internal state. The variable 'when' is the time when the data collection started. - * Call this to start a new data collection window. - */ - inline void reset(nsecs_t when) { - max = 0; - min = std::numeric_limits::max(); - sum = 0; - sum2 = 0; - count = 0; - lastReportTime = when; - } -}; /* Keeps track of the state of single-touch protocol. */ class SingleTouchMotionAccumulator { @@ -1571,8 +1509,10 @@ private: VelocityControl mWheelXVelocityControl; VelocityControl mWheelYVelocityControl; + static constexpr std::chrono::duration STATS_REPORT_PERIOD = 5min; + // Latency statistics for touch events - struct LatencyStatistics mStatistics; + LatencyStatistics mStatistics{STATS_REPORT_PERIOD}; std::optional findViewport(); -- cgit v1.2.3-59-g8ed1b From 3d3fa52c8bbe481ffb87d75e24725444bef0b939 Mon Sep 17 00:00:00 2001 From: Atif Niyaz Date: Thu, 25 Jul 2019 11:12:39 -0700 Subject: Move LatencyStatistics collection from InputReader to InputTransport This move allows us to grab a bigger context of how much time a motion event is spent from the kernel to right before the event was published. Test: Modified LatencyStats to report ever 10 seconds. Utilized logs to check that logs were reporting every 10 seconds. Checked also using logs that data was being added and calculated correctly. Change-Id: Iee0f9a2155e93ae77de5a5cd8b9fd1506186c60f Signed-off-by: Atif Niyaz --- include/input/InputTransport.h | 12 ++++++++++-- libs/input/Android.bp | 3 ++- libs/input/InputTransport.cpp | 16 ++++++++++++++++ services/inputflinger/Android.bp | 1 - services/inputflinger/InputReader.cpp | 13 ------------- services/inputflinger/InputReader.h | 8 -------- 6 files changed, 28 insertions(+), 25 deletions(-) (limited to 'include') diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h index df23f613c8..690e0a11c8 100644 --- a/include/input/InputTransport.h +++ b/include/input/InputTransport.h @@ -31,13 +31,16 @@ #include +#include + #include #include +#include +#include #include -#include #include +#include #include -#include namespace android { class Parcel; @@ -286,7 +289,12 @@ public: status_t receiveFinishedSignal(uint32_t* outSeq, bool* outHandled); private: + static constexpr std::chrono::duration TOUCH_STATS_REPORT_PERIOD = 5min; + sp mChannel; + LatencyStatistics mTouchStatistics{TOUCH_STATS_REPORT_PERIOD}; + + void reportTouchEventForStatistics(nsecs_t evdevTime); }; /* diff --git a/libs/input/Android.bp b/libs/input/Android.bp index 17138506e5..7749e66c4d 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -56,7 +56,8 @@ cc_library { shared_libs: [ "libutils", "libbinder", - "libui" + "libui", + "libstatslog", ], sanitize: { diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp index 904a6feb03..2ff301e270 100644 --- a/libs/input/InputTransport.cpp +++ b/libs/input/InputTransport.cpp @@ -34,6 +34,7 @@ #include #include +#include using android::base::StringPrintf; @@ -531,6 +532,10 @@ status_t InputPublisher::publishMotionEvent( msg.body.motion.pointers[i].properties.copyFrom(pointerProperties[i]); msg.body.motion.pointers[i].coords.copyFrom(pointerCoords[i]); } + + if (source == AINPUT_SOURCE_TOUCHSCREEN) { + reportTouchEventForStatistics(eventTime); + } return mChannel->sendMessage(&msg); } @@ -557,6 +562,17 @@ status_t InputPublisher::receiveFinishedSignal(uint32_t* outSeq, bool* outHandle return OK; } +void InputPublisher::reportTouchEventForStatistics(nsecs_t evdevTime) { + if (mTouchStatistics.shouldReport()) { + android::util::stats_write(android::util::TOUCH_EVENT_REPORTED, mTouchStatistics.getMin(), + mTouchStatistics.getMax(), mTouchStatistics.getMean(), + mTouchStatistics.getStDev(), mTouchStatistics.getCount()); + mTouchStatistics.reset(); + } + nsecs_t latency = nanoseconds_to_microseconds(systemTime(CLOCK_MONOTONIC) - evdevTime); + mTouchStatistics.addValue(latency); +} + // --- InputConsumer --- InputConsumer::InputConsumer(const sp& channel) : diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp index 8dd4d1df63..bdee6fe043 100644 --- a/services/inputflinger/Android.bp +++ b/services/inputflinger/Android.bp @@ -88,7 +88,6 @@ cc_library_shared { "libui", "libutils", "libhardware_legacy", - "libstatslog", ], header_libs: [ diff --git a/services/inputflinger/InputReader.cpp b/services/inputflinger/InputReader.cpp index df5dcafec9..d565e3ab38 100644 --- a/services/inputflinger/InputReader.cpp +++ b/services/inputflinger/InputReader.cpp @@ -57,7 +57,6 @@ #include #include #include -#include #define INDENT " " #define INDENT2 " " @@ -4310,24 +4309,12 @@ void TouchInputMapper::clearStylusDataPendingFlags() { mExternalStylusFusionTimeout = LLONG_MAX; } -void TouchInputMapper::reportEventForStatistics(nsecs_t evdevTime) { - if (mStatistics.shouldReport()) { - android::util::stats_write(android::util::TOUCH_EVENT_REPORTED, mStatistics.getMin(), - mStatistics.getMax(), mStatistics.getMean(), - mStatistics.getStDev(), mStatistics.getCount()); - mStatistics.reset(); - } - nsecs_t latency = nanoseconds_to_microseconds(systemTime(CLOCK_MONOTONIC) - evdevTime); - mStatistics.addValue(latency); -} - void TouchInputMapper::process(const RawEvent* rawEvent) { mCursorButtonAccumulator.process(rawEvent); mCursorScrollAccumulator.process(rawEvent); mTouchButtonAccumulator.process(rawEvent); if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) { - reportEventForStatistics(rawEvent->when); sync(rawEvent->when); } } diff --git a/services/inputflinger/InputReader.h b/services/inputflinger/InputReader.h index 33763b6cdf..e434869996 100644 --- a/services/inputflinger/InputReader.h +++ b/services/inputflinger/InputReader.h @@ -24,7 +24,6 @@ #include #include -#include #include #include #include @@ -1509,11 +1508,6 @@ private: VelocityControl mWheelXVelocityControl; VelocityControl mWheelYVelocityControl; - static constexpr std::chrono::duration STATS_REPORT_PERIOD = 5min; - - // Latency statistics for touch events - LatencyStatistics mStatistics{STATS_REPORT_PERIOD}; - std::optional findViewport(); void resetExternalStylus(); @@ -1582,8 +1576,6 @@ private: static void assignPointerIds(const RawState* last, RawState* current); - void reportEventForStatistics(nsecs_t evdevTime); - const char* modeToString(DeviceMode deviceMode); }; -- cgit v1.2.3-59-g8ed1b From f73f5f2b3b9da9e09cc76cf72323aaf2926a5b04 Mon Sep 17 00:00:00 2001 From: Leon Scroggins III Date: Mon, 12 Aug 2019 13:43:55 -0400 Subject: Add RGBA_F16 to the AndroidBitmapFormat (NDK) Bug: 135133301 Test: Id904c8cb4b22505635744ab271a639ace24ddbab Update AndroidBitmapFormat to keep up with Bitmap.Config. Change-Id: I3c17faf174b109b7c31c2eb5d1249bc881923b10 --- include/android/bitmap.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/android/bitmap.h b/include/android/bitmap.h index 2def64dc90..6fba0ac9ca 100644 --- a/include/android/bitmap.h +++ b/include/android/bitmap.h @@ -60,6 +60,8 @@ enum AndroidBitmapFormat { ANDROID_BITMAP_FORMAT_RGBA_4444 = 7, /** Alpha: 8 bits. */ ANDROID_BITMAP_FORMAT_A_8 = 8, + /** Each component is stored as a half float. **/ + ANDROID_BITMAP_FORMAT_RGBA_F16 = 9, }; /** Bitmap info, see AndroidBitmap_getInfo(). */ -- cgit v1.2.3-59-g8ed1b From 2ccbe3a0359a1bd59d282400351f2e604d5aabb9 Mon Sep 17 00:00:00 2001 From: Josh Gao Date: Fri, 9 Aug 2019 14:35:36 -0700 Subject: InputTransport: store fd in a unique_fd. Use unique_fd to hold the file descriptor, so that it gets protected from being closed by someone else by fdsan. Test: atest libinput_tests inputflinger_tests Change-Id: I08df199294f9ddd7646c7bcd637b9c035e3c1e12 --- include/input/InputTransport.h | 14 +++--- libs/input/IInputFlinger.cpp | 6 +-- libs/input/InputTransport.cpp | 88 +++++++++++++++++----------------- libs/input/tests/InputChannel_test.cpp | 27 +++++++---- 4 files changed, 70 insertions(+), 65 deletions(-) (limited to 'include') diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h index 690e0a11c8..28b8d80074 100644 --- a/include/input/InputTransport.h +++ b/include/input/InputTransport.h @@ -42,6 +42,8 @@ #include #include +#include + namespace android { class Parcel; @@ -166,8 +168,7 @@ protected: virtual ~InputChannel(); public: - InputChannel() = default; - InputChannel(const std::string& name, int fd); + static sp create(const std::string& name, android::base::unique_fd fd); /* Creates a pair of input channels. * @@ -177,7 +178,7 @@ public: sp& outServerChannel, sp& outClientChannel); inline std::string getName() const { return mName; } - inline int getFd() const { return mFd; } + inline int getFd() const { return mFd.get(); } /* Sends a message to the other endpoint. * @@ -208,16 +209,15 @@ public: sp dup() const; status_t write(Parcel& out) const; - status_t read(const Parcel& from); + static sp read(const Parcel& from); sp getToken() const; void setToken(const sp& token); private: - void setFd(int fd); - + InputChannel(const std::string& name, android::base::unique_fd fd); std::string mName; - int mFd = -1; + android::base::unique_fd mFd; sp mToken = nullptr; }; diff --git a/libs/input/IInputFlinger.cpp b/libs/input/IInputFlinger.cpp index d6a73bfd27..90f6e09a69 100644 --- a/libs/input/IInputFlinger.cpp +++ b/libs/input/IInputFlinger.cpp @@ -92,15 +92,13 @@ status_t BnInputFlinger::onTransact( } case REGISTER_INPUT_CHANNEL_TRANSACTION: { CHECK_INTERFACE(IInputFlinger, data, reply); - sp channel = new InputChannel(); - channel->read(data); + sp channel = InputChannel::read(data); registerInputChannel(channel); break; } case UNREGISTER_INPUT_CHANNEL_TRANSACTION: { CHECK_INTERFACE(IInputFlinger, data, reply); - sp channel = new InputChannel(); - channel->read(data); + sp channel = InputChannel::read(data); unregisterInputChannel(channel); break; } diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp index 2ff301e270..7835651f11 100644 --- a/libs/input/InputTransport.cpp +++ b/libs/input/InputTransport.cpp @@ -227,35 +227,28 @@ void InputMessage::getSanitizedCopy(InputMessage* msg) const { // --- InputChannel --- -InputChannel::InputChannel(const std::string& name, int fd) : - mName(name) { +sp InputChannel::create(const std::string& name, android::base::unique_fd fd) { + const int result = fcntl(fd, F_SETFL, O_NONBLOCK); + if (result != 0) { + LOG_ALWAYS_FATAL("channel '%s' ~ Could not make socket non-blocking: %s", name.c_str(), + strerror(errno)); + return nullptr; + } + return new InputChannel(name, std::move(fd)); +} + +InputChannel::InputChannel(const std::string& name, android::base::unique_fd fd) + : mName(name), mFd(std::move(fd)) { #if DEBUG_CHANNEL_LIFECYCLE ALOGD("Input channel constructed: name='%s', fd=%d", mName.c_str(), fd); #endif - - setFd(fd); } InputChannel::~InputChannel() { #if DEBUG_CHANNEL_LIFECYCLE - ALOGD("Input channel destroyed: name='%s', fd=%d", - mName.c_str(), mFd); + ALOGD("Input channel destroyed: name='%s', fd=%d", mName.c_str(), mFd.get()); #endif - - ::close(mFd); -} - -void InputChannel::setFd(int fd) { - if (mFd > 0) { - ::close(mFd); - } - mFd = fd; - if (mFd > 0) { - int result = fcntl(mFd, F_SETFL, O_NONBLOCK); - LOG_ALWAYS_FATAL_IF(result != 0, "channel '%s' ~ Could not make socket " - "non-blocking. errno=%d", mName.c_str(), errno); - } } status_t InputChannel::openInputChannelPair(const std::string& name, @@ -276,13 +269,13 @@ status_t InputChannel::openInputChannelPair(const std::string& name, setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize)); setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize)); - std::string serverChannelName = name; - serverChannelName += " (server)"; - outServerChannel = new InputChannel(serverChannelName, sockets[0]); + std::string serverChannelName = name + " (server)"; + android::base::unique_fd serverFd(sockets[0]); + outServerChannel = InputChannel::create(serverChannelName, std::move(serverFd)); - std::string clientChannelName = name; - clientChannelName += " (client)"; - outClientChannel = new InputChannel(clientChannelName, sockets[1]); + std::string clientChannelName = name + " (client)"; + android::base::unique_fd clientFd(sockets[1]); + outClientChannel = InputChannel::create(clientChannelName, std::move(clientFd)); return OK; } @@ -292,7 +285,7 @@ status_t InputChannel::sendMessage(const InputMessage* msg) { msg->getSanitizedCopy(&cleanMsg); ssize_t nWrite; do { - nWrite = ::send(mFd, &cleanMsg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL); + nWrite = ::send(mFd.get(), &cleanMsg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL); } while (nWrite == -1 && errno == EINTR); if (nWrite < 0) { @@ -327,7 +320,7 @@ status_t InputChannel::sendMessage(const InputMessage* msg) { status_t InputChannel::receiveMessage(InputMessage* msg) { ssize_t nRead; do { - nRead = ::recv(mFd, msg, sizeof(InputMessage), MSG_DONTWAIT); + nRead = ::recv(mFd.get(), msg, sizeof(InputMessage), MSG_DONTWAIT); } while (nRead == -1 && errno == EINTR); if (nRead < 0) { @@ -365,39 +358,44 @@ status_t InputChannel::receiveMessage(InputMessage* msg) { } sp InputChannel::dup() const { - int fd = ::dup(getFd()); - return fd >= 0 ? new InputChannel(getName(), fd) : nullptr; + android::base::unique_fd newFd(::dup(getFd())); + if (!newFd.ok()) { + ALOGE("Could not duplicate fd %i for channel %s: %s", getFd(), mName.c_str(), + strerror(errno)); + return nullptr; + } + return InputChannel::create(mName, std::move(newFd)); } - status_t InputChannel::write(Parcel& out) const { - status_t s = out.writeString8(String8(getName().c_str())); - + status_t s = out.writeCString(getName().c_str()); if (s != OK) { return s; } + s = out.writeStrongBinder(mToken); if (s != OK) { return s; } - s = out.writeDupFileDescriptor(getFd()); - + s = out.writeUniqueFileDescriptor(mFd); return s; } -status_t InputChannel::read(const Parcel& from) { - mName = from.readString8(); - mToken = from.readStrongBinder(); - - int rawFd = from.readFileDescriptor(); - setFd(::dup(rawFd)); - - if (mFd < 0) { - return BAD_VALUE; +sp InputChannel::read(const Parcel& from) { + std::string name = from.readCString(); + sp token = from.readStrongBinder(); + android::base::unique_fd rawFd; + status_t fdResult = from.readUniqueFileDescriptor(&rawFd); + if (fdResult != OK) { + return nullptr; } - return OK; + sp channel = InputChannel::create(name, std::move(rawFd)); + if (channel != nullptr) { + channel->setToken(token); + } + return channel; } sp InputChannel::getToken() const { diff --git a/libs/input/tests/InputChannel_test.cpp b/libs/input/tests/InputChannel_test.cpp index f1675c0d36..af74edd65d 100644 --- a/libs/input/tests/InputChannel_test.cpp +++ b/libs/input/tests/InputChannel_test.cpp @@ -22,11 +22,12 @@ #include #include +#include #include #include -#include #include #include +#include namespace android { @@ -43,20 +44,28 @@ TEST_F(InputChannelTest, ConstructorAndDestructor_TakesOwnershipOfFileDescriptor // of a pipe and to check for EPIPE on the other end after the channel is destroyed. Pipe pipe; - sp inputChannel = new InputChannel("channel name", pipe.sendFd); + android::base::unique_fd sendFd(pipe.sendFd); + + sp inputChannel = InputChannel::create("channel name", std::move(sendFd)); + EXPECT_NE(inputChannel, nullptr) << "channel should be successfully created"; EXPECT_STREQ("channel name", inputChannel->getName().c_str()) << "channel should have provided name"; - EXPECT_EQ(pipe.sendFd, inputChannel->getFd()) - << "channel should have provided fd"; + EXPECT_NE(-1, inputChannel->getFd()) << "channel should have valid fd"; - inputChannel.clear(); // destroys input channel + // InputChannel should be the owner of the file descriptor now + ASSERT_FALSE(sendFd.ok()); +} - EXPECT_EQ(-EPIPE, pipe.readSignal()) - << "channel should have closed fd when destroyed"; +TEST_F(InputChannelTest, SetAndGetToken) { + Pipe pipe; + sp channel = + InputChannel::create("test channel", android::base::unique_fd(pipe.sendFd)); + EXPECT_EQ(channel->getToken(), nullptr); - // clean up fds of Pipe endpoints that were closed so we don't try to close them again - pipe.sendFd = -1; + sp token = new BBinder(); + channel->setToken(token); + EXPECT_EQ(token, channel->getToken()); } TEST_F(InputChannelTest, OpenInputChannelPair_ReturnsAPairOfConnectedChannels) { -- cgit v1.2.3-59-g8ed1b From 3578917f82c84ad1be334105624084c7b1c95b54 Mon Sep 17 00:00:00 2001 From: Garfield Tan Date: Mon, 9 Sep 2019 16:50:14 -0700 Subject: Add SCROLL, RELATIVE_X and RELATIVE_Y axes labels. CTS tests then can use this to obtain axis value and sets them. Bug: 133368243 Test: We can obtain values of these axes. Change-Id: I2d94c9345639feb40c8486e07535bfd6fe059066 --- include/input/InputEventLabels.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'include') diff --git a/include/input/InputEventLabels.h b/include/input/InputEventLabels.h index 59d16d15af..eaa562bb7b 100644 --- a/include/input/InputEventLabels.h +++ b/include/input/InputEventLabels.h @@ -359,6 +359,9 @@ static const InputEventLabel AXES[] = { DEFINE_AXIS(BRAKE), DEFINE_AXIS(DISTANCE), DEFINE_AXIS(TILT), + DEFINE_AXIS(SCROLL), + DEFINE_AXIS(RELATIVE_X), + DEFINE_AXIS(RELATIVE_Y), DEFINE_AXIS(GENERIC_1), DEFINE_AXIS(GENERIC_2), DEFINE_AXIS(GENERIC_3), -- cgit v1.2.3-59-g8ed1b From 6cbb97591535868d629a562dfd7d8e24865cf551 Mon Sep 17 00:00:00 2001 From: Arthur Hung Date: Thu, 5 Sep 2019 16:38:18 +0800 Subject: Revert "Fix drag and drop (2/3)" This reverts commit fbe5d9c4233dcd802a122f70ca5a3641fcd556ca. Bug: 137819199 Test: manual Change-Id: I7afec569519b9c69eb03225672db6db141b20241 --- include/input/IInputFlinger.h | 4 +-- libs/gui/tests/EndToEndNativeInputTest.cpp | 50 ------------------------------ libs/input/IInputFlinger.cpp | 17 ---------- services/inputflinger/InputManager.cpp | 4 --- services/inputflinger/InputManager.h | 1 - services/inputflinger/host/InputFlinger.h | 1 - services/surfaceflinger/SurfaceFlinger.cpp | 15 +-------- services/surfaceflinger/SurfaceFlinger.h | 1 - 8 files changed, 2 insertions(+), 91 deletions(-) (limited to 'include') diff --git a/include/input/IInputFlinger.h b/include/input/IInputFlinger.h index 4365a3c4e3..d23e3b7767 100644 --- a/include/input/IInputFlinger.h +++ b/include/input/IInputFlinger.h @@ -37,7 +37,6 @@ public: virtual void setInputWindows(const std::vector& inputHandles, const sp& setInputWindowsListener) = 0; - virtual void transferTouchFocus(const sp& fromToken, const sp& toToken) = 0; virtual void registerInputChannel(const sp& channel) = 0; virtual void unregisterInputChannel(const sp& channel) = 0; }; @@ -51,8 +50,7 @@ public: enum { SET_INPUT_WINDOWS_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION, REGISTER_INPUT_CHANNEL_TRANSACTION, - UNREGISTER_INPUT_CHANNEL_TRANSACTION, - TRANSFER_TOUCH_FOCUS + UNREGISTER_INPUT_CHANNEL_TRANSACTION }; virtual status_t onTransact(uint32_t code, const Parcel& data, diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp index 386f731d23..f158e566e6 100644 --- a/libs/gui/tests/EndToEndNativeInputTest.cpp +++ b/libs/gui/tests/EndToEndNativeInputTest.cpp @@ -133,27 +133,6 @@ public: EXPECT_EQ(AMOTION_EVENT_ACTION_UP, mev->getAction()); } - void expectMotionEvent(int motionEventType, int x, int y) { - InputEvent *ev = consumeEvent(); - ASSERT_NE(ev, nullptr); - ASSERT_EQ(ev->getType(), AINPUT_EVENT_TYPE_MOTION); - MotionEvent *mev = static_cast(ev); - EXPECT_EQ(motionEventType, mev->getAction()); - EXPECT_EQ(x, mev->getX(0)); - EXPECT_EQ(y, mev->getY(0)); - } - - void expectNoMotionEvent(int motionEventType) { - InputEvent *ev = consumeEvent(); - if (ev == nullptr || ev->getType() != AINPUT_EVENT_TYPE_MOTION) { - // Didn't find an event or a motion event so assume action didn't occur. - return; - } - - MotionEvent *mev = static_cast(ev); - EXPECT_NE(motionEventType, mev->getAction()); - } - ~InputSurface() { mInputFlinger->unregisterInputChannel(mServerChannel); } @@ -278,15 +257,6 @@ void injectTap(int x, int y) { } } -void injectMotionEvent(std::string event, int x, int y) { - char *buf1, *buf2; - asprintf(&buf1, "%d", x); - asprintf(&buf2, "%d", y); - if (fork() == 0) { - execlp("input", "input", "motionevent", event.c_str(), buf1, buf2, NULL); - } -} - TEST_F(InputSurfacesTest, can_receive_input) { std::unique_ptr surface = makeSurface(100, 100); surface->showAt(100, 100); @@ -504,26 +474,6 @@ TEST_F(InputSurfacesTest, input_respects_container_layer_visiblity) { bgSurface->expectTap(1, 1); } -TEST_F(InputSurfacesTest, transfer_touch_focus) { - std::unique_ptr fromSurface = makeSurface(100, 100); - - fromSurface->showAt(10, 10); - injectMotionEvent("DOWN", 11, 11); - fromSurface->expectMotionEvent(AMOTION_EVENT_ACTION_DOWN, 1, 1); - - std::unique_ptr toSurface = makeSurface(100, 100); - toSurface->showAt(10, 10); - - sp fromToken = fromSurface->mServerChannel->getToken(); - sp toToken = toSurface->mServerChannel->getToken(); - SurfaceComposerClient::Transaction t; - t.transferTouchFocus(fromToken, toToken).apply(true); - - injectMotionEvent("UP", 11, 11); - toSurface->expectMotionEvent(AMOTION_EVENT_ACTION_UP, 1, 1); - fromSurface->expectNoMotionEvent(AMOTION_EVENT_ACTION_UP); -} - TEST_F(InputSurfacesTest, input_respects_outscreen) { std::unique_ptr surface = makeSurface(100, 100); surface->showAt(-1, -1); diff --git a/libs/input/IInputFlinger.cpp b/libs/input/IInputFlinger.cpp index 90f6e09a69..8ec51653a8 100644 --- a/libs/input/IInputFlinger.cpp +++ b/libs/input/IInputFlinger.cpp @@ -45,16 +45,6 @@ public: IBinder::FLAG_ONEWAY); } - virtual void transferTouchFocus(const sp& fromToken, const sp& toToken) { - Parcel data, reply; - data.writeInterfaceToken(IInputFlinger::getInterfaceDescriptor()); - - data.writeStrongBinder(fromToken); - data.writeStrongBinder(toToken); - remote()->transact(BnInputFlinger::TRANSFER_TOUCH_FOCUS, data, &reply, - IBinder::FLAG_ONEWAY); - } - virtual void registerInputChannel(const sp& channel) { Parcel data, reply; data.writeInterfaceToken(IInputFlinger::getInterfaceDescriptor()); @@ -102,13 +92,6 @@ status_t BnInputFlinger::onTransact( unregisterInputChannel(channel); break; } - case TRANSFER_TOUCH_FOCUS: { - CHECK_INTERFACE(IInputFlinger, data, reply); - sp fromToken = data.readStrongBinder(); - sp toToken = data.readStrongBinder(); - transferTouchFocus(fromToken, toToken); - break; - } default: return BBinder::onTransact(code, data, reply, flags); } diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp index 3996cca646..acb53be69e 100644 --- a/services/inputflinger/InputManager.cpp +++ b/services/inputflinger/InputManager.cpp @@ -117,10 +117,6 @@ void InputManager::setInputWindows(const std::vector& infos, } } -void InputManager::transferTouchFocus(const sp& fromToken, const sp& toToken) { - mDispatcher->transferTouchFocus(fromToken, toToken); -} - // Used by tests only. void InputManager::registerInputChannel(const sp& channel) { IPCThreadState* ipc = IPCThreadState::self(); diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h index e568df54df..32f7ae0058 100644 --- a/services/inputflinger/InputManager.h +++ b/services/inputflinger/InputManager.h @@ -96,7 +96,6 @@ public: virtual void setInputWindows(const std::vector& handles, const sp& setInputWindowsListener); - virtual void transferTouchFocus(const sp& fromToken, const sp& toToken); virtual void registerInputChannel(const sp& channel); virtual void unregisterInputChannel(const sp& channel); diff --git a/services/inputflinger/host/InputFlinger.h b/services/inputflinger/host/InputFlinger.h index d8b352cbc6..973b4f92fa 100644 --- a/services/inputflinger/host/InputFlinger.h +++ b/services/inputflinger/host/InputFlinger.h @@ -42,7 +42,6 @@ public: virtual status_t dump(int fd, const Vector& args); void setInputWindows(const std::vector&, const sp&) {} - void transferTouchFocus(const sp&, const sp&) {} void registerInputChannel(const sp&) {} void unregisterInputChannel(const sp&) {} diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 3498419cdf..a35426e8d4 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -2606,7 +2606,7 @@ void SurfaceFlinger::updateInputFlinger() { setInputWindowsFinished(); } - executeInputWindowCommands(); + mInputWindowCommands.clear(); } void SurfaceFlinger::updateInputWindowInfo() { @@ -2630,19 +2630,6 @@ void SurfaceFlinger::commitInputWindowCommands() { mPendingInputWindowCommands.clear(); } -void SurfaceFlinger::executeInputWindowCommands() { - for (const auto& transferTouchFocusCommand : mInputWindowCommands.transferTouchFocusCommands) { - if (transferTouchFocusCommand.fromToken != nullptr && - transferTouchFocusCommand.toToken != nullptr && - transferTouchFocusCommand.fromToken != transferTouchFocusCommand.toToken) { - mInputFlinger->transferTouchFocus(transferTouchFocusCommand.fromToken, - transferTouchFocusCommand.toToken); - } - } - - mInputWindowCommands.clear(); -} - void SurfaceFlinger::updateCursorAsync() { compositionengine::CompositionRefreshArgs refreshArgs; diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index f220c26bdf..e0259c80ec 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -551,7 +551,6 @@ private: void updateInputFlinger(); void updateInputWindowInfo(); void commitInputWindowCommands() REQUIRES(mStateLock); - void executeInputWindowCommands(); void setInputWindowsFinished(); void updateCursorAsync(); -- cgit v1.2.3-59-g8ed1b From 0dd622ef5da2207ae4af7819ba2d20151e49e9e9 Mon Sep 17 00:00:00 2001 From: Leon Scroggins III Date: Fri, 23 Aug 2019 15:44:21 -0400 Subject: Add alpha to AndroidBitmapInfo Bug: 135133301 Test: If8859321c1b7e5149029f931e4d11ec6eeeb260a Change-Id: I3577602444837b703fcbf6498bd00e1ee1c97e09 --- include/android/bitmap.h | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/android/bitmap.h b/include/android/bitmap.h index 6fba0ac9ca..01cf2f88ea 100644 --- a/include/android/bitmap.h +++ b/include/android/bitmap.h @@ -64,6 +64,20 @@ enum AndroidBitmapFormat { ANDROID_BITMAP_FORMAT_RGBA_F16 = 9, }; +/** Bitmap alpha format */ +enum { + /** Pixel components are premultiplied by alpha. */ + ANDROID_BITMAP_FLAGS_ALPHA_PREMUL = 0, + /** Pixels are opaque. */ + ANDROID_BITMAP_FLAGS_ALPHA_OPAQUE = 1, + /** Pixel components are independent of alpha. */ + ANDROID_BITMAP_FLAGS_ALPHA_UNPREMUL = 2, + /** Bit mask for AndroidBitmapFormat.flags to isolate the alpha. */ + ANDROID_BITMAP_FLAGS_ALPHA_MASK = 0x3, + /** Shift for AndroidBitmapFormat.flags to isolate the alpha. */ + ANDROID_BITMAP_FLAGS_ALPHA_SHIFT = 0, +}; + /** Bitmap info, see AndroidBitmap_getInfo(). */ typedef struct { /** The bitmap width in pixels. */ @@ -74,8 +88,9 @@ typedef struct { uint32_t stride; /** The bitmap pixel format. See {@link AndroidBitmapFormat} */ int32_t format; - /** Unused. */ - uint32_t flags; // 0 for now + /** Two bits are used to encode alpha. Use ANDROID_BITMAP_FLAGS_ALPHA_MASK + * and ANDROID_BITMAP_FLAGS_ALPHA_SHIFT to retrieve them. */ + uint32_t flags; } AndroidBitmapInfo; /** -- cgit v1.2.3-59-g8ed1b From de4bf150c5685643daa1fa17f697e8b1cf3ea9ec Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Fri, 16 Aug 2019 11:12:52 -0500 Subject: Do not report latency for injected events When reporting the latency of touch events, ensure that injected events are excluded. These events can have arbitrary timestamps, and would not result in meaningful data. Also do not report events for statistics if inputfilter is enabled. Move the statistics reporting from InputTransport to InputDispatcher. This ensures that there's only 1 instance of the mStatistics object. This also provides easy access to the inputfilterenabled state. Bug: 13894199 Test: Change the reporting period to 0 (to report every event immediately) Inject events in various ways and ensure they don't go to statsd $ m statsd_testdrive && ./out/host/linux-x86/bin/statsd_testdrive 34 $ adb shell input tap 100 100 $ adb shell monkey 1000 Next, relaunch the statsd_testdrive script and touch the screen Observe that events are reported. Change-Id: Ief8040599a347e084e75584ed3164c60a6dbc4ad --- include/input/InputTransport.h | 5 ---- libs/input/Android.bp | 1 - libs/input/InputTransport.cpp | 15 ----------- libs/input/LatencyStatistics.cpp | 2 +- services/inputflinger/Android.bp | 1 + services/inputflinger/dispatcher/Android.bp | 1 + services/inputflinger/dispatcher/Entry.h | 18 +++++++++++++ .../inputflinger/dispatcher/InputDispatcher.cpp | 30 ++++++++++++++++++++++ services/inputflinger/dispatcher/InputDispatcher.h | 5 ++++ services/inputflinger/dispatcher/InputState.h | 3 --- 10 files changed, 56 insertions(+), 25 deletions(-) (limited to 'include') diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h index 28b8d80074..c056c972d2 100644 --- a/include/input/InputTransport.h +++ b/include/input/InputTransport.h @@ -35,7 +35,6 @@ #include #include -#include #include #include #include @@ -289,12 +288,8 @@ public: status_t receiveFinishedSignal(uint32_t* outSeq, bool* outHandled); private: - static constexpr std::chrono::duration TOUCH_STATS_REPORT_PERIOD = 5min; sp mChannel; - LatencyStatistics mTouchStatistics{TOUCH_STATS_REPORT_PERIOD}; - - void reportTouchEventForStatistics(nsecs_t evdevTime); }; /* diff --git a/libs/input/Android.bp b/libs/input/Android.bp index 7749e66c4d..8efaf3d90b 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -57,7 +57,6 @@ cc_library { "libutils", "libbinder", "libui", - "libstatslog", ], sanitize: { diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp index 8a2fc2a2d0..366c93cf1f 100644 --- a/libs/input/InputTransport.cpp +++ b/libs/input/InputTransport.cpp @@ -34,7 +34,6 @@ #include #include -#include using android::base::StringPrintf; @@ -538,9 +537,6 @@ status_t InputPublisher::publishMotionEvent( msg.body.motion.pointers[i].coords.copyFrom(pointerCoords[i]); } - if (source == AINPUT_SOURCE_TOUCHSCREEN) { - reportTouchEventForStatistics(eventTime); - } return mChannel->sendMessage(&msg); } @@ -567,17 +563,6 @@ status_t InputPublisher::receiveFinishedSignal(uint32_t* outSeq, bool* outHandle return OK; } -void InputPublisher::reportTouchEventForStatistics(nsecs_t evdevTime) { - if (mTouchStatistics.shouldReport()) { - android::util::stats_write(android::util::TOUCH_EVENT_REPORTED, mTouchStatistics.getMin(), - mTouchStatistics.getMax(), mTouchStatistics.getMean(), - mTouchStatistics.getStDev(), mTouchStatistics.getCount()); - mTouchStatistics.reset(); - } - nsecs_t latency = nanoseconds_to_microseconds(systemTime(CLOCK_MONOTONIC) - evdevTime); - mTouchStatistics.addValue(latency); -} - // --- InputConsumer --- InputConsumer::InputConsumer(const sp& channel) : diff --git a/libs/input/LatencyStatistics.cpp b/libs/input/LatencyStatistics.cpp index e343578e00..394da22a62 100644 --- a/libs/input/LatencyStatistics.cpp +++ b/libs/input/LatencyStatistics.cpp @@ -84,7 +84,7 @@ void LatencyStatistics::reset() { bool LatencyStatistics::shouldReport() { std::chrono::duration timeSinceReport = std::chrono::steady_clock::now() - mLastReportTime; - return mCount != 0 && timeSinceReport > mReportPeriod; + return mCount != 0 && timeSinceReport >= mReportPeriod; } } // namespace android diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp index 11578c393e..f6b5935bcf 100644 --- a/services/inputflinger/Android.bp +++ b/services/inputflinger/Android.bp @@ -44,6 +44,7 @@ cc_library_shared { "libhidlbase", "libinput", "liblog", + "libstatslog", "libutils", "libui", "server_configurable_flags", diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp index b8c3a808f1..9185e00272 100644 --- a/services/inputflinger/dispatcher/Android.bp +++ b/services/inputflinger/dispatcher/Android.bp @@ -34,6 +34,7 @@ cc_library_static { "libinputreporter", "libinputflinger_base", "liblog", + "libstatslog", "libui", "libutils", ], diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h index a9e22f19f1..28c2799477 100644 --- a/services/inputflinger/dispatcher/Entry.h +++ b/services/inputflinger/dispatcher/Entry.h @@ -29,6 +29,9 @@ namespace android::inputdispatcher { +// Sequence number for synthesized or injected events. +constexpr uint32_t SYNTHESIZED_EVENT_SEQUENCE_NUM = 0; + struct EventEntry { enum { TYPE_CONFIGURATION_CHANGED, TYPE_DEVICE_RESET, TYPE_KEY, TYPE_MOTION }; @@ -41,8 +44,23 @@ struct EventEntry { bool dispatchInProgress; // initially false, set to true while dispatching + /** + * Injected keys are events from an external (probably untrusted) application + * and are not related to real hardware state. They come in via + * InputDispatcher::injectInputEvent, which sets policy flag POLICY_FLAG_INJECTED. + */ inline bool isInjected() const { return injectionState != nullptr; } + /** + * Synthesized events are either injected events, or events that come + * from real hardware, but aren't directly attributable to a specific hardware event. + * Key repeat is a synthesized event, because it is related to an actual hardware state + * (a key is currently pressed), but the repeat itself is generated by the framework. + */ + inline bool isSynthesized() const { + return isInjected() || sequenceNum == SYNTHESIZED_EVENT_SEQUENCE_NUM; + } + void release(); virtual void appendDescription(std::string& msg) const = 0; diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 039462e23b..2361867e62 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -50,6 +50,7 @@ #include #include #include +#include #include #include #include @@ -2274,6 +2275,7 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, motionEntry->downTime, motionEntry->eventTime, motionEntry->pointerCount, motionEntry->pointerProperties, usingCoords); + reportTouchEventForStatistics(*motionEntry); break; } @@ -4478,6 +4480,34 @@ void InputDispatcher::updateDispatchStatistics(nsecs_t currentTime, const EventE // TODO Write some statistics about how long we spend waiting. } +/** + * Report the touch event latency to the statsd server. + * Input events are reported for statistics if: + * - This is a touchscreen event + * - InputFilter is not enabled + * - Event is not injected or synthesized + * + * Statistics should be reported before calling addValue, to prevent a fresh new sample + * from getting aggregated with the "old" data. + */ +void InputDispatcher::reportTouchEventForStatistics(const MotionEntry& motionEntry) + REQUIRES(mLock) { + const bool reportForStatistics = (motionEntry.source == AINPUT_SOURCE_TOUCHSCREEN) && + !(motionEntry.isSynthesized()) && !mInputFilterEnabled; + if (!reportForStatistics) { + return; + } + + if (mTouchStatistics.shouldReport()) { + android::util::stats_write(android::util::TOUCH_EVENT_REPORTED, mTouchStatistics.getMin(), + mTouchStatistics.getMax(), mTouchStatistics.getMean(), + mTouchStatistics.getStDev(), mTouchStatistics.getCount()); + mTouchStatistics.reset(); + } + const float latencyMicros = nanoseconds_to_microseconds(now() - motionEntry.eventTime); + mTouchStatistics.addValue(latencyMicros); +} + void InputDispatcher::traceInboundQueueLengthLocked() { if (ATRACE_ENABLED()) { ATRACE_INT("iq", mInboundQueue.size()); diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index f2c04028e4..0d9d6b0c15 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -455,6 +456,10 @@ private: void doOnPointerDownOutsideFocusLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); // Statistics gathering. + static constexpr std::chrono::duration TOUCH_STATS_REPORT_PERIOD = 5min; + LatencyStatistics mTouchStatistics{TOUCH_STATS_REPORT_PERIOD}; + + void reportTouchEventForStatistics(const MotionEntry& entry); void updateDispatchStatistics(nsecs_t currentTime, const EventEntry* entry, int32_t injectionResult, nsecs_t timeSpentWaitingForApplication); void traceInboundQueueLengthLocked() REQUIRES(mLock); diff --git a/services/inputflinger/dispatcher/InputState.h b/services/inputflinger/dispatcher/InputState.h index bccef0fca3..47e9b36219 100644 --- a/services/inputflinger/dispatcher/InputState.h +++ b/services/inputflinger/dispatcher/InputState.h @@ -24,9 +24,6 @@ namespace android::inputdispatcher { -// Sequence number for synthesized or injected events. -constexpr uint32_t SYNTHESIZED_EVENT_SEQUENCE_NUM = 0; - /* Tracks dispatched key and motion event state so that cancellation events can be * synthesized when events are dropped. */ class InputState { -- cgit v1.2.3-59-g8ed1b From 524027761e00c014fbe7e52741ca82bf6e6f1dca Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Tue, 22 Oct 2019 09:32:30 -0700 Subject: Use enum class for InputMessage type Using enum class helps with type safety, and allows compile-time detection of missing switch/case statements. Bug: 70668286 Test: compile-time only Change-Id: I5ae95cfbf2b38330fa34eca1c9f444a90190cd25 --- include/input/InputTransport.h | 10 +-- libs/input/InputTransport.cpp | 109 ++++++++++++++++----------------- libs/input/tests/InputChannel_test.cpp | 8 +-- 3 files changed, 63 insertions(+), 64 deletions(-) (limited to 'include') diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h index c056c972d2..1822e4a0db 100644 --- a/include/input/InputTransport.h +++ b/include/input/InputTransport.h @@ -60,14 +60,14 @@ class Parcel; * in StructLayout_test should be made. */ struct InputMessage { - enum { - TYPE_KEY = 1, - TYPE_MOTION = 2, - TYPE_FINISHED = 3, + enum class Type : uint32_t { + KEY, + MOTION, + FINISHED, }; struct Header { - uint32_t type; + Type type; // 4 bytes // We don't need this field in order to align the body below but we // leave it here because InputMessage::size() and other functions // compute the size of this structure as sizeof(Header) + sizeof(Body). diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp index 366c93cf1f..c4f7fe0bf2 100644 --- a/libs/input/InputTransport.cpp +++ b/libs/input/InputTransport.cpp @@ -93,13 +93,12 @@ inline static bool isPointerEvent(int32_t source) { bool InputMessage::isValid(size_t actualSize) const { if (size() == actualSize) { switch (header.type) { - case TYPE_KEY: - return true; - case TYPE_MOTION: - return body.motion.pointerCount > 0 - && body.motion.pointerCount <= MAX_POINTERS; - case TYPE_FINISHED: - return true; + case Type::KEY: + return true; + case Type::MOTION: + return body.motion.pointerCount > 0 && body.motion.pointerCount <= MAX_POINTERS; + case Type::FINISHED: + return true; } } return false; @@ -107,12 +106,12 @@ bool InputMessage::isValid(size_t actualSize) const { size_t InputMessage::size() const { switch (header.type) { - case TYPE_KEY: - return sizeof(Header) + body.key.size(); - case TYPE_MOTION: - return sizeof(Header) + body.motion.size(); - case TYPE_FINISHED: - return sizeof(Header) + body.finished.size(); + case Type::KEY: + return sizeof(Header) + body.key.size(); + case Type::MOTION: + return sizeof(Header) + body.motion.size(); + case Type::FINISHED: + return sizeof(Header) + body.finished.size(); } return sizeof(Header); } @@ -129,7 +128,7 @@ void InputMessage::getSanitizedCopy(InputMessage* msg) const { // Write the body switch(header.type) { - case InputMessage::TYPE_KEY: { + case InputMessage::Type::KEY: { // uint32_t seq msg->body.key.seq = body.key.seq; // nsecs_t eventTime @@ -156,7 +155,7 @@ void InputMessage::getSanitizedCopy(InputMessage* msg) const { msg->body.key.downTime = body.key.downTime; break; } - case InputMessage::TYPE_MOTION: { + case InputMessage::Type::MOTION: { // uint32_t seq msg->body.motion.seq = body.motion.seq; // nsecs_t eventTime @@ -212,7 +211,7 @@ void InputMessage::getSanitizedCopy(InputMessage* msg) const { } break; } - case InputMessage::TYPE_FINISHED: { + case InputMessage::Type::FINISHED: { msg->body.finished.seq = body.finished.seq; msg->body.finished.handled = body.finished.handled; break; @@ -457,7 +456,7 @@ status_t InputPublisher::publishKeyEvent( } InputMessage msg; - msg.header.type = InputMessage::TYPE_KEY; + msg.header.type = InputMessage::Type::KEY; msg.body.key.seq = seq; msg.body.key.deviceId = deviceId; msg.body.key.source = source; @@ -511,7 +510,7 @@ status_t InputPublisher::publishMotionEvent( } InputMessage msg; - msg.header.type = InputMessage::TYPE_MOTION; + msg.header.type = InputMessage::Type::MOTION; msg.body.motion.seq = seq; msg.body.motion.deviceId = deviceId; msg.body.motion.source = source; @@ -553,7 +552,7 @@ status_t InputPublisher::receiveFinishedSignal(uint32_t* outSeq, bool* outHandle *outHandled = false; return result; } - if (msg.header.type != InputMessage::TYPE_FINISHED) { + if (msg.header.type != InputMessage::Type::FINISHED) { ALOGE("channel '%s' publisher ~ Received unexpected message of type %d from consumer", mChannel->getName().c_str(), msg.header.type); return UNKNOWN_ERROR; @@ -614,59 +613,59 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, } switch (mMsg.header.type) { - case InputMessage::TYPE_KEY: { - KeyEvent* keyEvent = factory->createKeyEvent(); - if (!keyEvent) return NO_MEMORY; + case InputMessage::Type::KEY: { + KeyEvent* keyEvent = factory->createKeyEvent(); + if (!keyEvent) return NO_MEMORY; - initializeKeyEvent(keyEvent, &mMsg); - *outSeq = mMsg.body.key.seq; - *outEvent = keyEvent; + initializeKeyEvent(keyEvent, &mMsg); + *outSeq = mMsg.body.key.seq; + *outEvent = keyEvent; #if DEBUG_TRANSPORT_ACTIONS ALOGD("channel '%s' consumer ~ consumed key event, seq=%u", mChannel->getName().c_str(), *outSeq); #endif break; - } + } - case InputMessage::TYPE_MOTION: { - ssize_t batchIndex = findBatch(mMsg.body.motion.deviceId, mMsg.body.motion.source); - if (batchIndex >= 0) { - Batch& batch = mBatches.editItemAt(batchIndex); - if (canAddSample(batch, &mMsg)) { - batch.samples.push(mMsg); + case InputMessage::Type::MOTION: { + ssize_t batchIndex = findBatch(mMsg.body.motion.deviceId, mMsg.body.motion.source); + if (batchIndex >= 0) { + Batch& batch = mBatches.editItemAt(batchIndex); + if (canAddSample(batch, &mMsg)) { + batch.samples.push(mMsg); #if DEBUG_TRANSPORT_ACTIONS ALOGD("channel '%s' consumer ~ appended to batch event", mChannel->getName().c_str()); #endif break; - } else if (isPointerEvent(mMsg.body.motion.source) && - mMsg.body.motion.action == AMOTION_EVENT_ACTION_CANCEL) { - // No need to process events that we are going to cancel anyways - const size_t count = batch.samples.size(); - for (size_t i = 0; i < count; i++) { - const InputMessage& msg = batch.samples.itemAt(i); - sendFinishedSignal(msg.body.motion.seq, false); - } - batch.samples.removeItemsAt(0, count); - mBatches.removeAt(batchIndex); - } else { - // We cannot append to the batch in progress, so we need to consume - // the previous batch right now and defer the new message until later. - mMsgDeferred = true; - status_t result = consumeSamples(factory, - batch, batch.samples.size(), outSeq, outEvent); - mBatches.removeAt(batchIndex); - if (result) { - return result; - } + } else if (isPointerEvent(mMsg.body.motion.source) && + mMsg.body.motion.action == AMOTION_EVENT_ACTION_CANCEL) { + // No need to process events that we are going to cancel anyways + const size_t count = batch.samples.size(); + for (size_t i = 0; i < count; i++) { + const InputMessage& msg = batch.samples.itemAt(i); + sendFinishedSignal(msg.body.motion.seq, false); + } + batch.samples.removeItemsAt(0, count); + mBatches.removeAt(batchIndex); + } else { + // We cannot append to the batch in progress, so we need to consume + // the previous batch right now and defer the new message until later. + mMsgDeferred = true; + status_t result = consumeSamples(factory, batch, batch.samples.size(), + outSeq, outEvent); + mBatches.removeAt(batchIndex); + if (result) { + return result; + } #if DEBUG_TRANSPORT_ACTIONS ALOGD("channel '%s' consumer ~ consumed batch event and " "deferred current event, seq=%u", mChannel->getName().c_str(), *outSeq); #endif break; + } } - } // Start a new batch if needed. if (mMsg.body.motion.action == AMOTION_EVENT_ACTION_MOVE @@ -694,7 +693,7 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, mChannel->getName().c_str(), *outSeq); #endif break; - } + } default: ALOGE("channel '%s' consumer ~ Received unexpected message of type %d", @@ -1074,7 +1073,7 @@ status_t InputConsumer::sendFinishedSignal(uint32_t seq, bool handled) { status_t InputConsumer::sendUnchainedFinishedSignal(uint32_t seq, bool handled) { InputMessage msg; - msg.header.type = InputMessage::TYPE_FINISHED; + msg.header.type = InputMessage::Type::FINISHED; msg.body.finished.seq = seq; msg.body.finished.handled = handled; return mChannel->sendMessage(&msg); diff --git a/libs/input/tests/InputChannel_test.cpp b/libs/input/tests/InputChannel_test.cpp index af74edd65d..7c331e132d 100644 --- a/libs/input/tests/InputChannel_test.cpp +++ b/libs/input/tests/InputChannel_test.cpp @@ -86,7 +86,7 @@ TEST_F(InputChannelTest, OpenInputChannelPair_ReturnsAPairOfConnectedChannels) { // Server->Client communication InputMessage serverMsg; memset(&serverMsg, 0, sizeof(InputMessage)); - serverMsg.header.type = InputMessage::TYPE_KEY; + serverMsg.header.type = InputMessage::Type::KEY; serverMsg.body.key.action = AKEY_EVENT_ACTION_DOWN; EXPECT_EQ(OK, serverChannel->sendMessage(&serverMsg)) << "server channel should be able to send message to client channel"; @@ -102,7 +102,7 @@ TEST_F(InputChannelTest, OpenInputChannelPair_ReturnsAPairOfConnectedChannels) { // Client->Server communication InputMessage clientReply; memset(&clientReply, 0, sizeof(InputMessage)); - clientReply.header.type = InputMessage::TYPE_FINISHED; + clientReply.header.type = InputMessage::Type::FINISHED; clientReply.body.finished.seq = 0x11223344; clientReply.body.finished.handled = true; EXPECT_EQ(OK, clientChannel->sendMessage(&clientReply)) @@ -161,7 +161,7 @@ TEST_F(InputChannelTest, SendSignal_WhenPeerClosed_ReturnsAnError) { serverChannel.clear(); // close server channel InputMessage msg; - msg.header.type = InputMessage::TYPE_KEY; + msg.header.type = InputMessage::Type::KEY; EXPECT_EQ(DEAD_OBJECT, clientChannel->sendMessage(&msg)) << "sendMessage should have returned DEAD_OBJECT"; } @@ -180,7 +180,7 @@ TEST_F(InputChannelTest, SendAndReceive_MotionClassification) { }; InputMessage serverMsg = {}, clientMsg; - serverMsg.header.type = InputMessage::TYPE_MOTION; + serverMsg.header.type = InputMessage::Type::MOTION; serverMsg.body.motion.seq = 1; serverMsg.body.motion.pointerCount = 1; -- cgit v1.2.3-59-g8ed1b From 3d70e532faeb9b8b2971b8643b824bacfa896047 Mon Sep 17 00:00:00 2001 From: Elliott Hughes Date: Tue, 29 Oct 2019 08:59:39 -0700 Subject: Add "Available since" text to all android/ headers. Also fix a few missing/incorrect annotations. Bug: http://b/143470841 Test: treehugger Change-Id: I1d9ec8350df03df7d80f1cc2334eef45deec30ec --- include/android/choreographer.h | 6 ++ include/android/configuration.h | 18 ++-- include/android/font.h | 18 ++++ include/android/font_matcher.h | 14 ++- include/android/hardware_buffer_jni.h | 4 + include/android/input.h | 16 +-- include/android/multinetwork.h | 11 ++ include/android/native_window_jni.h | 2 + include/android/sensor.h | 43 ++++++-- include/android/surface_control.h | 59 ++++++++++- include/android/surface_texture.h | 15 +++ include/android/surface_texture_jni.h | 8 +- include/android/system_fonts.h | 6 ++ include/android/trace.h | 8 +- .../ndk/include_ndk/android/binder_auto_utils.h | 2 +- .../ndk/include_ndk/android/binder_ibinder.h | 68 +++++++++++-- .../ndk/include_ndk/android/binder_ibinder_jni.h | 8 +- .../binder/ndk/include_ndk/android/binder_parcel.h | 111 ++++++++++++++++++++- .../binder/ndk/include_ndk/android/binder_status.h | 28 +++++- .../nativewindow/include/android/hardware_buffer.h | 22 ++++ libs/nativewindow/include/android/native_window.h | 7 ++ 21 files changed, 427 insertions(+), 47 deletions(-) (limited to 'include') diff --git a/include/android/choreographer.h b/include/android/choreographer.h index 1b589bca72..0d97e8c066 100644 --- a/include/android/choreographer.h +++ b/include/android/choreographer.h @@ -59,6 +59,8 @@ typedef void (*AChoreographer_frameCallback64)(int64_t frameTimeNanos, void* dat /** * Get the AChoreographer instance for the current thread. This must be called * on an ALooper thread. + * + * Available since API level 24. */ AChoreographer* AChoreographer_getInstance() __INTRODUCED_IN(24); @@ -82,6 +84,8 @@ void AChoreographer_postFrameCallbackDelayed(AChoreographer* choreographer, /** * Power a callback to be run on the next frame. The data pointer provided will * be passed to the callback function when it's called. + * + * Available since API level 29. */ void AChoreographer_postFrameCallback64(AChoreographer* choreographer, AChoreographer_frameCallback64 callback, void* data) __INTRODUCED_IN(29); @@ -90,6 +94,8 @@ void AChoreographer_postFrameCallback64(AChoreographer* choreographer, * Post a callback to be run on the frame following the specified delay. The * data pointer provided will be passed to the callback function when it's * called. + * + * Available since API level 29. */ void AChoreographer_postFrameCallbackDelayed64(AChoreographer* choreographer, AChoreographer_frameCallback64 callback, void* data, uint32_t delayMillis) __INTRODUCED_IN(29); diff --git a/include/android/configuration.h b/include/android/configuration.h index ef6c5a2f81..331072238b 100644 --- a/include/android/configuration.h +++ b/include/android/configuration.h @@ -675,50 +675,52 @@ int32_t AConfiguration_getUiModeNight(AConfiguration* config); */ void AConfiguration_setUiModeNight(AConfiguration* config, int32_t uiModeNight); -#if __ANDROID_API__ >= 13 /** * Return the current configuration screen width in dp units, or * ACONFIGURATION_SCREEN_WIDTH_DP_ANY if not set. */ -int32_t AConfiguration_getScreenWidthDp(AConfiguration* config) __INTRODUCED_IN(13); +int32_t AConfiguration_getScreenWidthDp(AConfiguration* config); /** * Set the configuration's current screen width in dp units. */ -void AConfiguration_setScreenWidthDp(AConfiguration* config, int32_t value) __INTRODUCED_IN(13); +void AConfiguration_setScreenWidthDp(AConfiguration* config, int32_t value); /** * Return the current configuration screen height in dp units, or * ACONFIGURATION_SCREEN_HEIGHT_DP_ANY if not set. */ -int32_t AConfiguration_getScreenHeightDp(AConfiguration* config) __INTRODUCED_IN(13); +int32_t AConfiguration_getScreenHeightDp(AConfiguration* config); /** * Set the configuration's current screen width in dp units. */ -void AConfiguration_setScreenHeightDp(AConfiguration* config, int32_t value) __INTRODUCED_IN(13); +void AConfiguration_setScreenHeightDp(AConfiguration* config, int32_t value); /** * Return the configuration's smallest screen width in dp units, or * ACONFIGURATION_SMALLEST_SCREEN_WIDTH_DP_ANY if not set. */ -int32_t AConfiguration_getSmallestScreenWidthDp(AConfiguration* config) __INTRODUCED_IN(13); +int32_t AConfiguration_getSmallestScreenWidthDp(AConfiguration* config); /** * Set the configuration's smallest screen width in dp units. */ -void AConfiguration_setSmallestScreenWidthDp(AConfiguration* config, int32_t value) __INTRODUCED_IN(13); -#endif /* __ANDROID_API__ >= 13 */ +void AConfiguration_setSmallestScreenWidthDp(AConfiguration* config, int32_t value); #if __ANDROID_API__ >= 17 /** * Return the configuration's layout direction, or * ACONFIGURATION_LAYOUTDIR_ANY if not set. + * + * Available since API level 17. */ int32_t AConfiguration_getLayoutDirection(AConfiguration* config) __INTRODUCED_IN(17); /** * Set the configuration's layout direction. + * + * Available since API level 17. */ void AConfiguration_setLayoutDirection(AConfiguration* config, int32_t value) __INTRODUCED_IN(17); #endif /* __ANDROID_API__ >= 17 */ diff --git a/include/android/font.h b/include/android/font.h index 8001ee1938..1618096d69 100644 --- a/include/android/font.h +++ b/include/android/font.h @@ -96,6 +96,8 @@ struct AFont; /** * Close an AFont. * + * Available since API level 29. + * * \param font a font returned by ASystemFontIterator_next or AFontMatchert_match. * Do nothing if NULL is passed. */ @@ -116,6 +118,8 @@ void AFont_close(AFont* _Nullable font) __INTRODUCED_IN(29); * The font file returned is guaranteed to be opend with O_RDONLY. * Note that the returned pointer is valid until AFont_close() is called for the given font. * + * Available since API level 29. + * * \param font a font object. Passing NULL is not allowed. * \return a string of the font file path. */ @@ -184,6 +188,8 @@ const char* _Nonnull AFont_getFontFilePath(const AFont* _Nonnull font) __INTRODU * * For more information about font weight, read [OpenType usWeightClass](https://docs.microsoft.com/en-us/typography/opentype/spec/os2#usweightclass) * + * Available since API level 29. + * * \param font a font object. Passing NULL is not allowed. * \return a positive integer less than or equal to {@link ASYSTEM_FONT_MAX_WEIGHT} is returned. */ @@ -192,6 +198,8 @@ uint16_t AFont_getWeight(const AFont* _Nonnull font) __INTRODUCED_IN(29); /** * Return true if the current font is italic, otherwise returns false. * + * Available since API level 29. + * * \param font a font object. Passing NULL is not allowed. * \return true if italic, otherwise false. */ @@ -204,6 +212,8 @@ bool AFont_isItalic(const AFont* _Nonnull font) __INTRODUCED_IN(29); * * Note that the returned pointer is valid until AFont_close() is called. * + * Available since API level 29. + * * \param font a font object. Passing NULL is not allowed. * \return a IETF BCP47 compliant language tag or nullptr if not available. */ @@ -216,6 +226,8 @@ const char* _Nullable AFont_getLocale(const AFont* _Nonnull font) __INTRODUCED_I * returns a non-negative value as an font offset in the collection. This * always returns 0 if the target font file is a regular font. * + * Available since API level 29. + * * \param font a font object. Passing NULL is not allowed. * \return a font collection index. */ @@ -247,6 +259,8 @@ size_t AFont_getCollectionIndex(const AFont* _Nonnull font) __INTRODUCED_IN(29); * * For more information about font variation settings, read [Font Variations Table](https://docs.microsoft.com/en-us/typography/opentype/spec/fvar) * + * Available since API level 29. + * * \param font a font object. Passing NULL is not allowed. * \return a number of font variation settings. */ @@ -258,6 +272,8 @@ size_t AFont_getAxisCount(const AFont* _Nonnull font) __INTRODUCED_IN(29); * * See AFont_getAxisCount for more details. * + * Available since API level 29. + * * \param font a font object. Passing NULL is not allowed. * \param axisIndex an index to the font variation settings. Passing value larger than or * equal to {@link AFont_getAxisCount} is not allowed. @@ -271,6 +287,8 @@ uint32_t AFont_getAxisTag(const AFont* _Nonnull font, uint32_t axisIndex) * * See AFont_getAxisCount for more details. * + * Available since API level 29. + * * \param font a font object. Passing NULL is not allowed. * \param axisIndex an index to the font variation settings. Passing value larger than or * equal to {@link ASYstemFont_getAxisCount} is not allwed. diff --git a/include/android/font_matcher.h b/include/android/font_matcher.h index 0b8f892b9b..d4bd892bf6 100644 --- a/include/android/font_matcher.h +++ b/include/android/font_matcher.h @@ -130,13 +130,17 @@ struct AFontMatcher; */ /** - * Creates a new AFontMatcher object + * Creates a new AFontMatcher object. + * + * Available since API level 29. */ AFontMatcher* _Nonnull AFontMatcher_create() __INTRODUCED_IN(29); /** * Destroy the matcher object. * + * Available since API level 29. + * * \param matcher a matcher object. Passing NULL is not allowed. */ void AFontMatcher_destroy(AFontMatcher* _Nonnull matcher) __INTRODUCED_IN(29); @@ -147,6 +151,8 @@ void AFontMatcher_destroy(AFontMatcher* _Nonnull matcher) __INTRODUCED_IN(29); * If this function is not called, the matcher performs with {@link ASYSTEM_FONT_WEIGHT_NORMAL} * with non-italic style. * + * Available since API level 29. + * * \param matcher a matcher object. Passing NULL is not allowed. * \param weight a font weight value. Only from 0 to 1000 value is valid * \param italic true if italic, otherwise false. @@ -161,6 +167,8 @@ void AFontMatcher_setStyle( * * If this function is not called, the matcher performs with empty locale list. * + * Available since API level 29. + * * \param matcher a matcher object. Passing NULL is not allowed. * \param languageTags a null character terminated comma separated IETF BCP47 compliant language * tags. @@ -174,6 +182,8 @@ void AFontMatcher_setLocales( * * If this function is not called, the matcher performs with {@link AFAMILY_VARIANT_DEFAULT}. * + * Available since API level 29. + * * \param matcher a matcher object. Passing NULL is not allowed. * \param familyVariant Must be one of {@link AFAMILY_VARIANT_DEFAULT}, * {@link AFAMILY_VARIANT_COMPACT} or {@link AFAMILY_VARIANT_ELEGANT} is valid. @@ -190,6 +200,8 @@ void AFontMatcher_setFamilyVariant( * Even if no font can render the given text, this function will return a non-null result for * drawing Tofu character. * + * Available since API level 29. + * * \param matcher a matcher object. Passing NULL is not allowed. * \param familyName a null character terminated font family name * \param text a UTF-16 encoded text buffer to be rendered. Do not pass empty string. diff --git a/include/android/hardware_buffer_jni.h b/include/android/hardware_buffer_jni.h index aedf36903d..293e5ac469 100644 --- a/include/android/hardware_buffer_jni.h +++ b/include/android/hardware_buffer_jni.h @@ -42,6 +42,8 @@ __BEGIN_DECLS * that is returned. To keep the AHardwareBuffer live after the Java * HardwareBuffer object got garbage collected, be sure to use AHardwareBuffer_acquire() * to acquire an additional reference. + * + * Available since API level 26. */ AHardwareBuffer* AHardwareBuffer_fromHardwareBuffer(JNIEnv* env, jobject hardwareBufferObj) __INTRODUCED_IN(26); @@ -49,6 +51,8 @@ AHardwareBuffer* AHardwareBuffer_fromHardwareBuffer(JNIEnv* env, /** * Return a new Java HardwareBuffer object that wraps the passed native * AHardwareBuffer object. + * + * Available since API level 26. */ jobject AHardwareBuffer_toHardwareBuffer(JNIEnv* env, AHardwareBuffer* hardwareBuffer) __INTRODUCED_IN(26); diff --git a/include/android/input.h b/include/android/input.h index cfade6c806..ce439c6d75 100644 --- a/include/android/input.h +++ b/include/android/input.h @@ -986,10 +986,8 @@ int32_t AMotionEvent_getFlags(const AInputEvent* motion_event); */ int32_t AMotionEvent_getMetaState(const AInputEvent* motion_event); -#if __ANDROID_API__ >= 14 /** Get the button state of all buttons that are pressed. */ -int32_t AMotionEvent_getButtonState(const AInputEvent* motion_event) __INTRODUCED_IN(14); -#endif +int32_t AMotionEvent_getButtonState(const AInputEvent* motion_event); /** * Get a bitfield indicating which edges, if any, were touched by this motion event. @@ -1054,14 +1052,12 @@ size_t AMotionEvent_getPointerCount(const AInputEvent* motion_event); */ int32_t AMotionEvent_getPointerId(const AInputEvent* motion_event, size_t pointer_index); -#if __ANDROID_API__ >= 14 /** * Get the tool type of a pointer for the given pointer index. * The tool type indicates the type of tool used to make contact such as a * finger or stylus, if known. */ -int32_t AMotionEvent_getToolType(const AInputEvent* motion_event, size_t pointer_index) __INTRODUCED_IN(14); -#endif +int32_t AMotionEvent_getToolType(const AInputEvent* motion_event, size_t pointer_index); /** * Get the original raw X coordinate of this event. @@ -1151,11 +1147,9 @@ float AMotionEvent_getToolMinor(const AInputEvent* motion_event, size_t pointer_ */ float AMotionEvent_getOrientation(const AInputEvent* motion_event, size_t pointer_index); -#if __ANDROID_API__ >= 13 /** Get the value of the request axis for the given pointer index. */ float AMotionEvent_getAxisValue(const AInputEvent* motion_event, - int32_t axis, size_t pointer_index) __INTRODUCED_IN(13); -#endif + int32_t axis, size_t pointer_index); /** * Get the number of historical points in this event. These are movements that @@ -1286,14 +1280,12 @@ float AMotionEvent_getHistoricalToolMinor(const AInputEvent* motion_event, size_ float AMotionEvent_getHistoricalOrientation(const AInputEvent* motion_event, size_t pointer_index, size_t history_index); -#if __ANDROID_API__ >= 13 /** * Get the historical value of the request axis for the given pointer index * that occurred between this event and the previous motion event. */ float AMotionEvent_getHistoricalAxisValue(const AInputEvent* motion_event, - int32_t axis, size_t pointer_index, size_t history_index) __INTRODUCED_IN(13); -#endif + int32_t axis, size_t pointer_index, size_t history_index); struct AInputQueue; diff --git a/include/android/multinetwork.h b/include/android/multinetwork.h index d31d1f122f..59b1deb595 100644 --- a/include/android/multinetwork.h +++ b/include/android/multinetwork.h @@ -69,6 +69,7 @@ typedef uint64_t net_handle_t; * * This is the equivalent of: [android.net.Network#bindSocket()](https://developer.android.com/reference/android/net/Network.html#bindSocket(java.net.Socket)) * + * Available since API level 23. */ int android_setsocknetwork(net_handle_t network, int fd) __INTRODUCED_IN(23); @@ -86,6 +87,7 @@ int android_setsocknetwork(net_handle_t network, int fd) __INTRODUCED_IN(23); * * This is the equivalent of: [android.net.ConnectivityManager#setProcessDefaultNetwork()](https://developer.android.com/reference/android/net/ConnectivityManager.html#setProcessDefaultNetwork(android.net.Network)) * + * Available since API level 23. */ int android_setprocnetwork(net_handle_t network) __INTRODUCED_IN(23); @@ -103,6 +105,7 @@ int android_setprocnetwork(net_handle_t network) __INTRODUCED_IN(23); * * This is the equivalent of: [android.net.Network#getAllByName()](https://developer.android.com/reference/android/net/Network.html#getAllByName(java.lang.String)) * + * Available since API level 23. */ int android_getaddrinfofornetwork(net_handle_t network, const char *node, const char *service, @@ -144,6 +147,8 @@ enum ResNsendFlags : uint32_t { * * Returns a file descriptor to watch for read events, or a negative * POSIX error code (see errno.h) if an immediate error occurs. + * + * Available since API level 29. */ int android_res_nquery(net_handle_t network, const char *dname, int ns_class, int ns_type, uint32_t flags) __INTRODUCED_IN(29); @@ -155,6 +160,8 @@ int android_res_nquery(net_handle_t network, * * Returns a file descriptor to watch for read events, or a negative * POSIX error code (see errno.h) if an immediate error occurs. + * + * Available since API level 29. */ int android_res_nsend(net_handle_t network, const uint8_t *msg, size_t msglen, uint32_t flags) __INTRODUCED_IN(29); @@ -163,6 +170,8 @@ int android_res_nsend(net_handle_t network, * Read a result for the query associated with the |fd| descriptor. * Closes |fd| before returning. * + * Available since 29. + * * Returns: * < 0: negative POSIX error code (see errno.h for possible values). |rcode| is not set. * >= 0: length of |answer|. |rcode| is the resolver return code (e.g., ns_r_nxdomain) @@ -173,6 +182,8 @@ int android_res_nresult(int fd, /** * Attempts to cancel the in-progress query associated with the |nsend_fd| * descriptor. + * + * Available since API level 29. */ void android_res_cancel(int nsend_fd) __INTRODUCED_IN(29); diff --git a/include/android/native_window_jni.h b/include/android/native_window_jni.h index 0c196b9671..3a77ffe86b 100644 --- a/include/android/native_window_jni.h +++ b/include/android/native_window_jni.h @@ -51,6 +51,8 @@ ANativeWindow* ANativeWindow_fromSurface(JNIEnv* env, jobject surface); * the ANativeWindow; maintains it through general Java object's life cycle; * and will automatically release the reference when the Java object gets garbage * collected. + * + * Available since API level 26. */ jobject ANativeWindow_toSurface(JNIEnv* env, ANativeWindow* window) __INTRODUCED_IN(26); #endif diff --git a/include/android/sensor.h b/include/android/sensor.h index e9d5c16d0d..3ebe79fd2e 100644 --- a/include/android/sensor.h +++ b/include/android/sensor.h @@ -564,6 +564,7 @@ ASensorManager* ASensorManager_getInstance(); * * ASensorManager* sensorManager = ASensorManager_getInstanceForPackage("foo.bar.baz"); * + * Available since API level 26. */ ASensorManager* ASensorManager_getInstanceForPackage(const char* packageName) __INTRODUCED_IN(26); #endif @@ -583,6 +584,8 @@ ASensor const* ASensorManager_getDefaultSensor(ASensorManager* manager, int type /** * Returns the default sensor with the given type and wakeUp properties or NULL if no sensor * of this type and wakeUp properties exists. + * + * Available since API level 21. */ ASensor const* ASensorManager_getDefaultSensorEx(ASensorManager* manager, int type, bool wakeUp) __INTRODUCED_IN(21); #endif @@ -609,6 +612,8 @@ int ASensorManager_destroyEventQueue(ASensorManager* manager, ASensorEventQueue* * Create a direct channel of {@link ASENSOR_DIRECT_CHANNEL_TYPE_SHARED_MEMORY} to be used * for configuring sensor direct report. * + * Available since API level 26. + * * \param manager the {@link ASensorManager} instance obtained from * {@link ASensorManager_getInstanceForPackage}. * \param fd file descriptor representing a shared memory created by @@ -627,6 +632,8 @@ int ASensorManager_createSharedMemoryDirectChannel(ASensorManager* manager, int * Create a direct channel of {@link ASENSOR_DIRECT_CHANNEL_TYPE_HARDWARE_BUFFER} type to be used * for configuring sensor direct report. * + * Available since API level 26. + * * \param manager the {@link ASensorManager} instance obtained from * {@link ASensorManager_getInstanceForPackage}. * \param buffer {@link AHardwareBuffer} instance created by {@link AHardwareBuffer_allocate}. @@ -646,6 +653,8 @@ int ASensorManager_createHardwareBufferDirectChannel( * The buffer used for creating direct channel does not get destroyed with * {@link ASensorManager_destroy} and has to be close or released separately. * + * Available since API level 26. + * * \param manager the {@link ASensorManager} instance obtained from * {@link ASensorManager_getInstanceForPackage}. * \param channelId channel id (a positive integer) returned from @@ -678,6 +687,8 @@ void ASensorManager_destroyDirectChannel(ASensorManager* manager, int channelId) * * ASensorManager_configureDirectReport(manager, sensor, channel_id, ASENSOR_DIRECT_RATE_FAST); * + * Available since API level 26. + * * \param manager the {@link ASensorManager} instance obtained from * {@link ASensorManager_getInstanceForPackage}. * \param sensor a {@link ASensor} to denote which sensor to be operate. It can be NULL if rate @@ -780,7 +791,7 @@ int ASensorEventQueue_hasEvents(ASensorEventQueue* queue); */ ssize_t ASensorEventQueue_getEvents(ASensorEventQueue* queue, ASensorEvent* events, size_t count); -#if __ANDROID_API__ >= __ANDROID_API_Q__ +#if __ANDROID_API__ >= 29 /** * Request that {@link ASENSOR_TYPE_ADDITIONAL_INFO} events to be delivered on * the given {@link ASensorEventQueue}. @@ -796,13 +807,15 @@ ssize_t ASensorEventQueue_getEvents(ASensorEventQueue* queue, ASensorEvent* even * {@link AAdditionalInfoEvent#type}, as new values may be defined in the future * and may delivered to the client. * + * Available since API level 29. + * * \param queue {@link ASensorEventQueue} to configure * \param enable true to request {@link ASENSOR_TYPE_ADDITIONAL_INFO} events, * false to stop receiving events * \return 0 on success or a negative error code on failure */ -int ASensorEventQueue_requestAdditionalInfoEvents(ASensorEventQueue* queue, bool enable); -#endif /* __ANDROID_API__ >= __ANDRDOID_API_Q__ */ +int ASensorEventQueue_requestAdditionalInfoEvents(ASensorEventQueue* queue, bool enable) __INTRODUCED_IN(29); +#endif /* __ANDROID_API__ >= 29 */ /*****************************************************************************/ @@ -837,26 +850,36 @@ int ASensor_getMinDelay(ASensor const* sensor); /** * Returns the maximum size of batches for this sensor. Batches will often be * smaller, as the hardware fifo might be used for other sensors. + * + * Available since API level 21. */ int ASensor_getFifoMaxEventCount(ASensor const* sensor) __INTRODUCED_IN(21); /** * Returns the hardware batch fifo size reserved to this sensor. + * + * Available since API level 21. */ int ASensor_getFifoReservedEventCount(ASensor const* sensor) __INTRODUCED_IN(21); /** * Returns this sensor's string type. + * + * Available since API level 21. */ const char* ASensor_getStringType(ASensor const* sensor) __INTRODUCED_IN(21); /** * Returns the reporting mode for this sensor. One of AREPORTING_MODE_* constants. + * + * Available since API level 21. */ int ASensor_getReportingMode(ASensor const* sensor) __INTRODUCED_IN(21); /** * Returns true if this is a wake up sensor, false otherwise. + * + * Available since API level 21. */ bool ASensor_isWakeUpSensor(ASensor const* sensor) __INTRODUCED_IN(21); #endif /* __ANDROID_API__ >= 21 */ @@ -865,6 +888,8 @@ bool ASensor_isWakeUpSensor(ASensor const* sensor) __INTRODUCED_IN(21); /** * Test if sensor supports a certain type of direct channel. * + * Available since API level 26. + * * \param sensor a {@link ASensor} to denote the sensor to be checked. * \param channelType Channel type constant, either * {@ASENSOR_DIRECT_CHANNEL_TYPE_SHARED_MEMORY} @@ -874,7 +899,9 @@ bool ASensor_isWakeUpSensor(ASensor const* sensor) __INTRODUCED_IN(21); bool ASensor_isDirectChannelTypeSupported(ASensor const* sensor, int channelType) __INTRODUCED_IN(26); /** - * Get the highest direct rate level that a sensor support. + * Get the highest direct rate level that a sensor supports. + * + * Available since API level 26. * * \param sensor a {@link ASensor} to denote the sensor to be checked. * @@ -885,7 +912,7 @@ bool ASensor_isDirectChannelTypeSupported(ASensor const* sensor, int channelType int ASensor_getHighestDirectReportRateLevel(ASensor const* sensor) __INTRODUCED_IN(26); #endif /* __ANDROID_API__ >= 26 */ -#if __ANDROID_API__ >= __ANDROID_API_Q__ +#if __ANDROID_API__ >= 29 /** * Returns the sensor's handle. * @@ -899,9 +926,11 @@ int ASensor_getHighestDirectReportRateLevel(ASensor const* sensor) __INTRODUCED_ * It is important to note that the value returned by {@link ASensor_getHandle} is not the same as * the value returned by the Java API {@link android.hardware.Sensor#getId} and no mapping exists * between the values. + * + * Available since API level 29. */ -int ASensor_getHandle(ASensor const* sensor) __INTRODUCED_IN(__ANDROID_API_Q__); -#endif /* __ANDROID_API__ >= ANDROID_API_Q__ */ +int ASensor_getHandle(ASensor const* sensor) __INTRODUCED_IN(29); +#endif /* __ANDROID_API__ >= 29 */ #ifdef __cplusplus }; diff --git a/include/android/surface_control.h b/include/android/surface_control.h index abb8368069..31abb6622e 100644 --- a/include/android/surface_control.h +++ b/include/android/surface_control.h @@ -46,7 +46,7 @@ struct ASurfaceControl; */ typedef struct ASurfaceControl ASurfaceControl; -/* +/** * Creates an ASurfaceControl with either ANativeWindow or an ASurfaceControl as its parent. * |debug_name| is a debug name associated with this surface. It can be used to * identify this surface in the SurfaceFlinger's layer tree. It must not be @@ -54,10 +54,17 @@ typedef struct ASurfaceControl ASurfaceControl; * * The caller takes ownership of the ASurfaceControl returned and must release it * using ASurfaceControl_release below. + * + * Available since API level 29. */ ASurfaceControl* ASurfaceControl_createFromWindow(ANativeWindow* parent, const char* debug_name) __INTRODUCED_IN(29); +/** + * See ASurfaceControl_createFromWindow. + * + * Available since API level 29. + */ ASurfaceControl* ASurfaceControl_create(ASurfaceControl* parent, const char* debug_name) __INTRODUCED_IN(29); @@ -65,6 +72,8 @@ ASurfaceControl* ASurfaceControl_create(ASurfaceControl* parent, const char* deb * Releases the |surface_control| object. After releasing the ASurfaceControl the caller no longer * has ownership of the AsurfaceControl. The surface and it's children may remain on display as long * as their parent remains on display. + * + * Available since API level 29. */ void ASurfaceControl_release(ASurfaceControl* surface_control) __INTRODUCED_IN(29); @@ -79,11 +88,15 @@ typedef struct ASurfaceTransaction ASurfaceTransaction; /** * The caller takes ownership of the transaction and must release it using * ASurfaceControl_delete below. + * + * Available since API level 29. */ ASurfaceTransaction* ASurfaceTransaction_create() __INTRODUCED_IN(29); /** * Destroys the |transaction| object. + * + * Available since API level 29. */ void ASurfaceTransaction_delete(ASurfaceTransaction* transaction) __INTRODUCED_IN(29); @@ -93,6 +106,8 @@ void ASurfaceTransaction_delete(ASurfaceTransaction* transaction) __INTRODUCED_I * Note that the transaction is guaranteed to be applied atomically. The * transactions which are applied on the same thread are also guaranteed to be * applied in order. + * + * Available since API level 29. */ void ASurfaceTransaction_apply(ASurfaceTransaction* transaction) __INTRODUCED_IN(29); @@ -116,6 +131,8 @@ typedef struct ASurfaceTransactionStats ASurfaceTransactionStats; * * THREADING * The transaction completed callback can be invoked on any thread. + * + * Available since API level 29. */ typedef void (*ASurfaceTransaction_OnComplete)(void* context, ASurfaceTransactionStats* stats) __INTRODUCED_IN(29); @@ -123,6 +140,8 @@ typedef void (*ASurfaceTransaction_OnComplete)(void* context, ASurfaceTransactio /** * Returns the timestamp of when the frame was latched by the framework. Once a frame is * latched by the framework, it is presented at the following hardware vsync. + * + * Available since API level 29. */ int64_t ASurfaceTransactionStats_getLatchTime(ASurfaceTransactionStats* surface_transaction_stats) __INTRODUCED_IN(29); @@ -131,6 +150,8 @@ int64_t ASurfaceTransactionStats_getLatchTime(ASurfaceTransactionStats* surface_ * Returns a sync fence that signals when the transaction has been presented. * The recipient of the callback takes ownership of the fence and is responsible for closing * it. If a device does not support present fences, a -1 will be returned. + * + * Available since API level 29. */ int ASurfaceTransactionStats_getPresentFenceFd(ASurfaceTransactionStats* surface_transaction_stats) __INTRODUCED_IN(29); @@ -141,6 +162,8 @@ int ASurfaceTransactionStats_getPresentFenceFd(ASurfaceTransactionStats* surface * When the client is done using the array, it must release it by calling * ASurfaceTransactionStats_releaseASurfaceControls. * + * Available since API level 29. + * * |outASurfaceControlsSize| returns the size of the ASurfaceControls array. */ void ASurfaceTransactionStats_getASurfaceControls(ASurfaceTransactionStats* surface_transaction_stats, @@ -150,6 +173,8 @@ void ASurfaceTransactionStats_getASurfaceControls(ASurfaceTransactionStats* surf /** * Releases the array of ASurfaceControls that were returned by * ASurfaceTransactionStats_getASurfaceControls. + * + * Available since API level 29. */ void ASurfaceTransactionStats_releaseASurfaceControls(ASurfaceControl** surface_controls) __INTRODUCED_IN(29); @@ -158,6 +183,8 @@ void ASurfaceTransactionStats_releaseASurfaceControls(ASurfaceControl** surface_ * Returns the timestamp of when the CURRENT buffer was acquired. A buffer is considered * acquired when its acquire_fence_fd has signaled. A buffer cannot be latched or presented until * it is acquired. If no acquire_fence_fd was provided, this timestamp will be set to -1. + * + * Available since API level 29. */ int64_t ASurfaceTransactionStats_getAcquireTime(ASurfaceTransactionStats* surface_transaction_stats, ASurfaceControl* surface_control) @@ -180,6 +207,8 @@ int64_t ASurfaceTransactionStats_getAcquireTime(ASurfaceTransactionStats* surfac * * The client must ensure that all pending refs on a buffer are released before attempting to reuse * this buffer, otherwise synchronization errors may occur. + * + * Available since API level 29. */ int ASurfaceTransactionStats_getPreviousReleaseFenceFd( ASurfaceTransactionStats* surface_transaction_stats, @@ -190,6 +219,8 @@ int ASurfaceTransactionStats_getPreviousReleaseFenceFd( * Sets the callback that will be invoked when the updates from this transaction * are presented. For details on the callback semantics and data, see the * comments on the ASurfaceTransaction_OnComplete declaration above. + * + * Available since API level 29. */ void ASurfaceTransaction_setOnComplete(ASurfaceTransaction* transaction, void* context, ASurfaceTransaction_OnComplete func) __INTRODUCED_IN(29); @@ -199,6 +230,8 @@ void ASurfaceTransaction_setOnComplete(ASurfaceTransaction* transaction, void* c * Any children of the* reparented |surface_control| will remain children of the |surface_control|. * * The |new_parent| can be null. Surface controls with a null parent do not appear on the display. + * + * Available since API level 29. */ void ASurfaceTransaction_reparent(ASurfaceTransaction* transaction, ASurfaceControl* surface_control, ASurfaceControl* new_parent) @@ -213,6 +246,8 @@ enum { * Updates the visibility of |surface_control|. If show is set to * ASURFACE_TRANSACTION_VISIBILITY_HIDE, the |surface_control| and all surfaces in its subtree will * be hidden. + * + * Available since API level 29. */ void ASurfaceTransaction_setVisibility(ASurfaceTransaction* transaction, ASurfaceControl* surface_control, int8_t visibility) @@ -224,6 +259,8 @@ void ASurfaceTransaction_setVisibility(ASurfaceTransaction* transaction, * the same z order is undefined. * * Z orders may be from MIN_INT32 to MAX_INT32. A layer's default z order index is 0. + * + * Available since API level 29. */ void ASurfaceTransaction_setZOrder(ASurfaceTransaction* transaction, ASurfaceControl* surface_control, int32_t z_order) @@ -236,6 +273,8 @@ void ASurfaceTransaction_setZOrder(ASurfaceTransaction* transaction, * * The frameworks takes ownership of the |acquire_fence_fd| passed and is responsible * for closing it. + * + * Available since API level 29. */ void ASurfaceTransaction_setBuffer(ASurfaceTransaction* transaction, ASurfaceControl* surface_control, AHardwareBuffer* buffer, @@ -246,6 +285,8 @@ void ASurfaceTransaction_setBuffer(ASurfaceTransaction* transaction, * ASurfaceControl visible in transparent regions of the surface. Colors |r|, |g|, * and |b| must be within the range that is valid for |dataspace|. |dataspace| and |alpha| * will be the dataspace and alpha set for the background color layer. + * + * Available since API level 29. */ void ASurfaceTransaction_setColor(ASurfaceTransaction* transaction, ASurfaceControl* surface_control, float r, float g, float b, @@ -264,6 +305,8 @@ void ASurfaceTransaction_setColor(ASurfaceTransaction* transaction, * |transform| the transform applied after the source rect is applied to the buffer. This parameter * should be set to 0 for no transform. To specify a transfrom use the NATIVE_WINDOW_TRANSFORM_* * enum. + * + * Available since API level 29. */ void ASurfaceTransaction_setGeometry(ASurfaceTransaction* transaction, ASurfaceControl* surface_control, const ARect& source, @@ -281,6 +324,8 @@ enum { * Updates whether the content for the buffer associated with this surface is * completely opaque. If true, every pixel of content inside the buffer must be * opaque or visual errors can occur. + * + * Available since API level 29. */ void ASurfaceTransaction_setBufferTransparency(ASurfaceTransaction* transaction, ASurfaceControl* surface_control, @@ -290,6 +335,8 @@ void ASurfaceTransaction_setBufferTransparency(ASurfaceTransaction* transaction, /** * Updates the region for the content on this surface updated in this * transaction. If unspecified, the complete surface is assumed to be damaged. + * + * Available since API level 29. */ void ASurfaceTransaction_setDamageRegion(ASurfaceTransaction* transaction, ASurfaceControl* surface_control, const ARect rects[], @@ -304,6 +351,8 @@ void ASurfaceTransaction_setDamageRegion(ASurfaceTransaction* transaction, * * If an earlier transaction has a desired present time of x, and a later transaction has a desired * present time that is before x, the later transaction will not preempt the earlier transaction. + * + * Available since API level 29. */ void ASurfaceTransaction_setDesiredPresentTime(ASurfaceTransaction* transaction, int64_t desiredPresentTime) __INTRODUCED_IN(29); @@ -312,6 +361,8 @@ void ASurfaceTransaction_setDesiredPresentTime(ASurfaceTransaction* transaction, * Sets the alpha for the buffer. It uses a premultiplied blending. * * The |alpha| must be between 0.0 and 1.0. + * + * Available since API level 29. */ void ASurfaceTransaction_setBufferAlpha(ASurfaceTransaction* transaction, ASurfaceControl* surface_control, float alpha) @@ -321,6 +372,8 @@ void ASurfaceTransaction_setBufferAlpha(ASurfaceTransaction* transaction, * Sets the data space of the surface_control's buffers. * * If no data space is set, the surface control defaults to ADATASPACE_SRGB. + * + * Available since API level 29. */ void ASurfaceTransaction_setBufferDataSpace(ASurfaceTransaction* transaction, ASurfaceControl* surface_control, ADataSpace data_space) @@ -331,6 +384,8 @@ void ASurfaceTransaction_setBufferDataSpace(ASurfaceTransaction* transaction, * * When |metadata| is set to null, the framework does not use any smpte2086 metadata when rendering * the surface's buffer. + * + * Available since API level 29. */ void ASurfaceTransaction_setHdrMetadata_smpte2086(ASurfaceTransaction* transaction, ASurfaceControl* surface_control, @@ -342,6 +397,8 @@ void ASurfaceTransaction_setHdrMetadata_smpte2086(ASurfaceTransaction* transacti * * When |metadata| is set to null, the framework does not use any cta861.3 metadata when rendering * the surface's buffer. + * + * Available since API level 29. */ void ASurfaceTransaction_setHdrMetadata_cta861_3(ASurfaceTransaction* transaction, ASurfaceControl* surface_control, diff --git a/include/android/surface_texture.h b/include/android/surface_texture.h index 540d23a4c7..dde7eaa0b6 100644 --- a/include/android/surface_texture.h +++ b/include/android/surface_texture.h @@ -65,6 +65,9 @@ typedef struct ASurfaceTexture ASurfaceTexture; * Release the reference to the native ASurfaceTexture acquired with * ASurfaceTexture_fromSurfaceTexture(). * Failing to do so will result in leaked memory and graphic resources. + * + * Available since API level 28. + * * \param st A ASurfaceTexture reference acquired with ASurfaceTexture_fromSurfaceTexture() */ void ASurfaceTexture_release(ASurfaceTexture* st) __INTRODUCED_IN(28); @@ -73,6 +76,8 @@ void ASurfaceTexture_release(ASurfaceTexture* st) __INTRODUCED_IN(28); * Returns a reference to an ANativeWindow (i.e. the Producer) for this SurfaceTexture. * This is equivalent to Java's: Surface sur = new Surface(surfaceTexture); * + * Available since API level 28. + * * \param st A ASurfaceTexture reference acquired with ASurfaceTexture_fromSurfaceTexture() * @return A reference to an ANativeWindow. This reference MUST BE released when no longer needed * using ANativeWindow_release(). Failing to do so will result in leaked resources. nullptr is @@ -90,6 +95,8 @@ ANativeWindow* ASurfaceTexture_acquireANativeWindow(ASurfaceTexture* st) __INTRO * contexts. Note, however, that the image contents are only accessible from one OpenGL ES * context at a time. * + * Available since API level 28. + * * \param st A ASurfaceTexture reference acquired with ASurfaceTexture_fromSurfaceTexture() * \param texName The name of the OpenGL ES texture that will be created. This texture name * must be unusued in the OpenGL ES context that is current on the calling thread. @@ -108,6 +115,8 @@ int ASurfaceTexture_attachToGLContext(ASurfaceTexture* st, uint32_t texName) __I * contexts. Note, however, that the image contents are only accessible from one OpenGL ES * context at a time. * + * Available since API level 28. + * * \param st A ASurfaceTexture reference acquired with ASurfaceTexture_fromSurfaceTexture() * \return 0 on success, negative posix error code otherwise (see ) */ @@ -118,6 +127,8 @@ int ASurfaceTexture_detachFromGLContext(ASurfaceTexture* st) __INTRODUCED_IN(28) * called while the OpenGL ES context that owns the texture is current on the calling thread. * It will implicitly bind its texture to the GL_TEXTURE_EXTERNAL_OES texture target. * + * Available since API level 28. + * * \param st A ASurfaceTexture reference acquired with ASurfaceTexture_fromSurfaceTexture() * \return 0 on success, negative posix error code otherwise (see ) */ @@ -135,6 +146,8 @@ int ASurfaceTexture_updateTexImage(ASurfaceTexture* st) __INTRODUCED_IN(28); * The matrix is stored in column-major order so that it may be passed directly to OpenGL ES via * the glLoadMatrixf or glUniformMatrix4fv functions. * + * Available since API level 28. + * * \param st A ASurfaceTexture reference acquired with ASurfaceTexture_fromSurfaceTexture() * \param mtx the array into which the 4x4 matrix will be stored. The array must have exactly * 16 elements. @@ -156,6 +169,8 @@ void ASurfaceTexture_getTransformMatrix(ASurfaceTexture* st, float mtx[16]) __IN * For EGL/Vulkan producers, this timestamp is the desired present time set with the * EGL_ANDROID_presentation_time or VK_GOOGLE_display_timing extensions * + * Available since API level 28. + * * \param st A ASurfaceTexture reference acquired with ASurfaceTexture_fromSurfaceTexture() */ int64_t ASurfaceTexture_getTimestamp(ASurfaceTexture* st) __INTRODUCED_IN(28); diff --git a/include/android/surface_texture_jni.h b/include/android/surface_texture_jni.h index b0e1edd590..2266d541f6 100644 --- a/include/android/surface_texture_jni.h +++ b/include/android/surface_texture_jni.h @@ -32,6 +32,8 @@ __BEGIN_DECLS +#if __ANDROID_API__ >= 28 + /** * Get a reference to the native ASurfaceTexture from the corresponding java object. * @@ -40,13 +42,17 @@ __BEGIN_DECLS * properly once the Java object gets finalized. * However, this will not result in program termination. * + * Available since API level 28. + * * \param env JNI environment * \param surfacetexture Instance of Java SurfaceTexture object * \return native ASurfaceTexture reference or nullptr if the java object is not a SurfaceTexture. * The returned reference MUST BE released when it's no longer needed using * ASurfaceTexture_release(). */ -ASurfaceTexture* ASurfaceTexture_fromSurfaceTexture(JNIEnv* env, jobject surfacetexture); +ASurfaceTexture* ASurfaceTexture_fromSurfaceTexture(JNIEnv* env, jobject surfacetexture) __INTRODUCED_IN(28); + +#endif __END_DECLS diff --git a/include/android/system_fonts.h b/include/android/system_fonts.h index f0485a1871..6fd7d2c0ab 100644 --- a/include/android/system_fonts.h +++ b/include/android/system_fonts.h @@ -102,6 +102,8 @@ struct ASystemFontIterator; * * Use ASystemFont_close() to close the iterator. * + * Available since API level 29. + * * \return a pointer for a newly allocated iterator, nullptr on failure. */ ASystemFontIterator* _Nullable ASystemFontIterator_open() __INTRODUCED_IN(29); @@ -109,6 +111,8 @@ ASystemFontIterator* _Nullable ASystemFontIterator_open() __INTRODUCED_IN(29); /** * Close an opened system font iterator, freeing any related resources. * + * Available since API level 29. + * * \param iterator a pointer of an iterator for the system fonts. Do nothing if NULL is passed. */ void ASystemFontIterator_close(ASystemFontIterator* _Nullable iterator) __INTRODUCED_IN(29); @@ -116,6 +120,8 @@ void ASystemFontIterator_close(ASystemFontIterator* _Nullable iterator) __INTROD /** * Move to the next system font. * + * Available since API level 29. + * * \param iterator an iterator for the system fonts. Passing NULL is not allowed. * \return a font. If no more font is available, returns nullptr. You need to release the returned * font by ASystemFont_close when it is no longer needed. diff --git a/include/android/trace.h b/include/android/trace.h index bb7ff28f79..d59690ab2e 100644 --- a/include/android/trace.h +++ b/include/android/trace.h @@ -74,7 +74,7 @@ void ATrace_endSection() __INTRODUCED_IN(23); #endif /* __ANDROID_API__ >= 23 */ -#if __ANDROID_API__ >= __ANDROID_API_Q__ +#if __ANDROID_API__ >= 29 /** * Writes a trace message to indicate that a given section of code has @@ -83,6 +83,8 @@ void ATrace_endSection() __INTRODUCED_IN(23); * asynchronous events do not need to be nested. The name and cookie used to * begin an event must be used to end it. * + * Available since API level 29. + * * \param sectionName The method name to appear in the trace. * \param cookie Unique identifier for distinguishing simultaneous events */ @@ -93,6 +95,8 @@ void ATrace_beginAsyncSection(const char* sectionName, int32_t cookie) __INTRODU * Must be called exactly once for each call to {@link ATrace_beginAsyncSection} * using the same name and cookie. * + * Available since API level 29. + * * \param methodName The method name to appear in the trace. * \param cookie Unique identifier for distinguishing simultaneous events */ @@ -101,6 +105,8 @@ void ATrace_endAsyncSection(const char* sectionName, int32_t cookie) __INTRODUCE /** * Writes trace message to indicate the value of a given counter. * + * Available since API level 29. + * * \param counterName The counter name to appear in the trace. * \param counterValue The counter value. */ diff --git a/libs/binder/ndk/include_ndk/android/binder_auto_utils.h b/libs/binder/ndk/include_ndk/android/binder_auto_utils.h index dc3c8d2e3a..dc891caab4 100644 --- a/libs/binder/ndk/include_ndk/android/binder_auto_utils.h +++ b/libs/binder/ndk/include_ndk/android/binder_auto_utils.h @@ -21,7 +21,7 @@ /** * @file binder_auto_utils.h - * @brief These objects provide a more C++-like thin interface to the . + * @brief These objects provide a more C++-like thin interface to the binder. */ #pragma once diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h index 160739b044..4d5c044232 100644 --- a/libs/binder/ndk/include_ndk/android/binder_ibinder.h +++ b/libs/binder/ndk/include_ndk/android/binder_ibinder.h @@ -34,7 +34,7 @@ #include __BEGIN_DECLS -#if __ANDROID_API__ >= __ANDROID_API_Q__ +#if __ANDROID_API__ >= 29 // Also see TF_* in kernel's binder.h typedef uint32_t binder_flags_t; @@ -165,6 +165,8 @@ typedef binder_status_t (*AIBinder_Class_onTransact)(AIBinder* binder, transacti * * None of these parameters can be null. * + * Available since API level 29. + * * \param interfaceDescriptor this is a unique identifier for the class. This is used internally for * sanity checks on transactions. * \param onCreate see AIBinder_Class_onCreate. @@ -199,6 +201,8 @@ typedef binder_status_t (*AIBinder_onDump)(AIBinder* binder, int fd, const char* * If this isn't set, nothing will be dumped when dump is called (for instance with * android.os.Binder#dump). Must be called before any instance of the class is created. * + * Available since API level 29. + * * \param dump function to call when an instance of this binder class is being dumped. */ void AIBinder_Class_setOnDump(AIBinder_Class* clazz, AIBinder_onDump onDump) __INTRODUCED_IN(29); @@ -220,6 +224,8 @@ void AIBinder_Class_setOnDump(AIBinder_Class* clazz, AIBinder_onDump onDump) __I * these two objects are actually equal using the AIBinder pointer alone (which they should be able * to do). Also see the suggested memory ownership model suggested above. * + * Available since API level 29. + * * \param clazz the type of the object to be created. * \param args the args to pass to AIBinder_onCreate for that class. * @@ -231,6 +237,8 @@ __attribute__((warn_unused_result)) AIBinder* AIBinder_new(const AIBinder_Class* /** * If this is hosted in a process other than the current one. * + * Available since API level 29. + * * \param binder the binder being queried. * * \return true if the AIBinder represents an object in another process. @@ -244,6 +252,8 @@ bool AIBinder_isRemote(const AIBinder* binder) __INTRODUCED_IN(29); * updated as the result of a transaction made using AIBinder_transact, but it will also be updated * based on the results of bookkeeping or other transactions made internally. * + * Available since API level 29. + * * \param binder the binder being queried. * * \return true if the binder is alive. @@ -255,6 +265,8 @@ bool AIBinder_isAlive(const AIBinder* binder) __INTRODUCED_IN(29); * return. Usually this is used to make sure that a binder is alive, as a placeholder call, or as a * sanity check. * + * Available since API level 29. + * * \param binder the binder being queried. * * \return STATUS_OK if the ping succeeds. @@ -264,7 +276,9 @@ binder_status_t AIBinder_ping(AIBinder* binder) __INTRODUCED_IN(29); /** * Built-in transaction for all binder objects. This dumps information about a given binder. * - * See also AIBinder_Class_setOnDump, AIBinder_onDump + * See also AIBinder_Class_setOnDump, AIBinder_onDump. + * + * Available since API level 29. * * \param binder the binder to dump information about * \param fd where information should be dumped to @@ -287,6 +301,8 @@ binder_status_t AIBinder_dump(AIBinder* binder, int fd, const char** args, uint3 * * If binder is local, this will return STATUS_INVALID_OPERATION. * + * Available since API level 29. + * * \param binder the binder object you want to receive death notifications from. * \param recipient the callback that will receive notifications when/if the binder dies. * \param cookie the value that will be passed to the death recipient on death. @@ -306,6 +322,8 @@ binder_status_t AIBinder_linkToDeath(AIBinder* binder, AIBinder_DeathRecipient* * If the binder dies, it will automatically unlink. If the binder is deleted, it will be * automatically unlinked. * + * Available since API level 29. + * * \param binder the binder object to remove a previously linked death recipient from. * \param recipient the callback to remove. * \param cookie the cookie used to link to death. @@ -322,9 +340,11 @@ binder_status_t AIBinder_unlinkToDeath(AIBinder* binder, AIBinder_DeathRecipient * This can be used with higher-level system services to determine the caller's identity and check * permissions. * + * Available since API level 29. + * * \return calling uid or the current process's UID if this thread isn't processing a transaction. */ -uid_t AIBinder_getCallingUid(); +uid_t AIBinder_getCallingUid() __INTRODUCED_IN(29); /** * This returns the calling PID assuming that this thread is called from a thread that is processing @@ -335,14 +355,18 @@ uid_t AIBinder_getCallingUid(); * calling process dies and is replaced with another process with elevated permissions and the same * PID. * + * Available since API level 29. + * * \return calling pid or the current process's PID if this thread isn't processing a transaction. * If the transaction being processed is a oneway transaction, then this method will return 0. */ -pid_t AIBinder_getCallingPid(); +pid_t AIBinder_getCallingPid() __INTRODUCED_IN(29); /** * This can only be called if a strong reference to this object already exists in process. * + * Available since API level 29. + * * \param binder the binder object to add a refcount to. */ void AIBinder_incStrong(AIBinder* binder) __INTRODUCED_IN(29); @@ -350,6 +374,8 @@ void AIBinder_incStrong(AIBinder* binder) __INTRODUCED_IN(29); /** * This will delete the object and call onDestroy once the refcount reaches zero. * + * Available since API level 29. + * * \param binder the binder object to remove a refcount from. */ void AIBinder_decStrong(AIBinder* binder) __INTRODUCED_IN(29); @@ -357,6 +383,8 @@ void AIBinder_decStrong(AIBinder* binder) __INTRODUCED_IN(29); /** * For debugging only! * + * Available since API level 29. + * * \param binder the binder object to retrieve the refcount of. * * \return the number of strong-refs on this binder in this process. If binder is null, this will be @@ -373,6 +401,8 @@ int32_t AIBinder_debugGetRefCount(AIBinder* binder) __INTRODUCED_IN(29); * This returns true if the class association succeeds. If it fails, no change is made to the * binder object. * + * Available since API level 29. + * * \param binder the object to attach the class to. * \param clazz the clazz to attach to binder. * @@ -383,6 +413,8 @@ bool AIBinder_associateClass(AIBinder* binder, const AIBinder_Class* clazz) __IN /** * Returns the class that this binder was constructed with or associated with. * + * Available since API level 29. + * * \param binder the object that is being queried. * * \return the class that this binder is associated with. If this binder wasn't created with @@ -394,6 +426,8 @@ const AIBinder_Class* AIBinder_getClass(AIBinder* binder) __INTRODUCED_IN(29); * Value returned by onCreate for a local binder. For stateless classes (if onCreate returns * null), this also returns null. For a remote binder, this will always return null. * + * Available since API level 29. + * * \param binder the object that is being queried. * * \return the userdata returned from AIBinder_onCreate when this object was created. This may be @@ -422,6 +456,8 @@ void* AIBinder_getUserData(AIBinder* binder) __INTRODUCED_IN(29); * AIBinder_transact. Alternatively, if there is an error while filling out the parcel, it can be * deleted with AParcel_delete. * + * Available since API level 29. + * * \param binder the binder object to start a transaction on. * \param in out parameter for input data to the transaction. * @@ -442,6 +478,8 @@ binder_status_t AIBinder_prepareTransaction(AIBinder* binder, AParcel** in) __IN * This does not affect the ownership of binder. The out parcel's ownership is passed to the caller * and must be released with AParcel_delete when finished reading. * + * Available since API level 29. + * * \param binder the binder object to transact on. * \param code the implementation-specific code representing which transaction should be taken. * \param in the implementation-specific input data to this transaction. @@ -459,6 +497,8 @@ binder_status_t AIBinder_transact(AIBinder* binder, transaction_code_t code, APa * This does not take any ownership of the input binder, but it can be used to retrieve it if * something else in some process still holds a reference to it. * + * Available since API level 29. + * * \param binder object to create a weak pointer to. * * \return object representing a weak pointer to binder (or null if binder is null). @@ -469,6 +509,8 @@ __attribute__((warn_unused_result)) AIBinder_Weak* AIBinder_Weak_new(AIBinder* b /** * Deletes the weak reference. This will have no impact on the lifetime of the binder. * + * Available since API level 29. + * * \param weakBinder object created with AIBinder_Weak_new. */ void AIBinder_Weak_delete(AIBinder_Weak* weakBinder) __INTRODUCED_IN(29); @@ -477,6 +519,8 @@ void AIBinder_Weak_delete(AIBinder_Weak* weakBinder) __INTRODUCED_IN(29); * If promotion succeeds, result will have one strong refcount added to it. Otherwise, this returns * null. * + * Available since API level 29. + * * \param weakBinder weak pointer to attempt retrieving the original object from. * * \return an AIBinder object with one refcount given to the caller or null. @@ -487,6 +531,8 @@ __attribute__((warn_unused_result)) AIBinder* AIBinder_Weak_promote(AIBinder_Wea /** * This function is executed on death receipt. See AIBinder_linkToDeath/AIBinder_unlinkToDeath. * + * Available since API level 29. + * * \param cookie the cookie passed to AIBinder_linkToDeath. */ typedef void (*AIBinder_DeathRecipient_onBinderDied)(void* cookie) __INTRODUCED_IN(29); @@ -494,6 +540,8 @@ typedef void (*AIBinder_DeathRecipient_onBinderDied)(void* cookie) __INTRODUCED_ /** * Creates a new binder death recipient. This can be attached to multiple different binder objects. * + * Available since API level 29. + * * \param onBinderDied the callback to call when this death recipient is invoked. * * \return the newly constructed object (or null if onBinderDied is null). @@ -505,19 +553,23 @@ __attribute__((warn_unused_result)) AIBinder_DeathRecipient* AIBinder_DeathRecip * Deletes a binder death recipient. It is not necessary to call AIBinder_unlinkToDeath before * calling this as these will all be automatically unlinked. * + * Available since API level 29. + * * \param recipient the binder to delete (previously created with AIBinder_DeathRecipient_new). */ void AIBinder_DeathRecipient_delete(AIBinder_DeathRecipient* recipient) __INTRODUCED_IN(29); -#endif //__ANDROID_API__ >= __ANDROID_API_Q__ +#endif //__ANDROID_API__ >= 29 -#if __ANDROID_API__ >= __ANDROID_API_R__ +#if __ANDROID_API__ >= 30 /** * Gets the extension registered with AIBinder_setExtension. * * See AIBinder_setExtension. * + * Available since API level 30. + * * \param binder the object to get the extension of. * \param outExt the returned extension object. Will be null if there is no extension set or * non-null with one strong ref count. @@ -570,6 +622,8 @@ binder_status_t AIBinder_getExtension(AIBinder* binder, AIBinder** outExt) __INT * // if bar is null, then there is no extension or a different * // type of extension * + * Available since API level 30. + * * \param binder the object to get the extension on. Must be local. * \param ext the extension to set (binder will hold a strong reference to this) * @@ -578,7 +632,7 @@ binder_status_t AIBinder_getExtension(AIBinder* binder, AIBinder** outExt) __INT */ binder_status_t AIBinder_setExtension(AIBinder* binder, AIBinder* ext) __INTRODUCED_IN(30); -#endif //__ANDROID_API__ >= __ANDROID_API_R__ +#endif //__ANDROID_API__ >= 30 __END_DECLS diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder_jni.h b/libs/binder/ndk/include_ndk/android/binder_ibinder_jni.h index 124f36c55b..be3029c3ff 100644 --- a/libs/binder/ndk/include_ndk/android/binder_ibinder_jni.h +++ b/libs/binder/ndk/include_ndk/android/binder_ibinder_jni.h @@ -31,7 +31,7 @@ #include __BEGIN_DECLS -#if __ANDROID_API__ >= __ANDROID_API_Q__ +#if __ANDROID_API__ >= 29 /** * Converts an android.os.IBinder object into an AIBinder* object. @@ -40,6 +40,8 @@ __BEGIN_DECLS * AIBinder object, the original object is returned. The returned object has one refcount * associated with it, and so this should be accompanied with an AIBinder_decStrong call. * + * Available since API level 29. + * * \param env Java environment. * \param binder android.os.IBinder java object. * @@ -55,6 +57,8 @@ __attribute__((warn_unused_result)) AIBinder* AIBinder_fromJavaBinder(JNIEnv* en * If either env or the binder is null, null is returned. If this binder object was originally an * IBinder object, the original java object will be returned. * + * Available since API level 29. + * * \param env Java environment. * \param binder the object to convert. * @@ -63,7 +67,7 @@ __attribute__((warn_unused_result)) AIBinder* AIBinder_fromJavaBinder(JNIEnv* en __attribute__((warn_unused_result)) jobject AIBinder_toJavaBinder(JNIEnv* env, AIBinder* binder) __INTRODUCED_IN(29); -#endif //__ANDROID_API__ >= __ANDROID_API_Q__ +#endif //__ANDROID_API__ >= 29 __END_DECLS /** @} */ diff --git a/libs/binder/ndk/include_ndk/android/binder_parcel.h b/libs/binder/ndk/include_ndk/android/binder_parcel.h index 8c4170754a..86b75b8c61 100644 --- a/libs/binder/ndk/include_ndk/android/binder_parcel.h +++ b/libs/binder/ndk/include_ndk/android/binder_parcel.h @@ -35,7 +35,7 @@ struct AIBinder; typedef struct AIBinder AIBinder; __BEGIN_DECLS -#if __ANDROID_API__ >= __ANDROID_API_Q__ +#if __ANDROID_API__ >= 29 /** * This object represents a package of data that can be sent between processes. When transacting, an @@ -49,6 +49,8 @@ typedef struct AParcel AParcel; /** * Cleans up a parcel. * + * Available since API level 29. + * * \param parcel A parcel returned by AIBinder_prepareTransaction or AIBinder_transact when a * transaction is being aborted. */ @@ -57,6 +59,8 @@ void AParcel_delete(AParcel* parcel) __INTRODUCED_IN(29); /** * Sets the position within the parcel. * + * Available since API level 29. + * * \param parcel The parcel of which to set the position. * \param position Position of the parcel to set. This must be a value returned by * AParcel_getDataPosition. Positions are constant for a given parcel between processes. @@ -69,6 +73,8 @@ binder_status_t AParcel_setDataPosition(const AParcel* parcel, int32_t position) /** * Gets the current position within the parcel. * + * Available since API level 29. + * * \param parcel The parcel of which to get the position. * * \return The size of the parcel. This will always be greater than 0. The values returned by this @@ -389,6 +395,8 @@ typedef bool (*AParcel_byteArrayAllocator)(void* arrayData, int32_t length, int8 * Writes an AIBinder to the next location in a non-null parcel. Can be null. This does not take any * refcounts of ownership of the binder from the client. * + * Available since API level 29. + * * \param parcel the parcel to write to. * \param binder the value to write to the parcel. * @@ -400,6 +408,8 @@ binder_status_t AParcel_writeStrongBinder(AParcel* parcel, AIBinder* binder) __I * Reads an AIBinder from the next location in a non-null parcel. One strong ref-count of ownership * is passed to the caller of this function. * + * Available since API level 29. + * * \param parcel the parcel to read from. * \param binder the out parameter for what is read from the parcel. This may be null. * @@ -414,12 +424,14 @@ binder_status_t AParcel_readStrongBinder(const AParcel* parcel, AIBinder** binde * * This corresponds to the SDK's android.os.ParcelFileDescriptor. * + * Available since API level 29. + * * \param parcel the parcel to write to. * \param fd the value to write to the parcel (-1 to represent a null ParcelFileDescriptor). * * \return STATUS_OK on successful write. */ -binder_status_t AParcel_writeParcelFileDescriptor(AParcel* parcel, int fd); +binder_status_t AParcel_writeParcelFileDescriptor(AParcel* parcel, int fd) __INTRODUCED_IN(29); /** * Reads an int from the next location in a non-null parcel. @@ -428,13 +440,16 @@ binder_status_t AParcel_writeParcelFileDescriptor(AParcel* parcel, int fd); * * This corresponds to the SDK's android.os.ParcelFileDescriptor. * + * Available since API level 29. + * * \param parcel the parcel to read from. * \param fd the out parameter for what is read from the parcel (or -1 to represent a null * ParcelFileDescriptor) * * \return STATUS_OK on successful write. */ -binder_status_t AParcel_readParcelFileDescriptor(const AParcel* parcel, int* fd); +binder_status_t AParcel_readParcelFileDescriptor(const AParcel* parcel, int* fd) + __INTRODUCED_IN(29); /** * Writes an AStatus object to the next location in a non-null parcel. @@ -445,6 +460,8 @@ binder_status_t AParcel_readParcelFileDescriptor(const AParcel* parcel, int* fd) * this happens or if writing the status object itself fails, the return value from this function * should be propagated to the client, and AParcel_readStatusHeader shouldn't be called. * + * Available since API level 29. + * * \param parcel the parcel to write to. * \param status the value to write to the parcel. * @@ -457,6 +474,8 @@ binder_status_t AParcel_writeStatusHeader(AParcel* parcel, const AStatus* status * Reads an AStatus from the next location in a non-null parcel. Ownership is passed to the caller * of this function. * + * Available since API level 29. + * * \param parcel the parcel to read from. * \param status the out parameter for what is read from the parcel. * @@ -470,6 +489,8 @@ binder_status_t AParcel_readStatusHeader(const AParcel* parcel, AStatus** status * * If length is -1, and string is nullptr, this will write a 'null' string to the parcel. * + * Available since API level 29. + * * \param parcel the parcel to write to. * \param string the null-terminated string to write to the parcel, at least of size 'length'. * \param length the length of the string to be written. @@ -487,6 +508,8 @@ binder_status_t AParcel_writeString(AParcel* parcel, const char* string, int32_t * the output buffer from this read. If there is a 'null' string on the binder buffer, the allocator * will be called with length -1. * + * Available since API level 29. + * * \param parcel the parcel to read from. * \param stringData some external representation of a string. * \param allocator allocator that will be called once the size of the string is known. @@ -504,6 +527,8 @@ binder_status_t AParcel_readString(const AParcel* parcel, void* stringData, * returned from this function will be used to fill out the data from the parcel. If length is -1, * this will write a 'null' string array to the binder buffer. * + * Available since API level 29. + * * \param parcel the parcel to write to. * \param arrayData some external representation of an array. * \param length the length of the array to be written. @@ -526,6 +551,8 @@ binder_status_t AParcel_writeStringArray(AParcel* parcel, const void* arrayData, * the contents of the string that is read. If the string array being read is 'null', this will * instead just pass -1 to AParcel_stringArrayAllocator. * + * Available since API level 29. + * * \param parcel the parcel to read from. * \param arrayData some external representation of an array. * \param allocator the callback that will be called with arrayData once the size of the output @@ -543,6 +570,8 @@ binder_status_t AParcel_readStringArray(const AParcel* parcel, void* arrayData, /** * Writes an array of parcelables (user-defined types) to the next location in a non-null parcel. * + * Available since API level 29. + * * \param parcel the parcel to write to. * \param arrayData an array of size 'length' (or null if length is -1, may be null if length is 0). * \param length the length of arrayData or -1 if this represents a null array. @@ -562,6 +591,8 @@ binder_status_t AParcel_writeParcelableArray(AParcel* parcel, const void* arrayD * length is greater than zero, elementReader will be called for every index to read the * corresponding parcelable. * + * Available since API level 29. + * * \param parcel the parcel to read from. * \param arrayData some external representation of an array. * \param allocator the callback that will be called to allocate the array. @@ -578,6 +609,8 @@ binder_status_t AParcel_readParcelableArray(const AParcel* parcel, void* arrayDa /** * Writes int32_t value to the next location in a non-null parcel. * + * Available since API level 29. + * * \param parcel the parcel to write to. * \param value the value to write to the parcel. * @@ -588,6 +621,8 @@ binder_status_t AParcel_writeInt32(AParcel* parcel, int32_t value) __INTRODUCED_ /** * Writes uint32_t value to the next location in a non-null parcel. * + * Available since API level 29. + * * \param parcel the parcel to write to. * \param value the value to write to the parcel. * @@ -598,6 +633,8 @@ binder_status_t AParcel_writeUint32(AParcel* parcel, uint32_t value) __INTRODUCE /** * Writes int64_t value to the next location in a non-null parcel. * + * Available since API level 29. + * * \param parcel the parcel to write to. * \param value the value to write to the parcel. * @@ -608,6 +645,8 @@ binder_status_t AParcel_writeInt64(AParcel* parcel, int64_t value) __INTRODUCED_ /** * Writes uint64_t value to the next location in a non-null parcel. * + * Available since API level 29. + * * \param parcel the parcel to write to. * \param value the value to write to the parcel. * @@ -618,6 +657,8 @@ binder_status_t AParcel_writeUint64(AParcel* parcel, uint64_t value) __INTRODUCE /** * Writes float value to the next location in a non-null parcel. * + * Available since API level 29. + * * \param parcel the parcel to write to. * \param value the value to write to the parcel. * @@ -628,6 +669,8 @@ binder_status_t AParcel_writeFloat(AParcel* parcel, float value) __INTRODUCED_IN /** * Writes double value to the next location in a non-null parcel. * + * Available since API level 29. + * * \param parcel the parcel to write to. * \param value the value to write to the parcel. * @@ -638,6 +681,8 @@ binder_status_t AParcel_writeDouble(AParcel* parcel, double value) __INTRODUCED_ /** * Writes bool value to the next location in a non-null parcel. * + * Available since API level 29. + * * \param parcel the parcel to write to. * \param value the value to write to the parcel. * @@ -648,6 +693,8 @@ binder_status_t AParcel_writeBool(AParcel* parcel, bool value) __INTRODUCED_IN(2 /** * Writes char16_t value to the next location in a non-null parcel. * + * Available since API level 29. + * * \param parcel the parcel to write to. * \param value the value to write to the parcel. * @@ -658,6 +705,8 @@ binder_status_t AParcel_writeChar(AParcel* parcel, char16_t value) __INTRODUCED_ /** * Writes int8_t value to the next location in a non-null parcel. * + * Available since API level 29. + * * \param parcel the parcel to write to. * \param value the value to write to the parcel. * @@ -668,6 +717,8 @@ binder_status_t AParcel_writeByte(AParcel* parcel, int8_t value) __INTRODUCED_IN /** * Reads into int32_t value from the next location in a non-null parcel. * + * Available since API level 29. + * * \param parcel the parcel to read from. * \param value the value to read from the parcel. * @@ -678,6 +729,8 @@ binder_status_t AParcel_readInt32(const AParcel* parcel, int32_t* value) __INTRO /** * Reads into uint32_t value from the next location in a non-null parcel. * + * Available since API level 29. + * * \param parcel the parcel to read from. * \param value the value to read from the parcel. * @@ -688,6 +741,8 @@ binder_status_t AParcel_readUint32(const AParcel* parcel, uint32_t* value) __INT /** * Reads into int64_t value from the next location in a non-null parcel. * + * Available since API level 29. + * * \param parcel the parcel to read from. * \param value the value to read from the parcel. * @@ -698,6 +753,8 @@ binder_status_t AParcel_readInt64(const AParcel* parcel, int64_t* value) __INTRO /** * Reads into uint64_t value from the next location in a non-null parcel. * + * Available since API level 29. + * * \param parcel the parcel to read from. * \param value the value to read from the parcel. * @@ -708,6 +765,8 @@ binder_status_t AParcel_readUint64(const AParcel* parcel, uint64_t* value) __INT /** * Reads into float value from the next location in a non-null parcel. * + * Available since API level 29. + * * \param parcel the parcel to read from. * \param value the value to read from the parcel. * @@ -718,6 +777,8 @@ binder_status_t AParcel_readFloat(const AParcel* parcel, float* value) __INTRODU /** * Reads into double value from the next location in a non-null parcel. * + * Available since API level 29. + * * \param parcel the parcel to read from. * \param value the value to read from the parcel. * @@ -728,6 +789,8 @@ binder_status_t AParcel_readDouble(const AParcel* parcel, double* value) __INTRO /** * Reads into bool value from the next location in a non-null parcel. * + * Available since API level 29. + * * \param parcel the parcel to read from. * \param value the value to read from the parcel. * @@ -738,6 +801,8 @@ binder_status_t AParcel_readBool(const AParcel* parcel, bool* value) __INTRODUCE /** * Reads into char16_t value from the next location in a non-null parcel. * + * Available since API level 29. + * * \param parcel the parcel to read from. * \param value the value to read from the parcel. * @@ -748,6 +813,8 @@ binder_status_t AParcel_readChar(const AParcel* parcel, char16_t* value) __INTRO /** * Reads into int8_t value from the next location in a non-null parcel. * + * Available since API level 29. + * * \param parcel the parcel to read from. * \param value the value to read from the parcel. * @@ -758,6 +825,8 @@ binder_status_t AParcel_readByte(const AParcel* parcel, int8_t* value) __INTRODU /** * Writes an array of int32_t to the next location in a non-null parcel. * + * Available since API level 29. + * * \param parcel the parcel to write to. * \param arrayData an array of size 'length' (or null if length is -1, may be null if length is 0). * \param length the length of arrayData or -1 if this represents a null array. @@ -770,6 +839,8 @@ binder_status_t AParcel_writeInt32Array(AParcel* parcel, const int32_t* arrayDat /** * Writes an array of uint32_t to the next location in a non-null parcel. * + * Available since API level 29. + * * \param parcel the parcel to write to. * \param arrayData an array of size 'length' (or null if length is -1, may be null if length is 0). * \param length the length of arrayData or -1 if this represents a null array. @@ -782,6 +853,8 @@ binder_status_t AParcel_writeUint32Array(AParcel* parcel, const uint32_t* arrayD /** * Writes an array of int64_t to the next location in a non-null parcel. * + * Available since API level 29. + * * \param parcel the parcel to write to. * \param arrayData an array of size 'length' (or null if length is -1, may be null if length is 0). * \param length the length of arrayData or -1 if this represents a null array. @@ -794,6 +867,8 @@ binder_status_t AParcel_writeInt64Array(AParcel* parcel, const int64_t* arrayDat /** * Writes an array of uint64_t to the next location in a non-null parcel. * + * Available since API level 29. + * * \param parcel the parcel to write to. * \param arrayData an array of size 'length' (or null if length is -1, may be null if length is 0). * \param length the length of arrayData or -1 if this represents a null array. @@ -806,6 +881,8 @@ binder_status_t AParcel_writeUint64Array(AParcel* parcel, const uint64_t* arrayD /** * Writes an array of float to the next location in a non-null parcel. * + * Available since API level 29. + * * \param parcel the parcel to write to. * \param arrayData an array of size 'length' (or null if length is -1, may be null if length is 0). * \param length the length of arrayData or -1 if this represents a null array. @@ -818,6 +895,8 @@ binder_status_t AParcel_writeFloatArray(AParcel* parcel, const float* arrayData, /** * Writes an array of double to the next location in a non-null parcel. * + * Available since API level 29. + * * \param parcel the parcel to write to. * \param arrayData an array of size 'length' (or null if length is -1, may be null if length is 0). * \param length the length of arrayData or -1 if this represents a null array. @@ -833,6 +912,8 @@ binder_status_t AParcel_writeDoubleArray(AParcel* parcel, const double* arrayDat * getter(arrayData, i) will be called for each i in [0, length) in order to get the underlying * values to write to the parcel. * + * Available since API level 29. + * * \param parcel the parcel to write to. * \param arrayData some external representation of an array. * \param length the length of arrayData (or -1 if this represents a null array). @@ -846,6 +927,8 @@ binder_status_t AParcel_writeBoolArray(AParcel* parcel, const void* arrayData, i /** * Writes an array of char16_t to the next location in a non-null parcel. * + * Available since API level 29. + * * \param parcel the parcel to write to. * \param arrayData an array of size 'length' (or null if length is -1, may be null if length is 0). * \param length the length of arrayData or -1 if this represents a null array. @@ -858,6 +941,8 @@ binder_status_t AParcel_writeCharArray(AParcel* parcel, const char16_t* arrayDat /** * Writes an array of int8_t to the next location in a non-null parcel. * + * Available since API level 29. + * * \param parcel the parcel to write to. * \param arrayData an array of size 'length' (or null if length is -1, may be null if length is 0). * \param length the length of arrayData or -1 if this represents a null array. @@ -874,6 +959,8 @@ binder_status_t AParcel_writeByteArray(AParcel* parcel, const int8_t* arrayData, * length is greater than zero, the buffer returned by the allocator will be filled with the * corresponding data * + * Available since API level 29. + * * \param parcel the parcel to read from. * \param arrayData some external representation of an array. * \param allocator the callback that will be called to allocate the array. @@ -890,6 +977,8 @@ binder_status_t AParcel_readInt32Array(const AParcel* parcel, void* arrayData, * length is greater than zero, the buffer returned by the allocator will be filled with the * corresponding data * + * Available since API level 29. + * * \param parcel the parcel to read from. * \param arrayData some external representation of an array. * \param allocator the callback that will be called to allocate the array. @@ -906,6 +995,8 @@ binder_status_t AParcel_readUint32Array(const AParcel* parcel, void* arrayData, * length is greater than zero, the buffer returned by the allocator will be filled with the * corresponding data * + * Available since API level 29. + * * \param parcel the parcel to read from. * \param arrayData some external representation of an array. * \param allocator the callback that will be called to allocate the array. @@ -922,6 +1013,8 @@ binder_status_t AParcel_readInt64Array(const AParcel* parcel, void* arrayData, * length is greater than zero, the buffer returned by the allocator will be filled with the * corresponding data * + * Available since API level 29. + * * \param parcel the parcel to read from. * \param arrayData some external representation of an array. * \param allocator the callback that will be called to allocate the array. @@ -938,6 +1031,8 @@ binder_status_t AParcel_readUint64Array(const AParcel* parcel, void* arrayData, * length is greater than zero, the buffer returned by the allocator will be filled with the * corresponding data * + * Available since API level 29. + * * \param parcel the parcel to read from. * \param arrayData some external representation of an array. * \param allocator the callback that will be called to allocate the array. @@ -954,6 +1049,8 @@ binder_status_t AParcel_readFloatArray(const AParcel* parcel, void* arrayData, * length is greater than zero, the buffer returned by the allocator will be filled with the * corresponding data * + * Available since API level 29. + * * \param parcel the parcel to read from. * \param arrayData some external representation of an array. * \param allocator the callback that will be called to allocate the array. @@ -969,6 +1066,8 @@ binder_status_t AParcel_readDoubleArray(const AParcel* parcel, void* arrayData, * First, allocator will be called with the length of the array. Then, for every i in [0, length), * setter(arrayData, i, x) will be called where x is the value at the associated index. * + * Available since API level 29. + * * \param parcel the parcel to read from. * \param arrayData some external representation of an array. * \param allocator the callback that will be called to allocate the array. @@ -988,6 +1087,8 @@ binder_status_t AParcel_readBoolArray(const AParcel* parcel, void* arrayData, * length is greater than zero, the buffer returned by the allocator will be filled with the * corresponding data * + * Available since API level 29. + * * \param parcel the parcel to read from. * \param arrayData some external representation of an array. * \param allocator the callback that will be called to allocate the array. @@ -1004,6 +1105,8 @@ binder_status_t AParcel_readCharArray(const AParcel* parcel, void* arrayData, * length is greater than zero, the buffer returned by the allocator will be filled with the * corresponding data * + * Available since API level 29. + * * \param parcel the parcel to read from. * \param arrayData some external representation of an array. * \param allocator the callback that will be called to allocate the array. @@ -1015,7 +1118,7 @@ binder_status_t AParcel_readByteArray(const AParcel* parcel, void* arrayData, // @END-PRIMITIVE-READ-WRITE -#endif //__ANDROID_API__ >= __ANDROID_API_Q__ +#endif //__ANDROID_API__ >= 29 __END_DECLS /** @} */ diff --git a/libs/binder/ndk/include_ndk/android/binder_status.h b/libs/binder/ndk/include_ndk/android/binder_status.h index 2671b9b6fc..78d70f87ba 100644 --- a/libs/binder/ndk/include_ndk/android/binder_status.h +++ b/libs/binder/ndk/include_ndk/android/binder_status.h @@ -30,7 +30,7 @@ #include __BEGIN_DECLS -#if __ANDROID_API__ >= __ANDROID_API_Q__ +#if __ANDROID_API__ >= 29 enum { STATUS_OK = 0, @@ -105,6 +105,8 @@ typedef struct AStatus AStatus; /** * New status which is considered a success. * + * Available since API level 29. + * * \return a newly constructed status object that the caller owns. */ __attribute__((warn_unused_result)) AStatus* AStatus_newOk() __INTRODUCED_IN(29); @@ -112,6 +114,8 @@ __attribute__((warn_unused_result)) AStatus* AStatus_newOk() __INTRODUCED_IN(29) /** * New status with exception code. * + * Available since API level 29. + * * \param exception the code that this status should represent. If this is EX_NONE, then this * constructs an non-error status object. * @@ -123,6 +127,8 @@ __attribute__((warn_unused_result)) AStatus* AStatus_fromExceptionCode(binder_ex /** * New status with exception code and message. * + * Available since API level 29. + * * \param exception the code that this status should represent. If this is EX_NONE, then this * constructs an non-error status object. * \param message the error message to associate with this status object. @@ -137,6 +143,8 @@ __attribute__((warn_unused_result)) AStatus* AStatus_fromExceptionCodeWithMessag * * This is considered to be EX_TRANSACTION_FAILED with extra information. * + * Available since API level 29. + * * \param serviceSpecific an implementation defined error code. * * \return a newly constructed status object that the caller owns. @@ -149,6 +157,8 @@ __attribute__((warn_unused_result)) AStatus* AStatus_fromServiceSpecificError( * * This is considered to be EX_TRANSACTION_FAILED with extra information. * + * Available since API level 29. + * * \param serviceSpecific an implementation defined error code. * \param message the error message to associate with this status object. * @@ -162,6 +172,8 @@ __attribute__((warn_unused_result)) AStatus* AStatus_fromServiceSpecificErrorWit * is returned by an API on AIBinder or AParcel, and that is to be returned from a method returning * an AStatus instance. * + * Available since API level 29. + * * \param a low-level error to associate with this status object. * * \return a newly constructed status object that the caller owns. @@ -173,6 +185,8 @@ __attribute__((warn_unused_result)) AStatus* AStatus_fromStatus(binder_status_t * Whether this object represents a successful transaction. If this function returns true, then * AStatus_getExceptionCode will return EX_NONE. * + * Available since API level 29. + * * \param status the status being queried. * * \return whether the status represents a successful transaction. For more details, see below. @@ -182,6 +196,8 @@ bool AStatus_isOk(const AStatus* status) __INTRODUCED_IN(29); /** * The exception that this status object represents. * + * Available since API level 29. + * * \param status the status being queried. * * \return the exception code that this object represents. @@ -194,6 +210,8 @@ binder_exception_t AStatus_getExceptionCode(const AStatus* status) __INTRODUCED_ * 0, the status object may still represent a different exception or status. To find out if this * transaction as a whole is okay, use AStatus_isOk instead. * + * Available since API level 29. + * * \param status the status being queried. * * \return the service-specific error code if the exception code is EX_SERVICE_SPECIFIC or 0. @@ -206,6 +224,8 @@ int32_t AStatus_getServiceSpecificError(const AStatus* status) __INTRODUCED_IN(2 * object may represent a different exception or a service specific error. To find out if this * transaction as a whole is okay, use AStatus_isOk instead. * + * Available since API level 29. + * * \param status the status being queried. * * \return the status code if the exception code is EX_TRANSACTION_FAILED or 0. @@ -218,6 +238,8 @@ binder_status_t AStatus_getStatus(const AStatus* status) __INTRODUCED_IN(29); * * The returned string has the lifetime of the status object passed into this function. * + * Available since API level 29. + * * \param status the status being queried. * * \return the message associated with this error. @@ -227,11 +249,13 @@ const char* AStatus_getMessage(const AStatus* status) __INTRODUCED_IN(29); /** * Deletes memory associated with the status instance. * + * Available since API level 29. + * * \param status the status to delete, returned from AStatus_newOk or one of the AStatus_from* APIs. */ void AStatus_delete(AStatus* status) __INTRODUCED_IN(29); -#endif //__ANDROID_API__ >= __ANDROID_API_Q__ +#endif //__ANDROID_API__ >= 29 __END_DECLS /** @} */ diff --git a/libs/nativewindow/include/android/hardware_buffer.h b/libs/nativewindow/include/android/hardware_buffer.h index da959e36d2..ae5e47ba97 100644 --- a/libs/nativewindow/include/android/hardware_buffer.h +++ b/libs/nativewindow/include/android/hardware_buffer.h @@ -342,6 +342,8 @@ typedef struct AHardwareBuffer AHardwareBuffer; * not compatible with its usage flags, the results are undefined and * may include program termination. * + * Available since API level 26. + * * \return 0 on success, or an error number of the allocation fails for * any reason. The returned buffer has a reference count of 1. */ @@ -352,18 +354,24 @@ int AHardwareBuffer_allocate(const AHardwareBuffer_Desc* desc, * * This prevents the object from being deleted until the last reference * is removed. + * + * Available since API level 26. */ void AHardwareBuffer_acquire(AHardwareBuffer* buffer) __INTRODUCED_IN(26); /** * Remove a reference that was previously acquired with * AHardwareBuffer_acquire() or AHardwareBuffer_allocate(). + * + * Available since API level 26. */ void AHardwareBuffer_release(AHardwareBuffer* buffer) __INTRODUCED_IN(26); /** * Return a description of the AHardwareBuffer in the passed * AHardwareBuffer_Desc struct. + * + * Available since API level 26. */ void AHardwareBuffer_describe(const AHardwareBuffer* buffer, AHardwareBuffer_Desc* outDesc) __INTRODUCED_IN(26); @@ -413,6 +421,8 @@ void AHardwareBuffer_describe(const AHardwareBuffer* buffer, * simultaneously, and the contents of the buffer behave like shared * memory. * + * Available since API level 26. + * * \return 0 on success. -EINVAL if \a buffer is NULL, the usage flags * are not a combination of AHARDWAREBUFFER_USAGE_CPU_*, or the buffer * has more than one layer. Error number if the lock fails for any other @@ -441,6 +451,8 @@ int AHardwareBuffer_lock(AHardwareBuffer* buffer, uint64_t usage, * * See the AHardwareBuffer_lock documentation for all other locking semantics. * + * Available since API level 29. + * * \return 0 on success. -EINVAL if \a buffer is NULL, the usage flags * are not a combination of AHARDWAREBUFFER_USAGE_CPU_*, or the buffer * has more than one layer. Error number if the lock fails for any other @@ -462,6 +474,8 @@ int AHardwareBuffer_lockPlanes(AHardwareBuffer* buffer, uint64_t usage, * completed before the function returned and no further operations are * necessary. * + * Available since API level 26. + * * \return 0 on success. -EINVAL if \a buffer is NULL. Error number if * the unlock fails for any reason. */ @@ -470,6 +484,8 @@ int AHardwareBuffer_unlock(AHardwareBuffer* buffer, int32_t* fence) __INTRODUCED /** * Send the AHardwareBuffer to an AF_UNIX socket. * + * Available since API level 26. + * * \return 0 on success, -EINVAL if \a buffer is NULL, or an error * number if the operation fails for any reason. */ @@ -478,6 +494,8 @@ int AHardwareBuffer_sendHandleToUnixSocket(const AHardwareBuffer* buffer, int so /** * Receive an AHardwareBuffer from an AF_UNIX socket. * + * Available since API level 26. + * * \return 0 on success, -EINVAL if \a outBuffer is NULL, or an error * number if the operation fails for any reason. */ @@ -501,6 +519,8 @@ int AHardwareBuffer_recvHandleFromUnixSocket(int socketFd, AHardwareBuffer** out * some implementations have implementation-defined limits on texture * size and layer count. * + * Available since API level 29. + * * \return 1 if the format and usage flag combination is allocatable, * 0 otherwise. */ @@ -514,6 +534,8 @@ int AHardwareBuffer_isSupported(const AHardwareBuffer_Desc* desc) __INTRODUCED_I * of the locked buffer. If the bytes per pixel or bytes per stride are unknown * or variable, or if the underlying mapper implementation does not support returning * additional information, then this call will fail with INVALID_OPERATION + * + * Available since API level 29. */ int AHardwareBuffer_lockAndGetInfo(AHardwareBuffer* buffer, uint64_t usage, int32_t fence, const ARect* rect, void** outVirtualAddress, diff --git a/libs/nativewindow/include/android/native_window.h b/libs/nativewindow/include/android/native_window.h index 6730596ec7..3e436e3b07 100644 --- a/libs/nativewindow/include/android/native_window.h +++ b/libs/nativewindow/include/android/native_window.h @@ -189,6 +189,8 @@ int32_t ANativeWindow_unlockAndPost(ANativeWindow* window); /** * Set a transform that will be applied to future buffers posted to the window. * + * Available since API level 26. + * * \param transform combination of {@link ANativeWindowTransform} flags * \return 0 for success, or -EINVAL if \p transform is invalid */ @@ -208,6 +210,8 @@ int32_t ANativeWindow_setBuffersTransform(ANativeWindow* window, int32_t transfo * measurement data instead of color images. The default dataSpace is 0, * ADATASPACE_UNKNOWN, unless it has been overridden by the producer. * + * Available since API level 28. + * * \param dataSpace data space of all buffers queued after this call. * \return 0 for success, -EINVAL if window is invalid or the dataspace is not * supported. @@ -216,6 +220,9 @@ int32_t ANativeWindow_setBuffersDataSpace(ANativeWindow* window, int32_t dataSpa /** * Get the dataspace of the buffers in window. + * + * Available since API level 28. + * * \return the dataspace of buffers in window, ADATASPACE_UNKNOWN is returned if * dataspace is unknown, or -EINVAL if window is invalid. */ -- cgit v1.2.3-59-g8ed1b From 26d3cfb571a5cc1567f53c4550fbf9ed7a5d501f Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Tue, 15 Oct 2019 17:02:32 -0700 Subject: Remove InputChannel::setToken The token is now assigned in the constructor. It is read-only. When you open inputchannelpair, the same token is assigned to both server and client side channels. Bug: 142581626 Test: presubmit Change-Id: I603603844b41f478e244b89dcdd1dab7e6260347 --- include/input/InputTransport.h | 47 ++++++++++++++-------- libs/gui/tests/EndToEndNativeInputTest.cpp | 3 +- libs/input/InputTransport.cpp | 47 +++++++++------------- libs/input/tests/InputChannel_test.cpp | 11 +++-- .../inputflinger/dispatcher/InputDispatcher.cpp | 40 +++++++++--------- .../inputflinger/tests/InputDispatcher_test.cpp | 4 +- 6 files changed, 78 insertions(+), 74 deletions(-) (limited to 'include') diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h index 1822e4a0db..94d90ad30b 100644 --- a/include/input/InputTransport.h +++ b/include/input/InputTransport.h @@ -167,11 +167,15 @@ protected: virtual ~InputChannel(); public: - static sp create(const std::string& name, android::base::unique_fd fd); + static sp create(const std::string& name, android::base::unique_fd fd, + sp token); - /* Creates a pair of input channels. + /** + * Create a pair of input channels. + * The two returned input channels are equivalent, and are labeled as "server" and "client" + * for convenience. The two input channels share the same token. * - * Returns OK on success. + * Return OK on success. */ static status_t openInputChannelPair(const std::string& name, sp& outServerChannel, sp& outClientChannel); @@ -179,46 +183,57 @@ public: inline std::string getName() const { return mName; } inline int getFd() const { return mFd.get(); } - /* Sends a message to the other endpoint. + /* Send a message to the other endpoint. * * If the channel is full then the message is guaranteed not to have been sent at all. * Try again after the consumer has sent a finished signal indicating that it has * consumed some of the pending messages from the channel. * - * Returns OK on success. - * Returns WOULD_BLOCK if the channel is full. - * Returns DEAD_OBJECT if the channel's peer has been closed. + * Return OK on success. + * Return WOULD_BLOCK if the channel is full. + * Return DEAD_OBJECT if the channel's peer has been closed. * Other errors probably indicate that the channel is broken. */ status_t sendMessage(const InputMessage* msg); - /* Receives a message sent by the other endpoint. + /* Receive a message sent by the other endpoint. * * If there is no message present, try again after poll() indicates that the fd * is readable. * - * Returns OK on success. - * Returns WOULD_BLOCK if there is no message present. - * Returns DEAD_OBJECT if the channel's peer has been closed. + * Return OK on success. + * Return WOULD_BLOCK if there is no message present. + * Return DEAD_OBJECT if the channel's peer has been closed. * Other errors probably indicate that the channel is broken. */ status_t receiveMessage(InputMessage* msg); - /* Returns a new object that has a duplicate of this channel's fd. */ + /* Return a new object that has a duplicate of this channel's fd. */ sp dup() const; status_t write(Parcel& out) const; static sp read(const Parcel& from); - sp getToken() const; - void setToken(const sp& token); + /** + * The connection token is used to identify the input connection, i.e. + * the pair of input channels that were created simultaneously. Input channels + * are always created in pairs, and the token can be used to find the server-side + * input channel from the client-side input channel, and vice versa. + * + * Do not use connection token to check equality of a specific input channel object + * to another, because two different (client and server) input channels will share the + * same connection token. + * + * Return the token that identifies this connection. + */ + sp getConnectionToken() const; private: - InputChannel(const std::string& name, android::base::unique_fd fd); + InputChannel(const std::string& name, android::base::unique_fd fd, sp token); std::string mName; android::base::unique_fd mFd; - sp mToken = nullptr; + sp mToken; }; /* diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp index 03b9cd75db..8d36ba7b70 100644 --- a/libs/gui/tests/EndToEndNativeInputTest.cpp +++ b/libs/gui/tests/EndToEndNativeInputTest.cpp @@ -69,7 +69,6 @@ public: mSurfaceControl = sc; InputChannel::openInputChannelPair("testchannels", mServerChannel, mClientChannel); - mServerChannel->setToken(new BBinder()); mInputFlinger = getInputFlinger(); mInputFlinger->registerInputChannel(mServerChannel); @@ -165,7 +164,7 @@ private: } void populateInputInfo(int width, int height) { - mInputInfo.token = mServerChannel->getToken(); + mInputInfo.token = mServerChannel->getConnectionToken(); mInputInfo.name = "Test info"; mInputInfo.layoutParamsFlags = InputWindowInfo::FLAG_NOT_TOUCH_MODAL; mInputInfo.layoutParamsType = InputWindowInfo::TYPE_BASE_APPLICATION; diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp index c4f7fe0bf2..a5dd3c0544 100644 --- a/libs/input/InputTransport.cpp +++ b/libs/input/InputTransport.cpp @@ -11,7 +11,7 @@ #define DEBUG_CHANNEL_MESSAGES 0 // Log debug messages whenever InputChannel objects are created/destroyed -#define DEBUG_CHANNEL_LIFECYCLE 0 +static constexpr bool DEBUG_CHANNEL_LIFECYCLE = false; // Log debug messages about transport actions #define DEBUG_TRANSPORT_ACTIONS 0 @@ -225,28 +225,28 @@ void InputMessage::getSanitizedCopy(InputMessage* msg) const { // --- InputChannel --- -sp InputChannel::create(const std::string& name, android::base::unique_fd fd) { +sp InputChannel::create(const std::string& name, android::base::unique_fd fd, + sp token) { const int result = fcntl(fd, F_SETFL, O_NONBLOCK); if (result != 0) { LOG_ALWAYS_FATAL("channel '%s' ~ Could not make socket non-blocking: %s", name.c_str(), strerror(errno)); return nullptr; } - return new InputChannel(name, std::move(fd)); + return new InputChannel(name, std::move(fd), token); } -InputChannel::InputChannel(const std::string& name, android::base::unique_fd fd) - : mName(name), mFd(std::move(fd)) { -#if DEBUG_CHANNEL_LIFECYCLE - ALOGD("Input channel constructed: name='%s', fd=%d", - mName.c_str(), fd); -#endif +InputChannel::InputChannel(const std::string& name, android::base::unique_fd fd, sp token) + : mName(name), mFd(std::move(fd)), mToken(token) { + if (DEBUG_CHANNEL_LIFECYCLE) { + ALOGD("Input channel constructed: name='%s', fd=%d", mName.c_str(), mFd.get()); + } } InputChannel::~InputChannel() { -#if DEBUG_CHANNEL_LIFECYCLE - ALOGD("Input channel destroyed: name='%s', fd=%d", mName.c_str(), mFd.get()); -#endif + if (DEBUG_CHANNEL_LIFECYCLE) { + ALOGD("Input channel destroyed: name='%s', fd=%d", mName.c_str(), mFd.get()); + } } status_t InputChannel::openInputChannelPair(const std::string& name, @@ -267,13 +267,15 @@ status_t InputChannel::openInputChannelPair(const std::string& name, setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize)); setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize)); + sp token = new BBinder(); + std::string serverChannelName = name + " (server)"; android::base::unique_fd serverFd(sockets[0]); - outServerChannel = InputChannel::create(serverChannelName, std::move(serverFd)); + outServerChannel = InputChannel::create(serverChannelName, std::move(serverFd), token); std::string clientChannelName = name + " (client)"; android::base::unique_fd clientFd(sockets[1]); - outClientChannel = InputChannel::create(clientChannelName, std::move(clientFd)); + outClientChannel = InputChannel::create(clientChannelName, std::move(clientFd), token); return OK; } @@ -369,7 +371,7 @@ sp InputChannel::dup() const { getName().c_str()); return nullptr; } - return InputChannel::create(mName, std::move(newFd)); + return InputChannel::create(mName, std::move(newFd), mToken); } status_t InputChannel::write(Parcel& out) const { @@ -396,24 +398,13 @@ sp InputChannel::read(const Parcel& from) { return nullptr; } - sp channel = InputChannel::create(name, std::move(rawFd)); - if (channel != nullptr) { - channel->setToken(token); - } - return channel; + return InputChannel::create(name, std::move(rawFd), token); } -sp InputChannel::getToken() const { +sp InputChannel::getConnectionToken() const { return mToken; } -void InputChannel::setToken(const sp& token) { - if (mToken != nullptr) { - ALOGE("Assigning InputChannel (%s) a second handle?", mName.c_str()); - } - mToken = token; -} - // --- InputPublisher --- InputPublisher::InputPublisher(const sp& channel) : diff --git a/libs/input/tests/InputChannel_test.cpp b/libs/input/tests/InputChannel_test.cpp index 7c331e132d..ada275d014 100644 --- a/libs/input/tests/InputChannel_test.cpp +++ b/libs/input/tests/InputChannel_test.cpp @@ -46,7 +46,8 @@ TEST_F(InputChannelTest, ConstructorAndDestructor_TakesOwnershipOfFileDescriptor android::base::unique_fd sendFd(pipe.sendFd); - sp inputChannel = InputChannel::create("channel name", std::move(sendFd)); + sp inputChannel = + InputChannel::create("channel name", std::move(sendFd), new BBinder()); EXPECT_NE(inputChannel, nullptr) << "channel should be successfully created"; EXPECT_STREQ("channel name", inputChannel->getName().c_str()) @@ -59,13 +60,11 @@ TEST_F(InputChannelTest, ConstructorAndDestructor_TakesOwnershipOfFileDescriptor TEST_F(InputChannelTest, SetAndGetToken) { Pipe pipe; + sp token = new BBinder(); sp channel = - InputChannel::create("test channel", android::base::unique_fd(pipe.sendFd)); - EXPECT_EQ(channel->getToken(), nullptr); + InputChannel::create("test channel", android::base::unique_fd(pipe.sendFd), token); - sp token = new BBinder(); - channel->setToken(token); - EXPECT_EQ(token, channel->getToken()); + EXPECT_EQ(token, channel->getConnectionToken()); } TEST_F(InputChannelTest, OpenInputChannelPair_ReturnsAPairOfConnectedChannels) { diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 24b27b9f15..58a5b3c8d3 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -1038,7 +1038,8 @@ void InputDispatcher::dispatchEventLocked(nsecs_t currentTime, EventEntry* event pokeUserActivityLocked(*eventEntry); for (const InputTarget& inputTarget : inputTargets) { - sp connection = getConnectionLocked(inputTarget.inputChannel->getToken()); + sp connection = + getConnectionLocked(inputTarget.inputChannel->getConnectionToken()); if (connection != nullptr) { prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget); } else { @@ -2142,7 +2143,7 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp& connectio } dispatchPointerDownOutsideFocus(motionEntry.source, dispatchEntry->resolvedAction, - inputTarget->inputChannel->getToken()); + inputTarget->inputChannel->getConnectionToken()); break; } @@ -2472,7 +2473,7 @@ void InputDispatcher::synthesizeCancelationEventsForMonitorsLocked( void InputDispatcher::synthesizeCancelationEventsForInputChannelLocked( const sp& channel, const CancelationOptions& options) { - sp connection = getConnectionLocked(channel->getToken()); + sp connection = getConnectionLocked(channel->getConnectionToken()); if (connection == nullptr) { return; } @@ -2514,7 +2515,7 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( InputTarget target; sp windowHandle = - getWindowHandleLocked(connection->inputChannel->getToken()); + getWindowHandleLocked(connection->inputChannel->getConnectionToken()); if (windowHandle != nullptr) { const InputWindowInfo* windowInfo = windowHandle->getInfo(); target.xOffset = -windowInfo->frameLeft; @@ -3866,7 +3867,7 @@ status_t InputDispatcher::registerInputChannel(const sp& inputChan { // acquire lock std::scoped_lock _l(mLock); - sp existingConnection = getConnectionLocked(inputChannel->getToken()); + sp existingConnection = getConnectionLocked(inputChannel->getConnectionToken()); if (existingConnection != nullptr) { ALOGW("Attempted to register already registered input channel '%s'", inputChannel->getName().c_str()); @@ -3877,7 +3878,7 @@ status_t InputDispatcher::registerInputChannel(const sp& inputChan int fd = inputChannel->getFd(); mConnectionsByFd[fd] = connection; - mInputChannelsByToken[inputChannel->getToken()] = inputChannel; + mInputChannelsByToken[inputChannel->getConnectionToken()] = inputChannel; mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this); } // release lock @@ -3897,7 +3898,7 @@ status_t InputDispatcher::registerInputMonitor(const sp& inputChan return BAD_VALUE; } - if (inputChannel->getToken() == nullptr) { + if (inputChannel->getConnectionToken() == nullptr) { ALOGW("Attempted to register input monitor without an identifying token."); return BAD_VALUE; } @@ -3906,7 +3907,7 @@ status_t InputDispatcher::registerInputMonitor(const sp& inputChan const int fd = inputChannel->getFd(); mConnectionsByFd[fd] = connection; - mInputChannelsByToken[inputChannel->getToken()] = inputChannel; + mInputChannelsByToken[inputChannel->getConnectionToken()] = inputChannel; auto& monitorsByDisplay = isGestureMonitor ? mGestureMonitorsByDisplay : mGlobalMonitorsByDisplay; @@ -3941,7 +3942,7 @@ status_t InputDispatcher::unregisterInputChannel(const sp& inputCh status_t InputDispatcher::unregisterInputChannelLocked(const sp& inputChannel, bool notify) { - sp connection = getConnectionLocked(inputChannel->getToken()); + sp connection = getConnectionLocked(inputChannel->getConnectionToken()); if (connection == nullptr) { ALOGW("Attempted to unregister already unregistered input channel '%s'", inputChannel->getName().c_str()); @@ -3950,7 +3951,7 @@ status_t InputDispatcher::unregisterInputChannelLocked(const sp& i [[maybe_unused]] const bool removed = removeByValue(mConnectionsByFd, connection); ALOG_ASSERT(removed); - mInputChannelsByToken.erase(inputChannel->getToken()); + mInputChannelsByToken.erase(inputChannel->getConnectionToken()); if (connection->monitor) { removeMonitorChannelLocked(inputChannel); @@ -4010,7 +4011,7 @@ status_t InputDispatcher::pilferPointers(const sp& token) { TouchState& state = mTouchStatesByDisplay.editValueAt(stateIndex); std::optional foundDeviceId; for (const TouchedMonitor& touchedMonitor : state.gestureMonitors) { - if (touchedMonitor.monitor.inputChannel->getToken() == token) { + if (touchedMonitor.monitor.inputChannel->getConnectionToken() == token) { foundDeviceId = state.deviceId; } } @@ -4041,7 +4042,7 @@ std::optional InputDispatcher::findGestureMonitorDisplayByTokenLocked( for (const auto& it : mGestureMonitorsByDisplay) { const std::vector& monitors = it.second; for (const Monitor& monitor : monitors) { - if (monitor.inputChannel->getToken() == token) { + if (monitor.inputChannel->getConnectionToken() == token) { return it.first; } } @@ -4056,7 +4057,7 @@ sp InputDispatcher::getConnectionLocked(const sp& inputConn for (const auto& pair : mConnectionsByFd) { const sp& connection = pair.second; - if (connection->inputChannel->getToken() == inputConnectionToken) { + if (connection->inputChannel->getConnectionToken() == inputConnectionToken) { return connection; } } @@ -4149,7 +4150,7 @@ void InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible(CommandEntry if (connection->status != Connection::STATUS_ZOMBIE) { mLock.unlock(); - mPolicy->notifyInputChannelBroken(connection->inputChannel->getToken()); + mPolicy->notifyInputChannelBroken(connection->inputChannel->getConnectionToken()); mLock.lock(); } @@ -4165,7 +4166,7 @@ void InputDispatcher::doNotifyFocusChangedLockedInterruptible(CommandEntry* comm void InputDispatcher::doNotifyANRLockedInterruptible(CommandEntry* commandEntry) { sp token = - commandEntry->inputChannel ? commandEntry->inputChannel->getToken() : nullptr; + commandEntry->inputChannel ? commandEntry->inputChannel->getConnectionToken() : nullptr; mLock.unlock(); nsecs_t newTimeout = @@ -4186,7 +4187,7 @@ void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible( android::base::Timer t; sp token = commandEntry->inputChannel != nullptr - ? commandEntry->inputChannel->getToken() + ? commandEntry->inputChannel->getConnectionToken() : nullptr; nsecs_t delay = mPolicy->interceptKeyBeforeDispatching(token, &event, entry->policyFlags); if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) { @@ -4305,7 +4306,7 @@ bool InputDispatcher::afterKeyEventLockedInterruptible(const sp& con mLock.unlock(); - mPolicy->dispatchUnhandledKey(connection->inputChannel->getToken(), &event, + mPolicy->dispatchUnhandledKey(connection->inputChannel->getConnectionToken(), &event, keyEntry->policyFlags, &event); mLock.lock(); @@ -4346,8 +4347,9 @@ bool InputDispatcher::afterKeyEventLockedInterruptible(const sp& con mLock.unlock(); - bool fallback = mPolicy->dispatchUnhandledKey(connection->inputChannel->getToken(), &event, - keyEntry->policyFlags, &event); + bool fallback = + mPolicy->dispatchUnhandledKey(connection->inputChannel->getConnectionToken(), + &event, keyEntry->policyFlags, &event); mLock.lock(); diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 7d69854868..b706a749fe 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -470,7 +470,6 @@ public: const sp& dispatcher, const std::string name, int32_t displayId) : FakeInputReceiver(dispatcher, name, displayId), mFocused(false), mFrame(Rect(0, 0, WIDTH, HEIGHT)), mLayoutParamFlags(0) { - mServerChannel->setToken(new BBinder()); mDispatcher->registerInputChannel(mServerChannel); inputApplicationHandle->updateInfo(); @@ -478,7 +477,7 @@ public: } virtual bool updateInfo() { - mInfo.token = mServerChannel ? mServerChannel->getToken() : nullptr; + mInfo.token = mServerChannel ? mServerChannel->getConnectionToken() : nullptr; mInfo.name = mName; mInfo.layoutParamsFlags = mLayoutParamFlags; mInfo.layoutParamsType = InputWindowInfo::TYPE_APPLICATION; @@ -859,7 +858,6 @@ public: FakeMonitorReceiver(const sp& dispatcher, const std::string name, int32_t displayId, bool isGestureMonitor = false) : FakeInputReceiver(dispatcher, name, displayId) { - mServerChannel->setToken(new BBinder()); mDispatcher->registerInputMonitor(mServerChannel, displayId, isGestureMonitor); } }; -- cgit v1.2.3-59-g8ed1b From cc4452292052758a6f2df841b80d9059ba164ec5 Mon Sep 17 00:00:00 2001 From: Alec Mouri Date: Tue, 22 Oct 2019 10:19:17 -0700 Subject: [AChoreographer] Split source files out from libandroid. This change is prework for allowing HWUI to depend on AChoreographer so that the dependency on DisplayEventReceiver is decoupled. AChoreographer needs to be split out from libandroid, as libandroid transitively depends on libhwui causing a circular dependency otherwise. Bug: 136262896 Test: builds Change-Id: Id60f9801cd82314065d82462e2a4286a3cb91e98 --- include/android/choreographer.h | 109 ---------- libs/nativedisplay/AChoreographer.cpp | 225 +++++++++++++++++++++ libs/nativedisplay/Android.bp | 10 + libs/nativedisplay/MODULE_LICENSE_APACHE2 | 0 libs/nativedisplay/NOTICE | 190 +++++++++++++++++ libs/nativedisplay/include/android/choreographer.h | 109 ++++++++++ libs/nativedisplay/libnativedisplay.map.txt | 10 + 7 files changed, 544 insertions(+), 109 deletions(-) delete mode 100644 include/android/choreographer.h create mode 100644 libs/nativedisplay/AChoreographer.cpp create mode 100644 libs/nativedisplay/MODULE_LICENSE_APACHE2 create mode 100644 libs/nativedisplay/NOTICE create mode 100644 libs/nativedisplay/include/android/choreographer.h create mode 100644 libs/nativedisplay/libnativedisplay.map.txt (limited to 'include') diff --git a/include/android/choreographer.h b/include/android/choreographer.h deleted file mode 100644 index 0d97e8c066..0000000000 --- a/include/android/choreographer.h +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @addtogroup Choreographer - * @{ - */ - -/** - * @file choreographer.h - */ - -#ifndef ANDROID_CHOREOGRAPHER_H -#define ANDROID_CHOREOGRAPHER_H - -#include -#include - -__BEGIN_DECLS - -struct AChoreographer; -typedef struct AChoreographer AChoreographer; - -/** - * Prototype of the function that is called when a new frame is being rendered. - * It's passed the time that the frame is being rendered as nanoseconds in the - * CLOCK_MONOTONIC time base, as well as the data pointer provided by the - * application that registered a callback. All callbacks that run as part of - * rendering a frame will observe the same frame time, so it should be used - * whenever events need to be synchronized (e.g. animations). - */ -typedef void (*AChoreographer_frameCallback)(long frameTimeNanos, void* data); - -/** - * Prototype of the function that is called when a new frame is being rendered. - * It's passed the time that the frame is being rendered as nanoseconds in the - * CLOCK_MONOTONIC time base, as well as the data pointer provided by the - * application that registered a callback. All callbacks that run as part of - * rendering a frame will observe the same frame time, so it should be used - * whenever events need to be synchronized (e.g. animations). - */ -typedef void (*AChoreographer_frameCallback64)(int64_t frameTimeNanos, void* data); - -#if __ANDROID_API__ >= 24 - -/** - * Get the AChoreographer instance for the current thread. This must be called - * on an ALooper thread. - * - * Available since API level 24. - */ -AChoreographer* AChoreographer_getInstance() __INTRODUCED_IN(24); - -/** - * Deprecated: Use AChoreographer_postFrameCallback64 instead. - */ -void AChoreographer_postFrameCallback(AChoreographer* choreographer, - AChoreographer_frameCallback callback, void* data) __INTRODUCED_IN(24) __DEPRECATED_IN(29); - -/** - * Deprecated: Use AChoreographer_postFrameCallbackDelayed64 instead. - */ -void AChoreographer_postFrameCallbackDelayed(AChoreographer* choreographer, - AChoreographer_frameCallback callback, void* data, - long delayMillis) __INTRODUCED_IN(24) __DEPRECATED_IN(29); - -#endif /* __ANDROID_API__ >= 24 */ - -#if __ANDROID_API__ >= 29 - -/** - * Power a callback to be run on the next frame. The data pointer provided will - * be passed to the callback function when it's called. - * - * Available since API level 29. - */ -void AChoreographer_postFrameCallback64(AChoreographer* choreographer, - AChoreographer_frameCallback64 callback, void* data) __INTRODUCED_IN(29); - -/** - * Post a callback to be run on the frame following the specified delay. The - * data pointer provided will be passed to the callback function when it's - * called. - * - * Available since API level 29. - */ -void AChoreographer_postFrameCallbackDelayed64(AChoreographer* choreographer, - AChoreographer_frameCallback64 callback, void* data, uint32_t delayMillis) __INTRODUCED_IN(29); - -#endif /* __ANDROID_API__ >= 29 */ - -__END_DECLS - -#endif // ANDROID_CHOREOGRAPHER_H - -/** @} */ diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp new file mode 100644 index 0000000000..63e073405f --- /dev/null +++ b/libs/nativedisplay/AChoreographer.cpp @@ -0,0 +1,225 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "Choreographer" +//#define LOG_NDEBUG 0 + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace android { + +static inline const char* toString(bool value) { + return value ? "true" : "false"; +} + +struct FrameCallback { + AChoreographer_frameCallback callback; + AChoreographer_frameCallback64 callback64; + void* data; + nsecs_t dueTime; + + inline bool operator<(const FrameCallback& rhs) const { + // Note that this is intentionally flipped because we want callbacks due sooner to be at + // the head of the queue + return dueTime > rhs.dueTime; + } +}; + + +class Choreographer : public DisplayEventDispatcher, public MessageHandler { +public: + void postFrameCallbackDelayed(AChoreographer_frameCallback cb, + AChoreographer_frameCallback64 cb64, void* data, nsecs_t delay); + + enum { + MSG_SCHEDULE_CALLBACKS = 0, + MSG_SCHEDULE_VSYNC = 1 + }; + virtual void handleMessage(const Message& message) override; + + static Choreographer* getForThread(); + +protected: + virtual ~Choreographer() = default; + +private: + explicit Choreographer(const sp& looper); + Choreographer(const Choreographer&) = delete; + + void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count) override; + void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, bool connected) override; + void dispatchConfigChanged(nsecs_t timestamp, PhysicalDisplayId displayId, + int32_t configId) override; + + void scheduleCallbacks(); + + // Protected by mLock + std::priority_queue mCallbacks; + + mutable Mutex mLock; + + const sp mLooper; + const std::thread::id mThreadId; +}; + + +static thread_local Choreographer* gChoreographer; +Choreographer* Choreographer::getForThread() { + if (gChoreographer == nullptr) { + sp looper = Looper::getForThread(); + if (!looper.get()) { + ALOGW("No looper prepared for thread"); + return nullptr; + } + gChoreographer = new Choreographer(looper); + status_t result = gChoreographer->initialize(); + if (result != OK) { + ALOGW("Failed to initialize"); + return nullptr; + } + } + return gChoreographer; +} + +Choreographer::Choreographer(const sp& looper) : + DisplayEventDispatcher(looper), mLooper(looper), mThreadId(std::this_thread::get_id()) { +} + +void Choreographer::postFrameCallbackDelayed( + AChoreographer_frameCallback cb, AChoreographer_frameCallback64 cb64, void* data, nsecs_t delay) { + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + FrameCallback callback{cb, cb64, data, now + delay}; + { + AutoMutex _l{mLock}; + mCallbacks.push(callback); + } + if (callback.dueTime <= now) { + if (std::this_thread::get_id() != mThreadId) { + Message m{MSG_SCHEDULE_VSYNC}; + mLooper->sendMessage(this, m); + } else { + scheduleVsync(); + } + } else { + Message m{MSG_SCHEDULE_CALLBACKS}; + mLooper->sendMessageDelayed(delay, this, m); + } +} + +void Choreographer::scheduleCallbacks() { + AutoMutex _{mLock}; + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + if (mCallbacks.top().dueTime <= now) { + ALOGV("choreographer %p ~ scheduling vsync", this); + scheduleVsync(); + return; + } +} + +// TODO(b/74619554): The PhysicalDisplayId is ignored because SF only emits VSYNC events for the +// internal display and DisplayEventReceiver::requestNextVsync only allows requesting VSYNC for +// the internal display implicitly. +void Choreographer::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId, uint32_t) { + std::vector callbacks{}; + { + AutoMutex _l{mLock}; + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + while (!mCallbacks.empty() && mCallbacks.top().dueTime < now) { + callbacks.push_back(mCallbacks.top()); + mCallbacks.pop(); + } + } + for (const auto& cb : callbacks) { + if (cb.callback64 != nullptr) { + cb.callback64(timestamp, cb.data); + } else if (cb.callback != nullptr) { + cb.callback(timestamp, cb.data); + } + } +} + +void Choreographer::dispatchHotplug(nsecs_t, PhysicalDisplayId displayId, bool connected) { + ALOGV("choreographer %p ~ received hotplug event (displayId=%" + ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", connected=%s), ignoring.", + this, displayId, toString(connected)); +} + +void Choreographer::dispatchConfigChanged(nsecs_t, PhysicalDisplayId displayId, + int32_t configId) { + ALOGV("choreographer %p ~ received config changed event (displayId=%" + ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", configId=%s), ignoring.", + this, displayId, toString(configId)); +} + +void Choreographer::handleMessage(const Message& message) { + switch (message.what) { + case MSG_SCHEDULE_CALLBACKS: + scheduleCallbacks(); + break; + case MSG_SCHEDULE_VSYNC: + scheduleVsync(); + break; + } +} + +} + +/* Glue for the NDK interface */ + +using android::Choreographer; + +static inline Choreographer* AChoreographer_to_Choreographer(AChoreographer* choreographer) { + return reinterpret_cast(choreographer); +} + +static inline AChoreographer* Choreographer_to_AChoreographer(Choreographer* choreographer) { + return reinterpret_cast(choreographer); +} + +AChoreographer* AChoreographer_getInstance() { + return Choreographer_to_AChoreographer(Choreographer::getForThread()); +} + +void AChoreographer_postFrameCallback(AChoreographer* choreographer, + AChoreographer_frameCallback callback, void* data) { + AChoreographer_to_Choreographer(choreographer)->postFrameCallbackDelayed( + callback, nullptr, data, 0); +} +void AChoreographer_postFrameCallbackDelayed(AChoreographer* choreographer, + AChoreographer_frameCallback callback, void* data, long delayMillis) { + AChoreographer_to_Choreographer(choreographer)->postFrameCallbackDelayed( + callback, nullptr, data, ms2ns(delayMillis)); +} +void AChoreographer_postFrameCallback64(AChoreographer* choreographer, + AChoreographer_frameCallback64 callback, void* data) { + AChoreographer_to_Choreographer(choreographer)->postFrameCallbackDelayed( + nullptr, callback, data, 0); +} +void AChoreographer_postFrameCallbackDelayed64(AChoreographer* choreographer, + AChoreographer_frameCallback64 callback, void* data, uint32_t delayMillis) { + AChoreographer_to_Choreographer(choreographer)->postFrameCallbackDelayed( + nullptr, callback, data, ms2ns(delayMillis)); +} diff --git a/libs/nativedisplay/Android.bp b/libs/nativedisplay/Android.bp index 66ebdfd6f8..45b935ac08 100644 --- a/libs/nativedisplay/Android.bp +++ b/libs/nativedisplay/Android.bp @@ -12,6 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. +ndk_headers { + name: "libachoreographer_ndk_headers", + from: "include/android", + to: "android", + srcs: ["include/android/*.h"], + license: "NOTICE", +} + cc_library_headers { name: "libnativedisplay_headers", export_include_dirs: ["include"], @@ -33,10 +41,12 @@ cc_library { ], srcs: [ + "AChoreographer.cpp", "ADisplay.cpp", ], shared_libs: [ + "libandroidfw", "libgui", "liblog", "libui", diff --git a/libs/nativedisplay/MODULE_LICENSE_APACHE2 b/libs/nativedisplay/MODULE_LICENSE_APACHE2 new file mode 100644 index 0000000000..e69de29bb2 diff --git a/libs/nativedisplay/NOTICE b/libs/nativedisplay/NOTICE new file mode 100644 index 0000000000..c5b1efa7aa --- /dev/null +++ b/libs/nativedisplay/NOTICE @@ -0,0 +1,190 @@ + + Copyright (c) 2005-2008, The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + diff --git a/libs/nativedisplay/include/android/choreographer.h b/libs/nativedisplay/include/android/choreographer.h new file mode 100644 index 0000000000..0d97e8c066 --- /dev/null +++ b/libs/nativedisplay/include/android/choreographer.h @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @addtogroup Choreographer + * @{ + */ + +/** + * @file choreographer.h + */ + +#ifndef ANDROID_CHOREOGRAPHER_H +#define ANDROID_CHOREOGRAPHER_H + +#include +#include + +__BEGIN_DECLS + +struct AChoreographer; +typedef struct AChoreographer AChoreographer; + +/** + * Prototype of the function that is called when a new frame is being rendered. + * It's passed the time that the frame is being rendered as nanoseconds in the + * CLOCK_MONOTONIC time base, as well as the data pointer provided by the + * application that registered a callback. All callbacks that run as part of + * rendering a frame will observe the same frame time, so it should be used + * whenever events need to be synchronized (e.g. animations). + */ +typedef void (*AChoreographer_frameCallback)(long frameTimeNanos, void* data); + +/** + * Prototype of the function that is called when a new frame is being rendered. + * It's passed the time that the frame is being rendered as nanoseconds in the + * CLOCK_MONOTONIC time base, as well as the data pointer provided by the + * application that registered a callback. All callbacks that run as part of + * rendering a frame will observe the same frame time, so it should be used + * whenever events need to be synchronized (e.g. animations). + */ +typedef void (*AChoreographer_frameCallback64)(int64_t frameTimeNanos, void* data); + +#if __ANDROID_API__ >= 24 + +/** + * Get the AChoreographer instance for the current thread. This must be called + * on an ALooper thread. + * + * Available since API level 24. + */ +AChoreographer* AChoreographer_getInstance() __INTRODUCED_IN(24); + +/** + * Deprecated: Use AChoreographer_postFrameCallback64 instead. + */ +void AChoreographer_postFrameCallback(AChoreographer* choreographer, + AChoreographer_frameCallback callback, void* data) __INTRODUCED_IN(24) __DEPRECATED_IN(29); + +/** + * Deprecated: Use AChoreographer_postFrameCallbackDelayed64 instead. + */ +void AChoreographer_postFrameCallbackDelayed(AChoreographer* choreographer, + AChoreographer_frameCallback callback, void* data, + long delayMillis) __INTRODUCED_IN(24) __DEPRECATED_IN(29); + +#endif /* __ANDROID_API__ >= 24 */ + +#if __ANDROID_API__ >= 29 + +/** + * Power a callback to be run on the next frame. The data pointer provided will + * be passed to the callback function when it's called. + * + * Available since API level 29. + */ +void AChoreographer_postFrameCallback64(AChoreographer* choreographer, + AChoreographer_frameCallback64 callback, void* data) __INTRODUCED_IN(29); + +/** + * Post a callback to be run on the frame following the specified delay. The + * data pointer provided will be passed to the callback function when it's + * called. + * + * Available since API level 29. + */ +void AChoreographer_postFrameCallbackDelayed64(AChoreographer* choreographer, + AChoreographer_frameCallback64 callback, void* data, uint32_t delayMillis) __INTRODUCED_IN(29); + +#endif /* __ANDROID_API__ >= 29 */ + +__END_DECLS + +#endif // ANDROID_CHOREOGRAPHER_H + +/** @} */ diff --git a/libs/nativedisplay/libnativedisplay.map.txt b/libs/nativedisplay/libnativedisplay.map.txt new file mode 100644 index 0000000000..3b29c18fc3 --- /dev/null +++ b/libs/nativedisplay/libnativedisplay.map.txt @@ -0,0 +1,10 @@ +LIBNATIVEDISPLAY { + global: + AChoreographer_getInstance; # apex # introduced=30 + AChoreographer_postFrameCallback; # apex # introduced=30 + AChoreographer_postFrameCallbackDelayed; # apex # introduced=30 + AChoreographer_postFrameCallback64; # apex # introduced=30 + AChoreographer_postFrameCallbackDelayed64; # apex # introduced=30 + local: + *; +}; -- cgit v1.2.3-59-g8ed1b From c0d3c4b24ec64e2fdef7b7a9133db8672a5df923 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 1 May 2019 14:49:39 -0700 Subject: Removing support for menu key from status bar Bug: 131763491 Test: Verified that menu key doesn't show up for legacy apps Change-Id: I1a93c2552945788331fdda376785647e7978da07 --- include/input/InputWindow.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include') diff --git a/include/input/InputWindow.h b/include/input/InputWindow.h index 916af699e0..f852cd0540 100644 --- a/include/input/InputWindow.h +++ b/include/input/InputWindow.h @@ -63,7 +63,6 @@ struct InputWindowInfo { FLAG_DISMISS_KEYGUARD = 0x00400000, FLAG_SPLIT_TOUCH = 0x00800000, FLAG_SLIPPERY = 0x20000000, - FLAG_NEEDS_MENU_KEY = 0x40000000, }; // Window types from WindowManager.LayoutParams -- cgit v1.2.3-59-g8ed1b From 727a44e37ee5ac778fc3abf0fcd849847f5d7c29 Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Sat, 23 Nov 2019 12:59:16 -0800 Subject: Use queue and unique_ptr for pooled events Currently, we are doing some manual memory management for pooled events. Refactor to use queue and unique_ptr Bug: 70668286 Test: presubmit Change-Id: Ia3f39a841a84b76243e4a68cb2cf8005e0a48ce3 --- include/input/Input.h | 14 +++++++------- libs/input/Input.cpp | 30 ++++++++++++------------------ 2 files changed, 19 insertions(+), 25 deletions(-) (limited to 'include') diff --git a/include/input/Input.h b/include/input/Input.h index cbd1a412bf..a7e706ed18 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -31,8 +31,8 @@ #include #include #include - #include +#include /* * Additional private constants not defined in ndk/ui/input.h. @@ -709,8 +709,8 @@ public: PreallocatedInputEventFactory() { } virtual ~PreallocatedInputEventFactory() { } - virtual KeyEvent* createKeyEvent() { return & mKeyEvent; } - virtual MotionEvent* createMotionEvent() { return & mMotionEvent; } + virtual KeyEvent* createKeyEvent() override { return &mKeyEvent; } + virtual MotionEvent* createMotionEvent() override { return &mMotionEvent; } private: KeyEvent mKeyEvent; @@ -725,16 +725,16 @@ public: explicit PooledInputEventFactory(size_t maxPoolSize = 20); virtual ~PooledInputEventFactory(); - virtual KeyEvent* createKeyEvent(); - virtual MotionEvent* createMotionEvent(); + virtual KeyEvent* createKeyEvent() override; + virtual MotionEvent* createMotionEvent() override; void recycle(InputEvent* event); private: const size_t mMaxPoolSize; - Vector mKeyEventPool; - Vector mMotionEventPool; + std::queue> mKeyEventPool; + std::queue> mMotionEventPool; }; } // namespace android diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index 34b305e548..c7303efd13 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -595,43 +595,37 @@ PooledInputEventFactory::PooledInputEventFactory(size_t maxPoolSize) : } PooledInputEventFactory::~PooledInputEventFactory() { - for (size_t i = 0; i < mKeyEventPool.size(); i++) { - delete mKeyEventPool.itemAt(i); - } - for (size_t i = 0; i < mMotionEventPool.size(); i++) { - delete mMotionEventPool.itemAt(i); - } } KeyEvent* PooledInputEventFactory::createKeyEvent() { - if (!mKeyEventPool.isEmpty()) { - KeyEvent* event = mKeyEventPool.top(); - mKeyEventPool.pop(); - return event; + if (mKeyEventPool.empty()) { + return new KeyEvent(); } - return new KeyEvent(); + KeyEvent* event = mKeyEventPool.front().release(); + mKeyEventPool.pop(); + return event; } MotionEvent* PooledInputEventFactory::createMotionEvent() { - if (!mMotionEventPool.isEmpty()) { - MotionEvent* event = mMotionEventPool.top(); - mMotionEventPool.pop(); - return event; + if (mMotionEventPool.empty()) { + return new MotionEvent(); } - return new MotionEvent(); + MotionEvent* event = mMotionEventPool.front().release(); + mMotionEventPool.pop(); + return event; } void PooledInputEventFactory::recycle(InputEvent* event) { switch (event->getType()) { case AINPUT_EVENT_TYPE_KEY: if (mKeyEventPool.size() < mMaxPoolSize) { - mKeyEventPool.push(static_cast(event)); + mKeyEventPool.push(std::unique_ptr(static_cast(event))); return; } break; case AINPUT_EVENT_TYPE_MOTION: if (mMotionEventPool.size() < mMaxPoolSize) { - mMotionEventPool.push(static_cast(event)); + mMotionEventPool.push(std::unique_ptr(static_cast(event))); return; } break; -- cgit v1.2.3-59-g8ed1b From 10fe676bf6423dfb17104e8f1fdacdaf1d1983da Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Mon, 25 Nov 2019 11:44:11 -0800 Subject: Add static asserts for InputMessage sizes We currently don't test the total size of InputMessage. But that's still important, to ensure it doesn't extend further than expected. Add asserts for the sizes of Header and Body. Bug: none Test: presubmit Change-Id: I2dbda2136186b22a9247fde058a9c356b07c808d --- include/input/InputTransport.h | 11 ++++++++--- libs/input/InputTransport.cpp | 4 ++-- libs/input/tests/StructLayout_test.cpp | 16 ++++++++++++++++ 3 files changed, 26 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h index 94d90ad30b..d39ee250a5 100644 --- a/include/input/InputTransport.h +++ b/include/input/InputTransport.h @@ -121,11 +121,16 @@ struct InputMessage { float yCursorPosition; uint32_t pointerCount; uint32_t empty3; - // Note that PointerCoords requires 8 byte alignment. + /** + * The "pointers" field must be the last field of the struct InputMessage. + * When we send the struct InputMessage across the socket, we are not + * writing the entire "pointers" array, but only the pointerCount portion + * of it as an optimization. Adding a field after "pointers" would break this. + */ struct Pointer { PointerProperties properties; PointerCoords coords; - } pointers[MAX_POINTERS]; + } pointers[MAX_POINTERS] __attribute__((aligned(8))); int32_t getActionId() const { uint32_t index = (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) @@ -141,7 +146,7 @@ struct InputMessage { struct Finished { uint32_t seq; - bool handled; + uint32_t handled; // actually a bool, but we must maintain 8-byte alignment inline size_t size() const { return sizeof(Finished); diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp index 8d1dd639bb..b7937dc95e 100644 --- a/libs/input/InputTransport.cpp +++ b/libs/input/InputTransport.cpp @@ -547,7 +547,7 @@ status_t InputPublisher::receiveFinishedSignal(uint32_t* outSeq, bool* outHandle return UNKNOWN_ERROR; } *outSeq = msg.body.finished.seq; - *outHandled = msg.body.finished.handled; + *outHandled = msg.body.finished.handled == 1; return OK; } @@ -1065,7 +1065,7 @@ status_t InputConsumer::sendUnchainedFinishedSignal(uint32_t seq, bool handled) InputMessage msg; msg.header.type = InputMessage::Type::FINISHED; msg.body.finished.seq = seq; - msg.body.finished.handled = handled; + msg.body.finished.handled = handled ? 1 : 0; return mChannel->sendMessage(&msg); } diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp index 8d8cf06c91..0fb6cfc204 100644 --- a/libs/input/tests/StructLayout_test.cpp +++ b/libs/input/tests/StructLayout_test.cpp @@ -73,4 +73,20 @@ void TestInputMessageAlignment() { CHECK_OFFSET(InputMessage::Body::Finished, handled, 4); } +void TestHeaderSize() { + static_assert(sizeof(InputMessage::Header) == 8); +} + +/** + * We cannot use the Body::size() method here because it is not static for + * the Motion type, where "pointerCount" variable affects the size and can change at runtime. + */ +void TestBodySize() { + static_assert(sizeof(InputMessage::Body::Key) == 64); + static_assert(sizeof(InputMessage::Body::Motion) == + offsetof(InputMessage::Body::Motion, pointers) + + sizeof(InputMessage::Body::Motion::Pointer) * MAX_POINTERS); + static_assert(sizeof(InputMessage::Body::Finished) == 8); +} + } // namespace android -- cgit v1.2.3-59-g8ed1b From 7feb2eaf82d88bd4d903f43776860015806274ce Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Mon, 25 Nov 2019 15:11:23 -0800 Subject: Add FocusEvent and InputMessage::Type::FOCUS FocusEvents will be consumed by InputConsumer on the app side. They will be used to notify app that focus has been gained / lost. They will also carry information about the current state of touch mode. Also add a new type of InputMessage with type FOCUS. This new data structure will be used to pass focus events to the apps from input across the socket. Bug: 70668286 Test: presubmit Change-Id: I88582c64ee41ecb49623b9b7f5c149eafa694788 --- include/android/input.h | 5 +- include/input/Input.h | 29 ++++++++++ include/input/InputTransport.h | 34 ++++++++--- libs/input/Input.cpp | 45 +++++++++++++++ libs/input/InputTransport.cpp | 67 +++++++++++++++++----- .../input/tests/InputPublisherAndConsumer_test.cpp | 43 ++++++++++++++ libs/input/tests/StructLayout_test.cpp | 5 ++ .../inputflinger/dispatcher/InputDispatcher.cpp | 2 +- .../inputflinger/tests/InputDispatcher_test.cpp | 4 +- 9 files changed, 209 insertions(+), 25 deletions(-) (limited to 'include') diff --git a/include/android/input.h b/include/android/input.h index ce439c6d75..f51cd79504 100644 --- a/include/android/input.h +++ b/include/android/input.h @@ -158,7 +158,10 @@ enum { AINPUT_EVENT_TYPE_KEY = 1, /** Indicates that the input event is a motion event. */ - AINPUT_EVENT_TYPE_MOTION = 2 + AINPUT_EVENT_TYPE_MOTION = 2, + + /** Focus event */ + AINPUT_EVENT_TYPE_FOCUS = 3, }; /** diff --git a/include/input/Input.h b/include/input/Input.h index a7e706ed18..f8718479f9 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -167,6 +167,8 @@ namespace android { class Parcel; #endif +const char* inputEventTypeToString(int32_t type); + /* * Flags that flow alongside events in the input dispatch system to help with certain * policy decisions such as waking from device sleep. @@ -686,6 +688,28 @@ protected: Vector mSamplePointerCoords; }; +/* + * Focus events. + */ +class FocusEvent : public InputEvent { +public: + virtual ~FocusEvent() {} + + virtual int32_t getType() const override { return AINPUT_EVENT_TYPE_FOCUS; } + + inline bool getHasFocus() const { return mHasFocus; } + + inline bool getInTouchMode() const { return mInTouchMode; } + + void initialize(bool hasFocus, bool inTouchMode); + + void initialize(const FocusEvent& from); + +protected: + bool mHasFocus; + bool mInTouchMode; +}; + /* * Input event factory. */ @@ -698,6 +722,7 @@ public: virtual KeyEvent* createKeyEvent() = 0; virtual MotionEvent* createMotionEvent() = 0; + virtual FocusEvent* createFocusEvent() = 0; }; /* @@ -711,10 +736,12 @@ public: virtual KeyEvent* createKeyEvent() override { return &mKeyEvent; } virtual MotionEvent* createMotionEvent() override { return &mMotionEvent; } + virtual FocusEvent* createFocusEvent() override { return &mFocusEvent; } private: KeyEvent mKeyEvent; MotionEvent mMotionEvent; + FocusEvent mFocusEvent; }; /* @@ -727,6 +754,7 @@ public: virtual KeyEvent* createKeyEvent() override; virtual MotionEvent* createMotionEvent() override; + virtual FocusEvent* createFocusEvent() override; void recycle(InputEvent* event); @@ -735,6 +763,7 @@ private: std::queue> mKeyEventPool; std::queue> mMotionEventPool; + std::queue> mFocusEventPool; }; } // namespace android diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h index d39ee250a5..ae47438ac8 100644 --- a/include/input/InputTransport.h +++ b/include/input/InputTransport.h @@ -64,6 +64,7 @@ struct InputMessage { KEY, MOTION, FINISHED, + FOCUS, }; struct Header { @@ -92,9 +93,7 @@ struct InputMessage { uint32_t empty2; nsecs_t downTime __attribute__((aligned(8))); - inline size_t size() const { - return sizeof(Key); - } + inline size_t size() const { return sizeof(Key); } } key; struct Motion { @@ -110,7 +109,7 @@ struct InputMessage { int32_t metaState; int32_t buttonState; MotionClassification classification; // base type: uint8_t - uint8_t empty2[3]; + uint8_t empty2[3]; // 3 bytes to fill gap created by classification int32_t edgeFlags; nsecs_t downTime __attribute__((aligned(8))); float xOffset; @@ -148,10 +147,17 @@ struct InputMessage { uint32_t seq; uint32_t handled; // actually a bool, but we must maintain 8-byte alignment - inline size_t size() const { - return sizeof(Finished); - } + inline size_t size() const { return sizeof(Finished); } } finished; + + struct Focus { + uint32_t seq; + // The following two fields take up 4 bytes total + uint16_t hasFocus; // actually a bool + uint16_t inTouchMode; // actually a bool, but we must maintain 8-byte alignment + + inline size_t size() const { return sizeof(Focus); } + } focus; } __attribute__((aligned(8))) body; bool isValid(size_t actualSize) const; @@ -294,6 +300,15 @@ public: uint32_t pointerCount, const PointerProperties* pointerProperties, const PointerCoords* pointerCoords); + /* Publishes a focus event to the input channel. + * + * Returns OK on success. + * Returns WOULD_BLOCK if the channel is full. + * Returns DEAD_OBJECT if the channel's peer has been closed. + * Other errors probably indicate that the channel is broken. + */ + status_t publishFocusEvent(uint32_t seq, bool hasFocus, bool inTouchMode); + /* Receives the finished signal from the consumer in reply to the original dispatch signal. * If a signal was received, returns the message sequence number, * and whether the consumer handled the message. @@ -349,8 +364,8 @@ public: * Returns NO_MEMORY if the event could not be created. * Other errors probably indicate that the channel is broken. */ - status_t consume(InputEventFactoryInterface* factory, bool consumeBatches, - nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent); + status_t consume(InputEventFactoryInterface* factory, bool consumeBatches, nsecs_t frameTime, + uint32_t* outSeq, InputEvent** outEvent); /* Sends a finished signal to the publisher to inform it that the message * with the specified sequence number has finished being process and whether @@ -521,6 +536,7 @@ private: static void rewriteMessage(TouchState& state, InputMessage& msg); static void initializeKeyEvent(KeyEvent* event, const InputMessage* msg); static void initializeMotionEvent(MotionEvent* event, const InputMessage* msg); + static void initializeFocusEvent(FocusEvent* event, const InputMessage* msg); static void addSample(MotionEvent* event, const InputMessage* msg); static bool canAddSample(const Batch& batch, const InputMessage* msg); static ssize_t findSampleNoLaterThan(const Batch& batch, nsecs_t time); diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index c7303efd13..8ccbc7f650 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -20,6 +20,7 @@ #include #include +#include #include #ifdef __ANDROID__ @@ -41,6 +42,21 @@ const char* motionClassificationToString(MotionClassification classification) { // --- InputEvent --- +const char* inputEventTypeToString(int32_t type) { + switch (type) { + case AINPUT_EVENT_TYPE_KEY: { + return "KEY"; + } + case AINPUT_EVENT_TYPE_MOTION: { + return "MOTION"; + } + case AINPUT_EVENT_TYPE_FOCUS: { + return "FOCUS"; + } + } + return "UNKNOWN"; +} + void InputEvent::initialize(int32_t deviceId, int32_t source, int32_t displayId) { mDeviceId = deviceId; mSource = source; @@ -587,6 +603,20 @@ int32_t MotionEvent::getAxisFromLabel(const char* label) { return getAxisByLabel(label); } +// --- FocusEvent --- + +void FocusEvent::initialize(bool hasFocus, bool inTouchMode) { + InputEvent::initialize(ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID, AINPUT_SOURCE_UNKNOWN, + ADISPLAY_ID_NONE); + mHasFocus = hasFocus; + mInTouchMode = inTouchMode; +} + +void FocusEvent::initialize(const FocusEvent& from) { + InputEvent::initialize(from); + mHasFocus = from.mHasFocus; + mInTouchMode = from.mInTouchMode; +} // --- PooledInputEventFactory --- @@ -615,6 +645,15 @@ MotionEvent* PooledInputEventFactory::createMotionEvent() { return event; } +FocusEvent* PooledInputEventFactory::createFocusEvent() { + if (mFocusEventPool.empty()) { + return new FocusEvent(); + } + FocusEvent* event = mFocusEventPool.front().release(); + mFocusEventPool.pop(); + return event; +} + void PooledInputEventFactory::recycle(InputEvent* event) { switch (event->getType()) { case AINPUT_EVENT_TYPE_KEY: @@ -629,6 +668,12 @@ void PooledInputEventFactory::recycle(InputEvent* event) { return; } break; + case AINPUT_EVENT_TYPE_FOCUS: + if (mFocusEventPool.size() < mMaxPoolSize) { + mFocusEventPool.push(std::unique_ptr(static_cast(event))); + return; + } + break; } delete event; } diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp index b7937dc95e..200e1f39d9 100644 --- a/libs/input/InputTransport.cpp +++ b/libs/input/InputTransport.cpp @@ -103,6 +103,8 @@ bool InputMessage::isValid(size_t actualSize) const { return body.motion.pointerCount > 0 && body.motion.pointerCount <= MAX_POINTERS; case Type::FINISHED: return true; + case Type::FOCUS: + return true; } } return false; @@ -116,6 +118,8 @@ size_t InputMessage::size() const { return sizeof(Header) + body.motion.size(); case Type::FINISHED: return sizeof(Header) + body.finished.size(); + case Type::FOCUS: + return sizeof(Header) + body.focus.size(); } return sizeof(Header); } @@ -220,6 +224,12 @@ void InputMessage::getSanitizedCopy(InputMessage* msg) const { msg->body.finished.handled = body.finished.handled; break; } + case InputMessage::Type::FOCUS: { + msg->body.focus.seq = body.focus.seq; + msg->body.focus.hasFocus = body.focus.hasFocus; + msg->body.focus.inTouchMode = body.focus.inTouchMode; + break; + } } } @@ -529,6 +539,23 @@ status_t InputPublisher::publishMotionEvent( return mChannel->sendMessage(&msg); } +status_t InputPublisher::publishFocusEvent(uint32_t seq, bool hasFocus, bool inTouchMode) { + if (ATRACE_ENABLED()) { + std::string message = + StringPrintf("publishFocusEvent(inputChannel=%s, hasFocus=%s, inTouchMode=%s)", + mChannel->getName().c_str(), toString(hasFocus), + toString(inTouchMode)); + ATRACE_NAME(message.c_str()); + } + + InputMessage msg; + msg.header.type = InputMessage::Type::FOCUS; + msg.body.focus.seq = seq; + msg.body.focus.hasFocus = hasFocus ? 1 : 0; + msg.body.focus.inTouchMode = inTouchMode ? 1 : 0; + return mChannel->sendMessage(&msg); +} + status_t InputPublisher::receiveFinishedSignal(uint32_t* outSeq, bool* outHandled) { if (DEBUG_TRANSPORT_ACTIONS) { ALOGD("channel '%s' publisher ~ receiveFinishedSignal", mChannel->getName().c_str()); @@ -565,8 +592,8 @@ bool InputConsumer::isTouchResamplingEnabled() { return property_get_bool(PROPERTY_RESAMPLING_ENABLED, true); } -status_t InputConsumer::consume(InputEventFactoryInterface* factory, - bool consumeBatches, nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) { +status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consumeBatches, + nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) { if (DEBUG_TRANSPORT_ACTIONS) { ALOGD("channel '%s' consumer ~ consume: consumeBatches=%s, frameTime=%" PRId64, mChannel->getName().c_str(), toString(consumeBatches), frameTime); @@ -669,19 +696,19 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, break; } - MotionEvent* motionEvent = factory->createMotionEvent(); - if (! motionEvent) return NO_MEMORY; + MotionEvent* motionEvent = factory->createMotionEvent(); + if (!motionEvent) return NO_MEMORY; - updateTouchState(mMsg); - initializeMotionEvent(motionEvent, &mMsg); - *outSeq = mMsg.body.motion.seq; - *outEvent = motionEvent; + updateTouchState(mMsg); + initializeMotionEvent(motionEvent, &mMsg); + *outSeq = mMsg.body.motion.seq; + *outEvent = motionEvent; - if (DEBUG_TRANSPORT_ACTIONS) { - ALOGD("channel '%s' consumer ~ consumed motion event, seq=%u", - mChannel->getName().c_str(), *outSeq); - } - break; + if (DEBUG_TRANSPORT_ACTIONS) { + ALOGD("channel '%s' consumer ~ consumed motion event, seq=%u", + mChannel->getName().c_str(), *outSeq); + } + break; } case InputMessage::Type::FINISHED: { @@ -689,6 +716,16 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, "InputConsumer!"); break; } + + case InputMessage::Type::FOCUS: { + FocusEvent* focusEvent = factory->createFocusEvent(); + if (!focusEvent) return NO_MEMORY; + + initializeFocusEvent(focusEvent, &mMsg); + *outSeq = mMsg.body.focus.seq; + *outEvent = focusEvent; + break; + } } } return OK; @@ -1113,6 +1150,10 @@ void InputConsumer::initializeKeyEvent(KeyEvent* event, const InputMessage* msg) msg->body.key.eventTime); } +void InputConsumer::initializeFocusEvent(FocusEvent* event, const InputMessage* msg) { + event->initialize(msg->body.focus.hasFocus == 1, msg->body.focus.inTouchMode == 1); +} + void InputConsumer::initializeMotionEvent(MotionEvent* event, const InputMessage* msg) { uint32_t pointerCount = msg->body.motion.pointerCount; PointerProperties pointerProperties[pointerCount]; diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp index a362f3281d..2fc77e97a0 100644 --- a/libs/input/tests/InputPublisherAndConsumer_test.cpp +++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp @@ -60,6 +60,7 @@ protected: void PublishAndConsumeKeyEvent(); void PublishAndConsumeMotionEvent(); + void PublishAndConsumeFocusEvent(); }; TEST_F(InputPublisherAndConsumerTest, GetChannel_ReturnsTheChannel) { @@ -256,6 +257,43 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { << "publisher receiveFinishedSignal should have set handled to consumer's reply"; } +void InputPublisherAndConsumerTest::PublishAndConsumeFocusEvent() { + status_t status; + + constexpr uint32_t seq = 15; + constexpr bool hasFocus = true; + constexpr bool inTouchMode = true; + + status = mPublisher->publishFocusEvent(seq, hasFocus, inTouchMode); + ASSERT_EQ(OK, status) << "publisher publishKeyEvent should return OK"; + + uint32_t consumeSeq; + InputEvent* event; + status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq, &event); + ASSERT_EQ(OK, status) << "consumer consume should return OK"; + + ASSERT_TRUE(event != nullptr) << "consumer should have returned non-NULL event"; + ASSERT_EQ(AINPUT_EVENT_TYPE_FOCUS, event->getType()) + << "consumer should have returned a focus event"; + + FocusEvent* focusEvent = static_cast(event); + EXPECT_EQ(seq, consumeSeq); + EXPECT_EQ(hasFocus, focusEvent->getHasFocus()); + EXPECT_EQ(inTouchMode, focusEvent->getInTouchMode()); + + status = mConsumer->sendFinishedSignal(seq, true); + ASSERT_EQ(OK, status) << "consumer sendFinishedSignal should return OK"; + + uint32_t finishedSeq = 0; + bool handled = false; + status = mPublisher->receiveFinishedSignal(&finishedSeq, &handled); + ASSERT_EQ(OK, status) << "publisher receiveFinishedSignal should return OK"; + ASSERT_EQ(seq, finishedSeq) + << "publisher receiveFinishedSignal should have returned the original sequence number"; + ASSERT_TRUE(handled) + << "publisher receiveFinishedSignal should have set handled to consumer's reply"; +} + TEST_F(InputPublisherAndConsumerTest, PublishKeyEvent_EndToEnd) { ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent()); } @@ -264,6 +302,10 @@ TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_EndToEnd) { ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent()); } +TEST_F(InputPublisherAndConsumerTest, PublishFocusEvent_EndToEnd) { + ASSERT_NO_FATAL_FAILURE(PublishAndConsumeFocusEvent()); +} + TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenSequenceNumberIsZero_ReturnsError) { status_t status; const size_t pointerCount = 1; @@ -322,6 +364,7 @@ TEST_F(InputPublisherAndConsumerTest, PublishMultipleEvents_EndToEnd) { ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent()); ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent()); ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent()); + ASSERT_NO_FATAL_FAILURE(PublishAndConsumeFocusEvent()); ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent()); ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent()); } diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp index 0fb6cfc204..9ab0dba08d 100644 --- a/libs/input/tests/StructLayout_test.cpp +++ b/libs/input/tests/StructLayout_test.cpp @@ -69,6 +69,10 @@ void TestInputMessageAlignment() { CHECK_OFFSET(InputMessage::Body::Motion, pointerCount, 88); CHECK_OFFSET(InputMessage::Body::Motion, pointers, 96); + CHECK_OFFSET(InputMessage::Body::Focus, seq, 0); + CHECK_OFFSET(InputMessage::Body::Focus, hasFocus, 4); + CHECK_OFFSET(InputMessage::Body::Focus, inTouchMode, 6); + CHECK_OFFSET(InputMessage::Body::Finished, seq, 0); CHECK_OFFSET(InputMessage::Body::Finished, handled, 4); } @@ -87,6 +91,7 @@ void TestBodySize() { offsetof(InputMessage::Body::Motion, pointers) + sizeof(InputMessage::Body::Motion::Pointer) * MAX_POINTERS); static_assert(sizeof(InputMessage::Body::Finished) == 8); + static_assert(sizeof(InputMessage::Body::Focus) == 8); } } // namespace android diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 746908ba40..9c0e08e297 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -3036,7 +3036,7 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, int32_t injec } default: - ALOGW("Cannot inject event of type %d", event->getType()); + ALOGW("Cannot inject %s events", inputEventTypeToString(event->getType())); return INPUT_EVENT_INJECTION_FAILED; } diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index ebce47f1e7..4f282611d5 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -410,7 +411,8 @@ public: ASSERT_NE(nullptr, event) << mName.c_str() << ": consumer should have returned non-NULL event."; ASSERT_EQ(expectedEventType, event->getType()) - << mName.c_str() << ": event type should match."; + << mName.c_str() << "expected " << inputEventTypeToString(expectedEventType) + << " event, got " << inputEventTypeToString(event->getType()) << " event"; EXPECT_EQ(expectedDisplayId, event->getDisplayId()); -- cgit v1.2.3-59-g8ed1b From 15375f9262b0574cfd21f5d4983843ee4170dade Mon Sep 17 00:00:00 2001 From: Marissa Wall Date: Tue, 3 Dec 2019 15:10:46 -0800 Subject: libui: remove frameworks/native/include/ui symlink Remove the global include for ui. Only symlink the two headers that are depended on by vendor code. The changes to Region and RegionHelepr were made by the linter. No code was changed. Bug: 145624276 Test: Compiles Change-Id: I1bbd99581faa703f73329ebde2da05d55950f910 --- include/private/ui/RegionHelper.h | 303 ------------------------------ include/ui | 1 - include/ui/DisplayInfo.h | 1 + include/ui/FloatRect.h | 1 + include/ui/PixelFormat.h | 1 + include/ui/Point.h | 1 + include/ui/PublicFormat.h | 1 + include/ui/Rect.h | 1 + include/ui/Region.h | 1 + include/ui/Size.h | 1 + libs/ui/Android.bp | 4 + libs/ui/GraphicBuffer.cpp | 1 - libs/ui/Region.cpp | 5 +- libs/ui/include_private/ui/RegionHelper.h | 274 +++++++++++++++++++++++++++ 14 files changed, 288 insertions(+), 308 deletions(-) delete mode 100644 include/private/ui/RegionHelper.h delete mode 120000 include/ui create mode 120000 include/ui/DisplayInfo.h create mode 120000 include/ui/FloatRect.h create mode 120000 include/ui/PixelFormat.h create mode 120000 include/ui/Point.h create mode 120000 include/ui/PublicFormat.h create mode 120000 include/ui/Rect.h create mode 120000 include/ui/Region.h create mode 120000 include/ui/Size.h create mode 100644 libs/ui/include_private/ui/RegionHelper.h (limited to 'include') diff --git a/include/private/ui/RegionHelper.h b/include/private/ui/RegionHelper.h deleted file mode 100644 index 0ec3e9474e..0000000000 --- a/include/private/ui/RegionHelper.h +++ /dev/null @@ -1,303 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_UI_PRIVATE_REGION_HELPER_H -#define ANDROID_UI_PRIVATE_REGION_HELPER_H - -#include -#include -#include - -#include - -namespace android { -// ---------------------------------------------------------------------------- - -template -class region_operator -{ -public: - typedef typename RECT::value_type TYPE; - static const TYPE max_value = std::numeric_limits::max(); - - /* - * Common boolean operations: - * value is computed as 0b101 op 0b110 - * other boolean operation are possible, simply compute - * their corresponding value with the above formulae and use - * it when instantiating a region_operator. - */ - static const uint32_t LHS = 0x5; // 0b101 - static const uint32_t RHS = 0x6; // 0b110 - enum { - op_nand = LHS & ~RHS, - op_and = LHS & RHS, - op_or = LHS | RHS, - op_xor = LHS ^ RHS - }; - - struct region { - RECT const* rects; - size_t count; - TYPE dx; - TYPE dy; - inline region(const region& rhs) - : rects(rhs.rects), count(rhs.count), dx(rhs.dx), dy(rhs.dy) { } - inline region(RECT const* _r, size_t _c) - : rects(_r), count(_c), dx(), dy() { } - inline region(RECT const* _r, size_t _c, TYPE _dx, TYPE _dy) - : rects(_r), count(_c), dx(_dx), dy(_dy) { } - }; - - class region_rasterizer { - friend class region_operator; - virtual void operator()(const RECT& rect) = 0; - public: - virtual ~region_rasterizer() { } - }; - - inline region_operator(uint32_t op, const region& lhs, const region& rhs) - : op_mask(op), spanner(lhs, rhs) - { - } - - void operator()(region_rasterizer& rasterizer) { - RECT current(Rect::EMPTY_RECT); - do { - SpannerInner spannerInner(spanner.lhs, spanner.rhs); - int inside = spanner.next(current.top, current.bottom); - spannerInner.prepare(inside); - do { - int inner_inside = spannerInner.next(current.left, current.right); - if ((op_mask >> inner_inside) & 1) { - if (current.left < current.right && - current.top < current.bottom) { - rasterizer(current); - } - } - } while(!spannerInner.isDone()); - } while(!spanner.isDone()); - } - -private: - uint32_t op_mask; - - class SpannerBase - { - public: - SpannerBase() - : lhs_head(max_value), lhs_tail(max_value), - rhs_head(max_value), rhs_tail(max_value) { - } - - enum { - lhs_before_rhs = 0, - lhs_after_rhs = 1, - lhs_coincide_rhs = 2 - }; - - protected: - TYPE lhs_head; - TYPE lhs_tail; - TYPE rhs_head; - TYPE rhs_tail; - - inline int next(TYPE& head, TYPE& tail, - bool& more_lhs, bool& more_rhs) - { - int inside; - more_lhs = false; - more_rhs = false; - if (lhs_head < rhs_head) { - inside = lhs_before_rhs; - head = lhs_head; - if (lhs_tail <= rhs_head) { - tail = lhs_tail; - more_lhs = true; - } else { - lhs_head = rhs_head; - tail = rhs_head; - } - } else if (rhs_head < lhs_head) { - inside = lhs_after_rhs; - head = rhs_head; - if (rhs_tail <= lhs_head) { - tail = rhs_tail; - more_rhs = true; - } else { - rhs_head = lhs_head; - tail = lhs_head; - } - } else { - inside = lhs_coincide_rhs; - head = lhs_head; - if (lhs_tail <= rhs_tail) { - tail = rhs_head = lhs_tail; - more_lhs = true; - } - if (rhs_tail <= lhs_tail) { - tail = lhs_head = rhs_tail; - more_rhs = true; - } - } - return inside; - } - }; - - class Spanner : protected SpannerBase - { - friend class region_operator; - region lhs; - region rhs; - - public: - inline Spanner(const region& _lhs, const region& _rhs) - : lhs(_lhs), rhs(_rhs) - { - if (lhs.count) { - SpannerBase::lhs_head = lhs.rects->top + lhs.dy; - SpannerBase::lhs_tail = lhs.rects->bottom + lhs.dy; - } - if (rhs.count) { - SpannerBase::rhs_head = rhs.rects->top + rhs.dy; - SpannerBase::rhs_tail = rhs.rects->bottom + rhs.dy; - } - } - - inline bool isDone() const { - return !rhs.count && !lhs.count; - } - - inline int next(TYPE& top, TYPE& bottom) - { - bool more_lhs = false; - bool more_rhs = false; - int inside = SpannerBase::next(top, bottom, more_lhs, more_rhs); - if (more_lhs) { - advance(lhs, SpannerBase::lhs_head, SpannerBase::lhs_tail); - } - if (more_rhs) { - advance(rhs, SpannerBase::rhs_head, SpannerBase::rhs_tail); - } - return inside; - } - - private: - static inline - void advance(region& reg, TYPE& aTop, TYPE& aBottom) { - // got to next span - size_t count = reg.count; - RECT const * rects = reg.rects; - RECT const * const end = rects + count; - const int top = rects->top; - while (rects != end && rects->top == top) { - rects++; - count--; - } - if (rects != end) { - aTop = rects->top + reg.dy; - aBottom = rects->bottom + reg.dy; - } else { - aTop = max_value; - aBottom = max_value; - } - reg.rects = rects; - reg.count = count; - } - }; - - class SpannerInner : protected SpannerBase - { - region lhs; - region rhs; - - public: - inline SpannerInner(const region& _lhs, const region& _rhs) - : lhs(_lhs), rhs(_rhs) - { - } - - inline void prepare(int inside) { - if (inside == SpannerBase::lhs_before_rhs) { - if (lhs.count) { - SpannerBase::lhs_head = lhs.rects->left + lhs.dx; - SpannerBase::lhs_tail = lhs.rects->right + lhs.dx; - } - SpannerBase::rhs_head = max_value; - SpannerBase::rhs_tail = max_value; - } else if (inside == SpannerBase::lhs_after_rhs) { - SpannerBase::lhs_head = max_value; - SpannerBase::lhs_tail = max_value; - if (rhs.count) { - SpannerBase::rhs_head = rhs.rects->left + rhs.dx; - SpannerBase::rhs_tail = rhs.rects->right + rhs.dx; - } - } else { - if (lhs.count) { - SpannerBase::lhs_head = lhs.rects->left + lhs.dx; - SpannerBase::lhs_tail = lhs.rects->right + lhs.dx; - } - if (rhs.count) { - SpannerBase::rhs_head = rhs.rects->left + rhs.dx; - SpannerBase::rhs_tail = rhs.rects->right + rhs.dx; - } - } - } - - inline bool isDone() const { - return SpannerBase::lhs_head == max_value && - SpannerBase::rhs_head == max_value; - } - - inline int next(TYPE& left, TYPE& right) - { - bool more_lhs = false; - bool more_rhs = false; - int inside = SpannerBase::next(left, right, more_lhs, more_rhs); - if (more_lhs) { - advance(lhs, SpannerBase::lhs_head, SpannerBase::lhs_tail); - } - if (more_rhs) { - advance(rhs, SpannerBase::rhs_head, SpannerBase::rhs_tail); - } - return inside; - } - - private: - static inline - void advance(region& reg, TYPE& left, TYPE& right) { - if (reg.rects && reg.count) { - const int cur_span_top = reg.rects->top; - reg.rects++; - reg.count--; - if (!reg.count || reg.rects->top != cur_span_top) { - left = max_value; - right = max_value; - } else { - left = reg.rects->left + reg.dx; - right = reg.rects->right + reg.dx; - } - } - } - }; - - Spanner spanner; -}; - -// ---------------------------------------------------------------------------- -}; - -#endif /* ANDROID_UI_PRIVATE_REGION_HELPER_H */ diff --git a/include/ui b/include/ui deleted file mode 120000 index 2fb3147669..0000000000 --- a/include/ui +++ /dev/null @@ -1 +0,0 @@ -../libs/ui/include/ui \ No newline at end of file diff --git a/include/ui/DisplayInfo.h b/include/ui/DisplayInfo.h new file mode 120000 index 0000000000..9a195eaed4 --- /dev/null +++ b/include/ui/DisplayInfo.h @@ -0,0 +1 @@ +../../libs/ui/include/ui/DisplayInfo.h \ No newline at end of file diff --git a/include/ui/FloatRect.h b/include/ui/FloatRect.h new file mode 120000 index 0000000000..d7bd737395 --- /dev/null +++ b/include/ui/FloatRect.h @@ -0,0 +1 @@ +../../libs/ui/include/ui/FloatRect.h \ No newline at end of file diff --git a/include/ui/PixelFormat.h b/include/ui/PixelFormat.h new file mode 120000 index 0000000000..4085433af6 --- /dev/null +++ b/include/ui/PixelFormat.h @@ -0,0 +1 @@ +../../libs/ui/include/ui/PixelFormat.h \ No newline at end of file diff --git a/include/ui/Point.h b/include/ui/Point.h new file mode 120000 index 0000000000..443938bc51 --- /dev/null +++ b/include/ui/Point.h @@ -0,0 +1 @@ +../../libs/ui/include/ui/Point.h \ No newline at end of file diff --git a/include/ui/PublicFormat.h b/include/ui/PublicFormat.h new file mode 120000 index 0000000000..7984c0e76a --- /dev/null +++ b/include/ui/PublicFormat.h @@ -0,0 +1 @@ +../../libs/ui/include/ui/PublicFormat.h \ No newline at end of file diff --git a/include/ui/Rect.h b/include/ui/Rect.h new file mode 120000 index 0000000000..a99c5f28c7 --- /dev/null +++ b/include/ui/Rect.h @@ -0,0 +1 @@ +../../libs/ui/include/ui/Rect.h \ No newline at end of file diff --git a/include/ui/Region.h b/include/ui/Region.h new file mode 120000 index 0000000000..2e46e0fe30 --- /dev/null +++ b/include/ui/Region.h @@ -0,0 +1 @@ +../../libs/ui/include/ui/Region.h \ No newline at end of file diff --git a/include/ui/Size.h b/include/ui/Size.h new file mode 120000 index 0000000000..c0da99bffc --- /dev/null +++ b/include/ui/Size.h @@ -0,0 +1 @@ +../../libs/ui/include/ui/Size.h \ No newline at end of file diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp index afa6a2b342..3775e2b600 100644 --- a/libs/ui/Android.bp +++ b/libs/ui/Android.bp @@ -69,6 +69,10 @@ cc_library_shared { include_dirs: [ "frameworks/native/include", ], + export_include_dirs: [ + "include", + "include_private", + ], // Uncomment the following line to enable VALIDATE_REGIONS traces //defaults: ["libui-validate-regions-defaults"], diff --git a/libs/ui/GraphicBuffer.cpp b/libs/ui/GraphicBuffer.cpp index 579e68e917..05fc590bac 100644 --- a/libs/ui/GraphicBuffer.cpp +++ b/libs/ui/GraphicBuffer.cpp @@ -27,7 +27,6 @@ #include #endif // LIBUI_IN_VNDK -#include #include #include #include diff --git a/libs/ui/Region.cpp b/libs/ui/Region.cpp index 1222cd6fad..83ebecaa67 100644 --- a/libs/ui/Region.cpp +++ b/libs/ui/Region.cpp @@ -23,11 +23,10 @@ #include +#include #include #include -#include - -#include +#include // ---------------------------------------------------------------------------- diff --git a/libs/ui/include_private/ui/RegionHelper.h b/libs/ui/include_private/ui/RegionHelper.h new file mode 100644 index 0000000000..92cfba8114 --- /dev/null +++ b/libs/ui/include_private/ui/RegionHelper.h @@ -0,0 +1,274 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_UI_PRIVATE_REGION_HELPER_H +#define ANDROID_UI_PRIVATE_REGION_HELPER_H + +#include +#include +#include + +#include + +namespace android { +// ---------------------------------------------------------------------------- + +template +class region_operator { +public: + typedef typename RECT::value_type TYPE; + static const TYPE max_value = std::numeric_limits::max(); + + /* + * Common boolean operations: + * value is computed as 0b101 op 0b110 + * other boolean operation are possible, simply compute + * their corresponding value with the above formulae and use + * it when instantiating a region_operator. + */ + static const uint32_t LHS = 0x5; // 0b101 + static const uint32_t RHS = 0x6; // 0b110 + enum { op_nand = LHS & ~RHS, op_and = LHS & RHS, op_or = LHS | RHS, op_xor = LHS ^ RHS }; + + struct region { + RECT const* rects; + size_t count; + TYPE dx; + TYPE dy; + inline region(const region& rhs) + : rects(rhs.rects), count(rhs.count), dx(rhs.dx), dy(rhs.dy) {} + inline region(RECT const* _r, size_t _c) : rects(_r), count(_c), dx(), dy() {} + inline region(RECT const* _r, size_t _c, TYPE _dx, TYPE _dy) + : rects(_r), count(_c), dx(_dx), dy(_dy) {} + }; + + class region_rasterizer { + friend class region_operator; + virtual void operator()(const RECT& rect) = 0; + + public: + virtual ~region_rasterizer() {} + }; + + inline region_operator(uint32_t op, const region& lhs, const region& rhs) + : op_mask(op), spanner(lhs, rhs) {} + + void operator()(region_rasterizer& rasterizer) { + RECT current(Rect::EMPTY_RECT); + do { + SpannerInner spannerInner(spanner.lhs, spanner.rhs); + int inside = spanner.next(current.top, current.bottom); + spannerInner.prepare(inside); + do { + int inner_inside = spannerInner.next(current.left, current.right); + if ((op_mask >> inner_inside) & 1) { + if (current.left < current.right && current.top < current.bottom) { + rasterizer(current); + } + } + } while (!spannerInner.isDone()); + } while (!spanner.isDone()); + } + +private: + uint32_t op_mask; + + class SpannerBase { + public: + SpannerBase() + : lhs_head(max_value), + lhs_tail(max_value), + rhs_head(max_value), + rhs_tail(max_value) {} + + enum { lhs_before_rhs = 0, lhs_after_rhs = 1, lhs_coincide_rhs = 2 }; + + protected: + TYPE lhs_head; + TYPE lhs_tail; + TYPE rhs_head; + TYPE rhs_tail; + + inline int next(TYPE& head, TYPE& tail, bool& more_lhs, bool& more_rhs) { + int inside; + more_lhs = false; + more_rhs = false; + if (lhs_head < rhs_head) { + inside = lhs_before_rhs; + head = lhs_head; + if (lhs_tail <= rhs_head) { + tail = lhs_tail; + more_lhs = true; + } else { + lhs_head = rhs_head; + tail = rhs_head; + } + } else if (rhs_head < lhs_head) { + inside = lhs_after_rhs; + head = rhs_head; + if (rhs_tail <= lhs_head) { + tail = rhs_tail; + more_rhs = true; + } else { + rhs_head = lhs_head; + tail = lhs_head; + } + } else { + inside = lhs_coincide_rhs; + head = lhs_head; + if (lhs_tail <= rhs_tail) { + tail = rhs_head = lhs_tail; + more_lhs = true; + } + if (rhs_tail <= lhs_tail) { + tail = lhs_head = rhs_tail; + more_rhs = true; + } + } + return inside; + } + }; + + class Spanner : protected SpannerBase { + friend class region_operator; + region lhs; + region rhs; + + public: + inline Spanner(const region& _lhs, const region& _rhs) : lhs(_lhs), rhs(_rhs) { + if (lhs.count) { + SpannerBase::lhs_head = lhs.rects->top + lhs.dy; + SpannerBase::lhs_tail = lhs.rects->bottom + lhs.dy; + } + if (rhs.count) { + SpannerBase::rhs_head = rhs.rects->top + rhs.dy; + SpannerBase::rhs_tail = rhs.rects->bottom + rhs.dy; + } + } + + inline bool isDone() const { return !rhs.count && !lhs.count; } + + inline int next(TYPE& top, TYPE& bottom) { + bool more_lhs = false; + bool more_rhs = false; + int inside = SpannerBase::next(top, bottom, more_lhs, more_rhs); + if (more_lhs) { + advance(lhs, SpannerBase::lhs_head, SpannerBase::lhs_tail); + } + if (more_rhs) { + advance(rhs, SpannerBase::rhs_head, SpannerBase::rhs_tail); + } + return inside; + } + + private: + static inline void advance(region& reg, TYPE& aTop, TYPE& aBottom) { + // got to next span + size_t count = reg.count; + RECT const* rects = reg.rects; + RECT const* const end = rects + count; + const int top = rects->top; + while (rects != end && rects->top == top) { + rects++; + count--; + } + if (rects != end) { + aTop = rects->top + reg.dy; + aBottom = rects->bottom + reg.dy; + } else { + aTop = max_value; + aBottom = max_value; + } + reg.rects = rects; + reg.count = count; + } + }; + + class SpannerInner : protected SpannerBase { + region lhs; + region rhs; + + public: + inline SpannerInner(const region& _lhs, const region& _rhs) : lhs(_lhs), rhs(_rhs) {} + + inline void prepare(int inside) { + if (inside == SpannerBase::lhs_before_rhs) { + if (lhs.count) { + SpannerBase::lhs_head = lhs.rects->left + lhs.dx; + SpannerBase::lhs_tail = lhs.rects->right + lhs.dx; + } + SpannerBase::rhs_head = max_value; + SpannerBase::rhs_tail = max_value; + } else if (inside == SpannerBase::lhs_after_rhs) { + SpannerBase::lhs_head = max_value; + SpannerBase::lhs_tail = max_value; + if (rhs.count) { + SpannerBase::rhs_head = rhs.rects->left + rhs.dx; + SpannerBase::rhs_tail = rhs.rects->right + rhs.dx; + } + } else { + if (lhs.count) { + SpannerBase::lhs_head = lhs.rects->left + lhs.dx; + SpannerBase::lhs_tail = lhs.rects->right + lhs.dx; + } + if (rhs.count) { + SpannerBase::rhs_head = rhs.rects->left + rhs.dx; + SpannerBase::rhs_tail = rhs.rects->right + rhs.dx; + } + } + } + + inline bool isDone() const { + return SpannerBase::lhs_head == max_value && SpannerBase::rhs_head == max_value; + } + + inline int next(TYPE& left, TYPE& right) { + bool more_lhs = false; + bool more_rhs = false; + int inside = SpannerBase::next(left, right, more_lhs, more_rhs); + if (more_lhs) { + advance(lhs, SpannerBase::lhs_head, SpannerBase::lhs_tail); + } + if (more_rhs) { + advance(rhs, SpannerBase::rhs_head, SpannerBase::rhs_tail); + } + return inside; + } + + private: + static inline void advance(region& reg, TYPE& left, TYPE& right) { + if (reg.rects && reg.count) { + const int cur_span_top = reg.rects->top; + reg.rects++; + reg.count--; + if (!reg.count || reg.rects->top != cur_span_top) { + left = max_value; + right = max_value; + } else { + left = reg.rects->left + reg.dx; + right = reg.rects->right + reg.dx; + } + } + } + }; + + Spanner spanner; +}; + +// ---------------------------------------------------------------------------- +}; // namespace android + +#endif /* ANDROID_UI_PRIVATE_REGION_HELPER_H */ -- cgit v1.2.3-59-g8ed1b From d041c5d9e162fbac4ef92c1d84a1bcd035fb8455 Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Fri, 3 May 2019 17:11:33 -0700 Subject: Default TV remote keys to no-wake For Android TV, we want the default behavior of keys to not wake up the device. We want to be able to whitelist keys if wake is desired. This differs from the behavior on mobile where the default is to wake the device unless its a media key. We separate by means of the idc file. Specifying "keyboard.doNotWakeByDefault=1" in the idc would indicate the device is a TV remote and should go through corresponding path. Bug: 144979700 Test: Change idc/kl files. Rebuild and examine 'dumpsys input' on key presses. atest inputflinger_tests Change-Id: I4168c4c21fd901ca0402e0211f636b06234b9a85 --- include/input/InputEventLabels.h | 11 ++- .../reader/mapper/KeyboardInputMapper.cpp | 11 ++- .../reader/mapper/KeyboardInputMapper.h | 1 + services/inputflinger/tests/InputReader_test.cpp | 78 ++++++++++++++++++++++ 4 files changed, 92 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/input/InputEventLabels.h b/include/input/InputEventLabels.h index eaa562bb7b..b327d76b1c 100644 --- a/include/input/InputEventLabels.h +++ b/include/input/InputEventLabels.h @@ -405,13 +405,12 @@ static const InputEventLabel LEDS[] = { { nullptr, 0 } }; -static const InputEventLabel FLAGS[] = { - DEFINE_FLAG(VIRTUAL), - DEFINE_FLAG(FUNCTION), - DEFINE_FLAG(GESTURE), +static const InputEventLabel FLAGS[] = {DEFINE_FLAG(VIRTUAL), + DEFINE_FLAG(FUNCTION), + DEFINE_FLAG(GESTURE), + DEFINE_FLAG(WAKE), - { nullptr, 0 } -}; + {nullptr, 0}}; static int lookupValueByLabel(const char* literal, const InputEventLabel *list) { while (list->literal) { diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp index f51d4a0d4a..348a7ada0e 100644 --- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp +++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp @@ -183,6 +183,9 @@ void KeyboardInputMapper::configureParameters() { mParameters.handlesKeyRepeat = false; config.tryGetProperty(String8("keyboard.handlesKeyRepeat"), mParameters.handlesKeyRepeat); + + mParameters.doNotWakeByDefault = false; + config.tryGetProperty(String8("keyboard.doNotWakeByDefault"), mParameters.doNotWakeByDefault); } void KeyboardInputMapper::dumpParameters(std::string& dump) { @@ -331,10 +334,12 @@ void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t scanCode, // Key down on external an keyboard should wake the device. // We don't do this for internal keyboards to prevent them from waking up in your pocket. - // For internal keyboards, the key layout file should specify the policy flags for - // each wake key individually. + // For internal keyboards and devices for which the default wake behavior is explicitly + // prevented (e.g. TV remotes), the key layout file should specify the policy flags for each + // wake key individually. // TODO: Use the input device configuration to control this behavior more finely. - if (down && getDevice()->isExternal() && !isMediaKey(keyCode)) { + if (down && getDevice()->isExternal() && !mParameters.doNotWakeByDefault && + !isMediaKey(keyCode)) { policyFlags |= POLICY_FLAG_WAKE; } diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.h b/services/inputflinger/reader/mapper/KeyboardInputMapper.h index de2a377e9a..badbcb26cf 100644 --- a/services/inputflinger/reader/mapper/KeyboardInputMapper.h +++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.h @@ -73,6 +73,7 @@ private: struct Parameters { bool orientationAware; bool handlesKeyRepeat; + bool doNotWakeByDefault; } mParameters; void configureParameters(); diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index d6624c9f5a..1fc8217df8 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -2580,6 +2580,84 @@ TEST_F(KeyboardInputMapperTest, Configure_AssignsDisplayPort) { AKEYCODE_DPAD_LEFT, newDisplayId)); } +TEST_F(KeyboardInputMapperTest, ExternalDevice_WakeBehavior) { + // For external devices, non-media keys will trigger wake on key down. Media keys need to be + // marked as WAKE in the keylayout file to trigger wake. + mDevice->setExternal(true); + + mFakeEventHub->addKey(DEVICE_ID, KEY_HOME, 0, AKEYCODE_HOME, 0); + mFakeEventHub->addKey(DEVICE_ID, KEY_PLAY, 0, AKEYCODE_MEDIA_PLAY, 0); + mFakeEventHub->addKey(DEVICE_ID, KEY_PLAYPAUSE, 0, AKEYCODE_MEDIA_PLAY_PAUSE, POLICY_FLAG_WAKE); + + KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, AINPUT_SOURCE_KEYBOARD, + AINPUT_KEYBOARD_TYPE_ALPHABETIC); + addMapperAndConfigure(mapper); + + process(mapper, ARBITRARY_TIME, EV_KEY, KEY_HOME, 1); + NotifyKeyArgs args; + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); + ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags); + + process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_HOME, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); + ASSERT_EQ(uint32_t(0), args.policyFlags); + + process(mapper, ARBITRARY_TIME, EV_KEY, KEY_PLAY, 1); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); + ASSERT_EQ(uint32_t(0), args.policyFlags); + + process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_PLAY, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); + ASSERT_EQ(uint32_t(0), args.policyFlags); + + process(mapper, ARBITRARY_TIME, EV_KEY, KEY_PLAYPAUSE, 1); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); + ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags); + + process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_PLAYPAUSE, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); + ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags); +} + +TEST_F(KeyboardInputMapperTest, ExternalDevice_DoNotWakeByDefaultBehavior) { + // Tv Remote key's wake behavior is prescribed by the keylayout file. + mDevice->setExternal(true); + + mFakeEventHub->addKey(DEVICE_ID, KEY_HOME, 0, AKEYCODE_HOME, POLICY_FLAG_WAKE); + mFakeEventHub->addKey(DEVICE_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0); + mFakeEventHub->addKey(DEVICE_ID, KEY_PLAY, 0, AKEYCODE_MEDIA_PLAY, POLICY_FLAG_WAKE); + + KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, AINPUT_SOURCE_KEYBOARD, + AINPUT_KEYBOARD_TYPE_ALPHABETIC); + addConfigurationProperty("keyboard.doNotWakeByDefault", "1"); + addMapperAndConfigure(mapper); + + process(mapper, ARBITRARY_TIME, EV_KEY, KEY_HOME, 1); + NotifyKeyArgs args; + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); + ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags); + + process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_HOME, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); + ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags); + + process(mapper, ARBITRARY_TIME, EV_KEY, KEY_DOWN, 1); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); + ASSERT_EQ(uint32_t(0), args.policyFlags); + + process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_DOWN, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); + ASSERT_EQ(uint32_t(0), args.policyFlags); + + process(mapper, ARBITRARY_TIME, EV_KEY, KEY_PLAY, 1); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); + ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags); + + process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_PLAY, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); + ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags); +} + // --- CursorInputMapperTest --- class CursorInputMapperTest : public InputMapperTest { -- cgit v1.2.3-59-g8ed1b From 06c97753112bb7a03d6e58c3774b2e44d19d7681 Mon Sep 17 00:00:00 2001 From: Leon Scroggins III Date: Tue, 3 Sep 2019 09:52:43 -0400 Subject: Add AndroidBitmap_getDataSpace Bug:135133301 Test: I7a5fcb726fba0c832bbb86a424d7534a7cfa35b6 This supplements AndroidBitmap_getInfo, allowing NDK clients to know how to interpret the colors in an android.graphics.Bitmap. Change-Id: Ia46dfb39d0f2708ce873343ec74bcc52e7bccd3a --- include/android/bitmap.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'include') diff --git a/include/android/bitmap.h b/include/android/bitmap.h index 01cf2f88ea..41718b2904 100644 --- a/include/android/bitmap.h +++ b/include/android/bitmap.h @@ -100,6 +100,19 @@ typedef struct { int AndroidBitmap_getInfo(JNIEnv* env, jobject jbitmap, AndroidBitmapInfo* info); +#if __ANDROID_API__ >= 30 + +/** + * Given a java bitmap object, return its ADataSpace. + * + * Note that ADataSpace only exposes a few values. This may return + * ADATASPACE_UNKNOWN, even for Named ColorSpaces, if they have no + * corresponding ADataSpace. + */ +int32_t AndroidBitmap_getDataSpace(JNIEnv* env, jobject jbitmap) __INTRODUCED_IN(30); + +#endif // __ANDROID_API__ >= 30 + /** * Given a java bitmap object, attempt to lock the pixel address. * Locking will ensure that the memory for the pixels will not move -- cgit v1.2.3-59-g8ed1b From af87b3e09141558215e28f84d954590b5e7f2f16 Mon Sep 17 00:00:00 2001 From: chaviw Date: Tue, 1 Oct 2019 16:59:28 -0700 Subject: Added input support for cloned layers This was done with a few changes: 1. Added a layerId in the input info so the InputInfo objects can be uniquely identified as per layer 2. When setting input info in InputDispatcher, compare InputInfo objects using layer id instead of input token. 3. Updated the crop region for layers based on the cloned hierarchy so the input is cropped to the correct region. Bug: 140756730 Test: InputDispatcherMultiWindowSameTokenTests Change-Id: I980f5d29d091fecb407f5cd6a289615505800927 --- include/input/InputWindow.h | 6 + libs/input/InputWindow.cpp | 2 + libs/input/tests/InputWindow_test.cpp | 2 + .../inputflinger/dispatcher/InputDispatcher.cpp | 22 ++- .../inputflinger/tests/InputDispatcher_test.cpp | 149 +++++++++++++++++++++ services/surfaceflinger/BufferLayer.cpp | 15 ++- services/surfaceflinger/Layer.cpp | 63 ++++++--- services/surfaceflinger/Layer.h | 7 +- 8 files changed, 239 insertions(+), 27 deletions(-) (limited to 'include') diff --git a/include/input/InputWindow.h b/include/input/InputWindow.h index f852cd0540..cbd64d520e 100644 --- a/include/input/InputWindow.h +++ b/include/input/InputWindow.h @@ -119,7 +119,11 @@ struct InputWindowInfo { /* These values are filled in by the WM and passed through SurfaceFlinger * unless specified otherwise. */ + // This value should NOT be used to uniquely identify the window. There may be different + // input windows that have the same token. sp token; + // This uniquely identifies the input window. + int32_t id = 0; std::string name; int32_t layoutParamsFlags; int32_t layoutParamsType; @@ -203,6 +207,8 @@ public: sp getToken() const; + int32_t getId() const { return mInfo.id; } + sp getApplicationToken() { return mInfo.applicationInfo.token; } diff --git a/libs/input/InputWindow.cpp b/libs/input/InputWindow.cpp index ec28757933..74a05055eb 100644 --- a/libs/input/InputWindow.cpp +++ b/libs/input/InputWindow.cpp @@ -73,6 +73,7 @@ status_t InputWindowInfo::write(Parcel& output) const { status_t s = output.writeStrongBinder(token); if (s != OK) return s; + output.writeInt32(id); output.writeString8(String8(name.c_str())); output.writeInt32(layoutParamsFlags); output.writeInt32(layoutParamsType); @@ -116,6 +117,7 @@ InputWindowInfo InputWindowInfo::read(const Parcel& from) { } ret.token = token; + ret.id = from.readInt32(); ret.name = from.readString8().c_str(); ret.layoutParamsFlags = from.readInt32(); ret.layoutParamsType = from.readInt32(); diff --git a/libs/input/tests/InputWindow_test.cpp b/libs/input/tests/InputWindow_test.cpp index 6db18abacf..cdc81d2f04 100644 --- a/libs/input/tests/InputWindow_test.cpp +++ b/libs/input/tests/InputWindow_test.cpp @@ -40,6 +40,7 @@ TEST(InputWindowInfo, Parcelling) { sp touchableRegionCropHandle = new BBinder(); InputWindowInfo i; i.token = new BBinder(); + i.id = 1; i.name = "Foobar"; i.layoutParamsFlags = 7; i.layoutParamsType = 39; @@ -72,6 +73,7 @@ TEST(InputWindowInfo, Parcelling) { p.setDataPosition(0); InputWindowInfo i2 = InputWindowInfo::read(p); ASSERT_EQ(i.token, i2.token); + ASSERT_EQ(i.id, i2.id); ASSERT_EQ(i.name, i2.name); ASSERT_EQ(i.layoutParamsFlags, i2.layoutParamsFlags); ASSERT_EQ(i.layoutParamsType, i2.layoutParamsType); diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 5a49b5e16a..116625c03e 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -240,6 +240,18 @@ static bool removeByValue(std::unordered_map& map, const V& value) { return removed; } +static bool haveSameToken(const sp& first, const sp& second) { + if (first == second) { + return true; + } + + if (first == nullptr || second == nullptr) { + return false; + } + + return first->getToken() == second->getToken(); +} + // --- InputDispatcherThread --- class InputDispatcher::InputDispatcherThread : public Thread { @@ -3278,9 +3290,9 @@ void InputDispatcher::updateWindowHandlesForDisplayLocked( // Since we compare the pointer of input window handles across window updates, we need // to make sure the handle object for the same window stays unchanged across updates. const std::vector>& oldHandles = getWindowHandlesLocked(displayId); - std::unordered_map, sp, IBinderHash> oldHandlesByTokens; + std::unordered_map> oldHandlesById; for (const sp& handle : oldHandles) { - oldHandlesByTokens[handle->getToken()] = handle; + oldHandlesById[handle->getId()] = handle; } std::vector> newHandles; @@ -3311,8 +3323,8 @@ void InputDispatcher::updateWindowHandlesForDisplayLocked( continue; } - if (oldHandlesByTokens.find(handle->getToken()) != oldHandlesByTokens.end()) { - const sp oldHandle = oldHandlesByTokens.at(handle->getToken()); + if (oldHandlesById.find(handle->getId()) != oldHandlesById.end()) { + const sp& oldHandle = oldHandlesById.at(handle->getId()); oldHandle->updateFrom(handle); newHandles.push_back(oldHandle); } else { @@ -3370,7 +3382,7 @@ void InputDispatcher::setInputWindows(const std::vector>& sp oldFocusedWindowHandle = getValueByKey(mFocusedWindowHandlesByDisplay, displayId); - if (oldFocusedWindowHandle != newFocusedWindowHandle) { + if (!haveSameToken(oldFocusedWindowHandle, newFocusedWindowHandle)) { if (oldFocusedWindowHandle != nullptr) { if (DEBUG_FOCUS) { ALOGD("Focus left window: %s in display %" PRId32, diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index b4d7608770..c25122c72b 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -526,6 +526,7 @@ public: mInfo.applicationInfo = *inputApplicationHandle->getInfo(); mInfo.token = token; + mInfo.id = 0; mInfo.name = name; mInfo.layoutParamsFlags = 0; mInfo.layoutParamsType = InputWindowInfo::TYPE_APPLICATION; @@ -564,6 +565,13 @@ public: void setLayoutParamFlags(int32_t flags) { mInfo.layoutParamsFlags = flags; } + void setId(int32_t id) { mInfo.id = id; } + + void setWindowScale(float xScale, float yScale) { + mInfo.windowXScale = xScale; + mInfo.windowYScale = yScale; + } + void consumeKeyDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) { consumeEvent(AINPUT_EVENT_TYPE_KEY, AKEY_EVENT_ACTION_DOWN, expectedDisplayId, expectedFlags); @@ -586,12 +594,21 @@ public: expectedFlags); } + InputEvent* consume() { + if (mInputReceiver == nullptr) { + return nullptr; + } + return mInputReceiver->consume(); + } + void assertNoEvents() { ASSERT_NE(mInputReceiver, nullptr) << "Call 'assertNoEvents' on a window with an InputReceiver"; mInputReceiver->assertNoEvents(); } + sp getToken() { return mInfo.token; } + private: std::unique_ptr mInputReceiver; }; @@ -667,6 +684,10 @@ static NotifyKeyArgs generateKeyArgs(int32_t action, int32_t displayId = ADISPLA static NotifyMotionArgs generateMotionArgs(int32_t action, int32_t source, int32_t displayId, const std::vector& points) { size_t pointerCount = points.size(); + if (action == AMOTION_EVENT_ACTION_DOWN || action == AMOTION_EVENT_ACTION_UP) { + EXPECT_EQ(1U, pointerCount) << "Actions DOWN and UP can only contain a single pointer"; + } + PointerProperties pointerProperties[pointerCount]; PointerCoords pointerCoords[pointerCount]; @@ -1291,4 +1312,132 @@ TEST_F(InputDispatcherOnPointerDownOutsideFocus, mFakePolicy->assertOnPointerDownWasNotCalled(); } +// These tests ensures we can send touch events to a single client when there are multiple input +// windows that point to the same client token. +class InputDispatcherMultiWindowSameTokenTests : public InputDispatcherTest { + virtual void SetUp() override { + InputDispatcherTest::SetUp(); + + sp application = new FakeApplicationHandle(); + mWindow1 = new FakeWindowHandle(application, mDispatcher, "Fake Window 1", + ADISPLAY_ID_DEFAULT); + // Adding FLAG_NOT_TOUCH_MODAL otherwise all taps will go to the top most window. + // We also need FLAG_SPLIT_TOUCH or we won't be able to get touches for both windows. + mWindow1->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL | + InputWindowInfo::FLAG_SPLIT_TOUCH); + mWindow1->setId(0); + mWindow1->setFrame(Rect(0, 0, 100, 100)); + + mWindow2 = new FakeWindowHandle(application, mDispatcher, "Fake Window 2", + ADISPLAY_ID_DEFAULT, mWindow1->getToken()); + mWindow2->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL | + InputWindowInfo::FLAG_SPLIT_TOUCH); + mWindow2->setId(1); + mWindow2->setFrame(Rect(100, 100, 200, 200)); + + mDispatcher->setInputWindows({mWindow1, mWindow2}, ADISPLAY_ID_DEFAULT); + } + +protected: + sp mWindow1; + sp mWindow2; + + // Helper function to convert the point from screen coordinates into the window's space + static PointF getPointInWindow(const InputWindowInfo* windowInfo, const PointF& point) { + float x = windowInfo->windowXScale * (point.x - windowInfo->frameLeft); + float y = windowInfo->windowYScale * (point.y - windowInfo->frameTop); + return {x, y}; + } + + void consumeMotionEvent(const sp& window, int32_t expectedAction, + const std::vector& points) { + std::string name = window->mName; + InputEvent* event = window->consume(); + + ASSERT_NE(nullptr, event) << name.c_str() + << ": consumer should have returned non-NULL event."; + + ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, event->getType()) + << name.c_str() << "expected " << inputEventTypeToString(AINPUT_EVENT_TYPE_MOTION) + << " event, got " << inputEventTypeToString(event->getType()) << " event"; + + const MotionEvent& motionEvent = static_cast(*event); + EXPECT_EQ(expectedAction, motionEvent.getAction()); + + for (size_t i = 0; i < points.size(); i++) { + float expectedX = points[i].x; + float expectedY = points[i].y; + + EXPECT_EQ(expectedX, motionEvent.getX(i)) + << "expected " << expectedX << " for x[" << i << "] coord of " << name.c_str() + << ", got " << motionEvent.getX(i); + EXPECT_EQ(expectedY, motionEvent.getY(i)) + << "expected " << expectedY << " for y[" << i << "] coord of " << name.c_str() + << ", got " << motionEvent.getY(i); + } + } +}; + +TEST_F(InputDispatcherMultiWindowSameTokenTests, SingleTouchSameScale) { + // Touch Window 1 + PointF touchedPoint = {10, 10}; + PointF expectedPoint = getPointInWindow(mWindow1->getInfo(), touchedPoint); + + NotifyMotionArgs motionArgs = + generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, {touchedPoint}); + mDispatcher->notifyMotion(&motionArgs); + consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, {expectedPoint}); + + // Release touch on Window 1 + motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, {touchedPoint}); + mDispatcher->notifyMotion(&motionArgs); + // consume the UP event + consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_UP, {expectedPoint}); + + // Touch Window 2 + touchedPoint = {150, 150}; + expectedPoint = getPointInWindow(mWindow2->getInfo(), touchedPoint); + + motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, {touchedPoint}); + mDispatcher->notifyMotion(&motionArgs); + + // Consuming from window1 since it's the window that has the InputReceiver + consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, {expectedPoint}); +} + +TEST_F(InputDispatcherMultiWindowSameTokenTests, SingleTouchDifferentScale) { + mWindow2->setWindowScale(0.5f, 0.5f); + + // Touch Window 1 + PointF touchedPoint = {10, 10}; + PointF expectedPoint = getPointInWindow(mWindow1->getInfo(), touchedPoint); + + NotifyMotionArgs motionArgs = + generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, {touchedPoint}); + mDispatcher->notifyMotion(&motionArgs); + consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, {expectedPoint}); + + // Release touch on Window 1 + motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, {touchedPoint}); + mDispatcher->notifyMotion(&motionArgs); + // consume the UP event + consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_UP, {expectedPoint}); + + // Touch Window 2 + touchedPoint = {150, 150}; + expectedPoint = getPointInWindow(mWindow2->getInfo(), touchedPoint); + + motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, {touchedPoint}); + mDispatcher->notifyMotion(&motionArgs); + + // Consuming from window1 since it's the window that has the InputReceiver + consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, {expectedPoint}); +} + } // namespace android::inputdispatcher diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp index 054acc5069..bdecdb78d0 100644 --- a/services/surfaceflinger/BufferLayer.cpp +++ b/services/surfaceflinger/BufferLayer.cpp @@ -770,17 +770,20 @@ void BufferLayer::updateCloneBufferInfo() { // After buffer info is updated, the drawingState from the real layer needs to be copied into // the cloned. This is because some properties of drawingState can change when latchBuffer is - // called. However, copying the drawingState would also overwrite the cloned layer's relatives. - // Therefore, temporarily store the relatives so they can be set in the cloned drawingState - // again. + // called. However, copying the drawingState would also overwrite the cloned layer's relatives + // and touchableRegionCrop. Therefore, temporarily store the relatives so they can be set in + // the cloned drawingState again. wp tmpZOrderRelativeOf = mDrawingState.zOrderRelativeOf; SortedVector> tmpZOrderRelatives = mDrawingState.zOrderRelatives; + wp tmpTouchableRegionCrop = mDrawingState.touchableRegionCrop; + InputWindowInfo tmpInputInfo = mDrawingState.inputInfo; + mDrawingState = clonedFrom->mDrawingState; - // TODO: (b/140756730) Ignore input for now since InputDispatcher doesn't support multiple - // InputWindows per client token yet. - mDrawingState.inputInfo.token = nullptr; + + mDrawingState.touchableRegionCrop = tmpTouchableRegionCrop; mDrawingState.zOrderRelativeOf = tmpZOrderRelativeOf; mDrawingState.zOrderRelatives = tmpZOrderRelatives; + mDrawingState.inputInfo = tmpInputInfo; } } // namespace android diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index 35fc4be056..e7572f0054 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -2025,6 +2025,7 @@ bool Layer::isRemovedFromCurrentState() const { InputWindowInfo Layer::fillInputInfo() { InputWindowInfo info = mDrawingState.inputInfo; + info.id = sequence; if (info.displayId == ADISPLAY_ID_NONE) { info.displayId = getLayerStack(); @@ -2081,9 +2082,29 @@ InputWindowInfo Layer::fillInputInfo() { info.touchableRegion = info.touchableRegion.intersect(Rect{cropLayer->mScreenBounds}); } + // If the layer is a clone, we need to crop the input region to cloned root to prevent + // touches from going outside the cloned area. + if (isClone()) { + sp clonedRoot = getClonedRoot(); + if (clonedRoot != nullptr) { + Rect rect(clonedRoot->mScreenBounds); + info.touchableRegion = info.touchableRegion.intersect(rect); + } + } + return info; } +sp Layer::getClonedRoot() { + if (mClonedChild != nullptr) { + return this; + } + if (mDrawingParent == nullptr || mDrawingParent.promote() == nullptr) { + return nullptr; + } + return mDrawingParent.promote()->getClonedRoot(); +} + bool Layer::hasInput() const { return mDrawingState.inputInfo.token != nullptr; } @@ -2119,10 +2140,6 @@ void Layer::setInitialValuesForClone(const sp& clonedFrom) { // copy drawing state from cloned layer mDrawingState = clonedFrom->mDrawingState; mClonedFrom = clonedFrom; - - // TODO: (b/140756730) Ignore input for now since InputDispatcher doesn't support multiple - // InputWindows per client token yet. - mDrawingState.inputInfo.token = nullptr; } void Layer::updateMirrorInfo() { @@ -2157,9 +2174,6 @@ void Layer::updateClonedDrawingState(std::map, sp>& clonedLayer if (isClonedFromAlive()) { sp clonedFrom = getClonedFrom(); mDrawingState = clonedFrom->mDrawingState; - // TODO: (b/140756730) Ignore input for now since InputDispatcher doesn't support multiple - // InputWindows per client token yet. - mDrawingState.inputInfo.token = nullptr; clonedLayersMap.emplace(clonedFrom, this); } @@ -2198,7 +2212,24 @@ void Layer::updateClonedChildren(const sp& mirrorRoot, } } -void Layer::updateClonedRelatives(std::map, sp> clonedLayersMap) { +void Layer::updateClonedInputInfo(const std::map, sp>& clonedLayersMap) { + auto cropLayer = mDrawingState.touchableRegionCrop.promote(); + if (cropLayer != nullptr) { + if (clonedLayersMap.count(cropLayer) == 0) { + // Real layer had a crop layer but it's not in the cloned hierarchy. Just set to + // self as crop layer to avoid going outside bounds. + mDrawingState.touchableRegionCrop = this; + } else { + const sp& clonedCropLayer = clonedLayersMap.at(cropLayer); + mDrawingState.touchableRegionCrop = clonedCropLayer; + } + } + // Cloned layers shouldn't handle watch outside since their z order is not determined by + // WM or the client. + mDrawingState.inputInfo.layoutParamsFlags &= ~InputWindowInfo::FLAG_WATCH_OUTSIDE_TOUCH; +} + +void Layer::updateClonedRelatives(const std::map, sp>& clonedLayersMap) { mDrawingState.zOrderRelativeOf = nullptr; mDrawingState.zOrderRelatives.clear(); @@ -2206,11 +2237,11 @@ void Layer::updateClonedRelatives(std::map, sp> clonedLayersMap return; } - sp clonedFrom = getClonedFrom(); + const sp& clonedFrom = getClonedFrom(); for (wp& relativeWeak : clonedFrom->mDrawingState.zOrderRelatives) { - sp relative = relativeWeak.promote(); - auto clonedRelative = clonedLayersMap[relative]; - if (clonedRelative != nullptr) { + const sp& relative = relativeWeak.promote(); + if (clonedLayersMap.count(relative) > 0) { + auto& clonedRelative = clonedLayersMap.at(relative); mDrawingState.zOrderRelatives.add(clonedRelative); } } @@ -2220,12 +2251,14 @@ void Layer::updateClonedRelatives(std::map, sp> clonedLayersMap // In that case, we treat the layer as if the relativeOf has been removed. This way, it will // still traverse the children, but the layer with the missing relativeOf will not be shown // on screen. - sp relativeOf = clonedFrom->mDrawingState.zOrderRelativeOf.promote(); - sp clonedRelativeOf = clonedLayersMap[relativeOf]; - if (clonedRelativeOf != nullptr) { + const sp& relativeOf = clonedFrom->mDrawingState.zOrderRelativeOf.promote(); + if (clonedLayersMap.count(relativeOf) > 0) { + const sp& clonedRelativeOf = clonedLayersMap.at(relativeOf); mDrawingState.zOrderRelativeOf = clonedRelativeOf; } + updateClonedInputInfo(clonedLayersMap); + for (sp& child : mDrawingChildren) { child->updateClonedRelatives(clonedLayersMap); } diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index 843d3ae88e..d697a6a182 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -497,8 +497,9 @@ protected: void updateClonedDrawingState(std::map, sp>& clonedLayersMap); void updateClonedChildren(const sp& mirrorRoot, std::map, sp>& clonedLayersMap); - void updateClonedRelatives(std::map, sp> clonedLayersMap); + void updateClonedRelatives(const std::map, sp>& clonedLayersMap); void addChildToDrawing(const sp& layer); + void updateClonedInputInfo(const std::map, sp>& clonedLayersMap); public: /* @@ -972,6 +973,10 @@ private: // Returns true if the layer can draw shadows on its border. virtual bool canDrawShadows() const { return true; } + + // Find the root of the cloned hierarchy, this means the first non cloned parent. + // This will return null if first non cloned parent is not found. + sp getClonedRoot(); }; } // namespace android -- cgit v1.2.3-59-g8ed1b From cb9232108322f4e74583feaf27c44c5331e4fa35 Mon Sep 17 00:00:00 2001 From: chaviw Date: Mon, 30 Dec 2019 14:05:11 -0800 Subject: Remove layer from window handle since it's no longer used Fixes: 146671630 Test: Builds and runs Change-Id: I095228a361ecdda8e1abd48c6d32b085b0dd20cf --- include/input/InputWindow.h | 1 - libs/input/InputWindow.cpp | 2 -- libs/input/tests/InputWindow_test.cpp | 2 -- .../benchmarks/InputDispatcher_benchmarks.cpp | 1 - services/inputflinger/dispatcher/InputDispatcher.cpp | 17 +++++++---------- services/inputflinger/tests/InputDispatcher_test.cpp | 1 - 6 files changed, 7 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/include/input/InputWindow.h b/include/input/InputWindow.h index cbd64d520e..c44db514d2 100644 --- a/include/input/InputWindow.h +++ b/include/input/InputWindow.h @@ -161,7 +161,6 @@ struct InputWindowInfo { bool hasFocus; bool hasWallpaper; bool paused; - int32_t layer; int32_t ownerPid; int32_t ownerUid; int32_t inputFeatures; diff --git a/libs/input/InputWindow.cpp b/libs/input/InputWindow.cpp index 74a05055eb..03ca459fb9 100644 --- a/libs/input/InputWindow.cpp +++ b/libs/input/InputWindow.cpp @@ -91,7 +91,6 @@ status_t InputWindowInfo::write(Parcel& output) const { output.writeBool(hasFocus); output.writeBool(hasWallpaper); output.writeBool(paused); - output.writeInt32(layer); output.writeInt32(ownerPid); output.writeInt32(ownerUid); output.writeInt32(inputFeatures); @@ -135,7 +134,6 @@ InputWindowInfo InputWindowInfo::read(const Parcel& from) { ret.hasFocus = from.readBool(); ret.hasWallpaper = from.readBool(); ret.paused = from.readBool(); - ret.layer = from.readInt32(); ret.ownerPid = from.readInt32(); ret.ownerUid = from.readInt32(); ret.inputFeatures = from.readInt32(); diff --git a/libs/input/tests/InputWindow_test.cpp b/libs/input/tests/InputWindow_test.cpp index cdc81d2f04..d1cb527a57 100644 --- a/libs/input/tests/InputWindow_test.cpp +++ b/libs/input/tests/InputWindow_test.cpp @@ -58,7 +58,6 @@ TEST(InputWindowInfo, Parcelling) { i.hasFocus = false; i.hasWallpaper = false; i.paused = false; - i.layer = 7; i.ownerPid = 19; i.ownerUid = 24; i.inputFeatures = 29; @@ -91,7 +90,6 @@ TEST(InputWindowInfo, Parcelling) { ASSERT_EQ(i.hasFocus, i2.hasFocus); ASSERT_EQ(i.hasWallpaper, i2.hasWallpaper); ASSERT_EQ(i.paused, i2.paused); - ASSERT_EQ(i.layer, i2.layer); ASSERT_EQ(i.ownerPid, i2.ownerPid); ASSERT_EQ(i.ownerUid, i2.ownerUid); ASSERT_EQ(i.inputFeatures, i2.inputFeatures); diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp index 9686ceaf2f..0d3c821215 100644 --- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp +++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp @@ -170,7 +170,6 @@ public: mInfo.hasFocus = true; mInfo.hasWallpaper = false; mInfo.paused = false; - mInfo.layer = 0; mInfo.ownerPid = INJECTOR_PID; mInfo.ownerUid = INJECTOR_UID; mInfo.inputFeatures = 0; diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 116625c03e..4766bcefed 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -3793,12 +3793,10 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { dump += StringPrintf(INDENT3 "%zu: name='%s', displayId=%d, " "portalToDisplayId=%d, paused=%s, hasFocus=%s, " - "hasWallpaper=%s, " - "visible=%s, canReceiveKeys=%s, flags=0x%08x, " - "type=0x%08x, layer=%d, " + "hasWallpaper=%s, visible=%s, canReceiveKeys=%s, " + "flags=0x%08x, type=0x%08x, " "frame=[%d,%d][%d,%d], globalScale=%f, " - "windowScale=(%f,%f), " - "touchableRegion=", + "windowScale=(%f,%f), touchableRegion=", i, windowInfo->name.c_str(), windowInfo->displayId, windowInfo->portalToDisplayId, toString(windowInfo->paused), @@ -3807,11 +3805,10 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { toString(windowInfo->visible), toString(windowInfo->canReceiveKeys), windowInfo->layoutParamsFlags, - windowInfo->layoutParamsType, windowInfo->layer, - windowInfo->frameLeft, windowInfo->frameTop, - windowInfo->frameRight, windowInfo->frameBottom, - windowInfo->globalScaleFactor, windowInfo->windowXScale, - windowInfo->windowYScale); + windowInfo->layoutParamsType, windowInfo->frameLeft, + windowInfo->frameTop, windowInfo->frameRight, + windowInfo->frameBottom, windowInfo->globalScaleFactor, + windowInfo->windowXScale, windowInfo->windowYScale); dumpRegion(dump, windowInfo->touchableRegion); dump += StringPrintf(", inputFeatures=0x%08x", windowInfo->inputFeatures); dump += StringPrintf(", ownerPid=%d, ownerUid=%d, dispatchingTimeout=%0.3fms\n", diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index c25122c72b..5ffc89d210 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -543,7 +543,6 @@ public: mInfo.hasFocus = false; mInfo.hasWallpaper = false; mInfo.paused = false; - mInfo.layer = 0; mInfo.ownerPid = INJECTOR_PID; mInfo.ownerUid = INJECTOR_UID; mInfo.inputFeatures = 0; -- cgit v1.2.3-59-g8ed1b From 718f9601c611f657fd872f84f27d5cc9aec533b4 Mon Sep 17 00:00:00 2001 From: Dominik Laskowski Date: Sat, 9 Nov 2019 20:01:35 -0800 Subject: ui: Deduplicate rotation types Introduce ui::Rotation, replacing ISurfaceComposer::Rotation as well as DISPLAY_ORIENTATION_* and DisplayState::eOrientation* constants. libinput has widespread use of int32_t for orientation, so move DISPLAY_ORIENTATION_* to input/DisplayViewport.h for now. Bug: 144601064 Test: go/wm-smoke Test: screencap Change-Id: Ic4b8494e37c9d79c00d5b4be5eb88585f09efebf --- cmds/surfacereplayer/replayer/Replayer.cpp | 2 +- include/input/DisplayViewport.h | 13 +- include/input/TouchVideoFrame.h | 1 - libs/gui/ISurfaceComposer.cpp | 9 +- libs/gui/LayerState.cpp | 5 +- libs/gui/SurfaceComposerClient.cpp | 39 +++-- libs/gui/include/gui/ISurfaceComposer.h | 22 +-- libs/gui/include/gui/LayerState.h | 12 +- libs/gui/include/gui/SurfaceComposerClient.h | 33 ++-- libs/gui/tests/BLASTBufferQueue_test.cpp | 10 +- libs/gui/tests/Surface_test.cpp | 9 +- libs/input/TouchVideoFrame.cpp | 1 + libs/input/tests/TouchVideoFrame_test.cpp | 1 + libs/ui/include/ui/DisplayInfo.h | 11 +- libs/ui/include/ui/Rotation.h | 57 +++++++ libs/ui/include/ui/Transform.h | 39 +++-- libs/ui/include_vndk/ui/Rotation.h | 1 + opengl/tests/lib/WindowSurface.cpp | 3 +- services/surfaceflinger/BufferLayer.cpp | 6 +- services/surfaceflinger/BufferStateLayer.cpp | 2 +- services/surfaceflinger/DisplayDevice.cpp | 75 +++------ services/surfaceflinger/DisplayDevice.h | 169 +++++++++------------ services/surfaceflinger/LayerRejecter.cpp | 2 +- services/surfaceflinger/RegionSamplingThread.cpp | 14 +- services/surfaceflinger/RenderArea.h | 16 +- services/surfaceflinger/SurfaceFlinger.cpp | 101 ++++++------ services/surfaceflinger/SurfaceFlinger.h | 10 +- services/surfaceflinger/SurfaceInterceptor.cpp | 8 +- services/surfaceflinger/tests/Credentials_test.cpp | 3 +- .../tests/LayerRenderTypeTransaction_test.cpp | 4 +- .../surfaceflinger/tests/LayerTransaction_test.cpp | 2 +- .../tests/unittests/DisplayTransactionTest.cpp | 12 +- .../tests/unittests/TestableSurfaceFlinger.h | 1 - 33 files changed, 329 insertions(+), 364 deletions(-) create mode 100644 libs/ui/include/ui/Rotation.h create mode 120000 libs/ui/include_vndk/ui/Rotation.h (limited to 'include') diff --git a/cmds/surfacereplayer/replayer/Replayer.cpp b/cmds/surfacereplayer/replayer/Replayer.cpp index 0d6c31ee2f..675aad6f81 100644 --- a/cmds/surfacereplayer/replayer/Replayer.cpp +++ b/cmds/surfacereplayer/replayer/Replayer.cpp @@ -606,7 +606,7 @@ void Replayer::setDisplayProjection(SurfaceComposerClient::Transaction& t, pc.viewport().bottom()); Rect frame = Rect(pc.frame().left(), pc.frame().top(), pc.frame().right(), pc.frame().bottom()); - t.setDisplayProjection(mDisplays[id], pc.orientation(), viewport, frame); + t.setDisplayProjection(mDisplays[id], ui::toRotation(pc.orientation()), viewport, frame); } status_t Replayer::createSurfaceControl( diff --git a/include/input/DisplayViewport.h b/include/input/DisplayViewport.h index fa456bb213..610062697c 100644 --- a/include/input/DisplayViewport.h +++ b/include/input/DisplayViewport.h @@ -17,16 +17,23 @@ #ifndef _LIBINPUT_DISPLAY_VIEWPORT_H #define _LIBINPUT_DISPLAY_VIEWPORT_H +#include +#include + #include -#include #include -#include -#include using android::base::StringPrintf; namespace android { +enum { + DISPLAY_ORIENTATION_0 = 0, + DISPLAY_ORIENTATION_90 = 1, + DISPLAY_ORIENTATION_180 = 2, + DISPLAY_ORIENTATION_270 = 3 +}; + /** * Describes the different type of viewports supported by input flinger. * Keep in sync with values in InputManagerService.java. diff --git a/include/input/TouchVideoFrame.h b/include/input/TouchVideoFrame.h index b49c623204..4fa2f86dc1 100644 --- a/include/input/TouchVideoFrame.h +++ b/include/input/TouchVideoFrame.h @@ -19,7 +19,6 @@ #include #include -#include #include namespace android { diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp index ab4d51e3cb..d34fe3b0ba 100644 --- a/libs/gui/ISurfaceComposer.cpp +++ b/libs/gui/ISurfaceComposer.cpp @@ -110,10 +110,10 @@ public: } virtual status_t captureScreen(const sp& display, sp* outBuffer, - bool& outCapturedSecureLayers, const ui::Dataspace reqDataspace, - const ui::PixelFormat reqPixelFormat, Rect sourceCrop, + bool& outCapturedSecureLayers, ui::Dataspace reqDataspace, + ui::PixelFormat reqPixelFormat, const Rect& sourceCrop, uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform, - ISurfaceComposer::Rotation rotation, bool captureSecureLayers) { + ui::Rotation rotation, bool captureSecureLayers) { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); data.writeStrongBinder(display); @@ -1214,8 +1214,7 @@ status_t BnSurfaceComposer::onTransact( bool capturedSecureLayers = false; status_t res = captureScreen(display, &outBuffer, capturedSecureLayers, reqDataspace, reqPixelFormat, sourceCrop, reqWidth, reqHeight, - useIdentityTransform, - static_cast(rotation), + useIdentityTransform, ui::toRotation(rotation), captureSecureLayers); reply->writeInt32(res); diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp index e033f93286..a7c4f4670a 100644 --- a/libs/gui/LayerState.cpp +++ b/libs/gui/LayerState.cpp @@ -203,7 +203,6 @@ status_t ComposerState::read(const Parcel& input) { DisplayState::DisplayState() : what(0), layerStack(0), - orientation(eOrientationDefault), viewport(Rect::EMPTY_RECT), frame(Rect::EMPTY_RECT), width(0), @@ -215,7 +214,7 @@ status_t DisplayState::write(Parcel& output) const { output.writeStrongBinder(IInterface::asBinder(surface)); output.writeUint32(what); output.writeUint32(layerStack); - output.writeUint32(orientation); + output.writeUint32(toRotationInt(orientation)); output.write(viewport); output.write(frame); output.writeUint32(width); @@ -228,7 +227,7 @@ status_t DisplayState::read(const Parcel& input) { surface = interface_cast(input.readStrongBinder()); what = input.readUint32(); layerStack = input.readUint32(); - orientation = input.readUint32(); + orientation = ui::toRotation(input.readUint32()); input.read(viewport); input.read(frame); width = input.readUint32(); diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index f378fc5ef6..1dac70a950 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -1399,9 +1399,9 @@ void SurfaceComposerClient::Transaction::setDisplayLayerStack(const sp& } void SurfaceComposerClient::Transaction::setDisplayProjection(const sp& token, - uint32_t orientation, - const Rect& layerStackRect, - const Rect& displayRect) { + ui::Rotation orientation, + const Rect& layerStackRect, + const Rect& displayRect) { DisplayState& s(getDisplayState(token)); s.orientation = orientation; s.viewport = layerStackRect; @@ -1773,28 +1773,26 @@ status_t SurfaceComposerClient::setGlobalShadowSettings(const half4& ambientColo // ---------------------------------------------------------------------------- -status_t ScreenshotClient::capture(const sp& display, const ui::Dataspace reqDataSpace, - const ui::PixelFormat reqPixelFormat, Rect sourceCrop, +status_t ScreenshotClient::capture(const sp& display, ui::Dataspace reqDataSpace, + ui::PixelFormat reqPixelFormat, const Rect& sourceCrop, uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform, - uint32_t rotation, bool captureSecureLayers, + ui::Rotation rotation, bool captureSecureLayers, sp* outBuffer, bool& outCapturedSecureLayers) { sp s(ComposerService::getComposerService()); if (s == nullptr) return NO_INIT; - status_t ret = - s->captureScreen(display, outBuffer, outCapturedSecureLayers, reqDataSpace, - reqPixelFormat, sourceCrop, reqWidth, reqHeight, useIdentityTransform, - static_cast(rotation), - captureSecureLayers); + status_t ret = s->captureScreen(display, outBuffer, outCapturedSecureLayers, reqDataSpace, + reqPixelFormat, sourceCrop, reqWidth, reqHeight, + useIdentityTransform, rotation, captureSecureLayers); if (ret != NO_ERROR) { return ret; } return ret; } -status_t ScreenshotClient::capture(const sp& display, const ui::Dataspace reqDataSpace, - const ui::PixelFormat reqPixelFormat, Rect sourceCrop, +status_t ScreenshotClient::capture(const sp& display, ui::Dataspace reqDataSpace, + ui::PixelFormat reqPixelFormat, const Rect& sourceCrop, uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform, - uint32_t rotation, sp* outBuffer) { + ui::Rotation rotation, sp* outBuffer) { bool ignored; return capture(display, reqDataSpace, reqPixelFormat, sourceCrop, reqWidth, reqHeight, useIdentityTransform, rotation, false, outBuffer, ignored); @@ -1807,9 +1805,8 @@ status_t ScreenshotClient::capture(uint64_t displayOrLayerStack, ui::Dataspace* return s->captureScreen(displayOrLayerStack, outDataspace, outBuffer); } -status_t ScreenshotClient::captureLayers(const sp& layerHandle, - const ui::Dataspace reqDataSpace, - const ui::PixelFormat reqPixelFormat, Rect sourceCrop, +status_t ScreenshotClient::captureLayers(const sp& layerHandle, ui::Dataspace reqDataSpace, + ui::PixelFormat reqPixelFormat, const Rect& sourceCrop, float frameScale, sp* outBuffer) { sp s(ComposerService::getComposerService()); if (s == nullptr) return NO_INIT; @@ -1819,8 +1816,8 @@ status_t ScreenshotClient::captureLayers(const sp& layerHandle, } status_t ScreenshotClient::captureChildLayers( - const sp& layerHandle, const ui::Dataspace reqDataSpace, - const ui::PixelFormat reqPixelFormat, Rect sourceCrop, + const sp& layerHandle, ui::Dataspace reqDataSpace, ui::PixelFormat reqPixelFormat, + const Rect& sourceCrop, const std::unordered_set, ISurfaceComposer::SpHash>& excludeHandles, float frameScale, sp* outBuffer) { sp s(ComposerService::getComposerService()); @@ -1830,5 +1827,5 @@ status_t ScreenshotClient::captureChildLayers( excludeHandles, frameScale, true /* childrenOnly */); return ret; } -// ---------------------------------------------------------------------------- -}; // namespace android + +} // namespace android diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h index 998973c4dc..9804c92d0c 100644 --- a/libs/gui/include/gui/ISurfaceComposer.h +++ b/libs/gui/include/gui/ISurfaceComposer.h @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -81,13 +82,6 @@ public: eEarlyWakeup = 0x04 }; - enum Rotation { - eRotateNone = 0, - eRotate90 = 1, - eRotate180 = 2, - eRotate270 = 3 - }; - enum VsyncSource { eVsyncSourceApp = 0, eVsyncSourceSurfaceFlinger = 1 @@ -249,10 +243,10 @@ public: * it) around its center. */ virtual status_t captureScreen(const sp& display, sp* outBuffer, - bool& outCapturedSecureLayers, const ui::Dataspace reqDataspace, - const ui::PixelFormat reqPixelFormat, Rect sourceCrop, + bool& outCapturedSecureLayers, ui::Dataspace reqDataspace, + ui::PixelFormat reqPixelFormat, const Rect& sourceCrop, uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform, - Rotation rotation = eRotateNone, + ui::Rotation rotation = ui::ROTATION_0, bool captureSecureLayers = false) = 0; /** * Capture the specified screen. This requires READ_FRAME_BUFFER @@ -276,8 +270,9 @@ public: * it) around its center. */ virtual status_t captureScreen(const sp& display, sp* outBuffer, - Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight, - bool useIdentityTransform, Rotation rotation = eRotateNone) { + const Rect& sourceCrop, uint32_t reqWidth, uint32_t reqHeight, + bool useIdentityTransform, + ui::Rotation rotation = ui::ROTATION_0) { bool outIgnored; return captureScreen(display, outBuffer, outIgnored, ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888, sourceCrop, reqWidth, reqHeight, @@ -301,8 +296,7 @@ public: */ virtual status_t captureLayers( const sp& layerHandleBinder, sp* outBuffer, - const ui::Dataspace reqDataspace, const ui::PixelFormat reqPixelFormat, - const Rect& sourceCrop, + ui::Dataspace reqDataspace, ui::PixelFormat reqPixelFormat, const Rect& sourceCrop, const std::unordered_set, SpHash>& excludeHandles, float frameScale = 1.0, bool childrenOnly = false) = 0; diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h index c2b5119242..fb186396a6 100644 --- a/libs/gui/include/gui/LayerState.h +++ b/libs/gui/include/gui/LayerState.h @@ -35,6 +35,7 @@ #include #include #include +#include namespace android { @@ -217,15 +218,6 @@ struct ComposerState { }; struct DisplayState { - enum { - eOrientationDefault = 0, - eOrientation90 = 1, - eOrientation180 = 2, - eOrientation270 = 3, - eOrientationUnchanged = 4, - eOrientationSwapMask = 0x01 - }; - enum { eSurfaceChanged = 0x01, eLayerStackChanged = 0x02, @@ -252,7 +244,7 @@ struct DisplayState { // 0, layers will be scaled by a factor of 2 and translated by (20, 10). // When orientation is 1, layers will be additionally rotated by 90 // degrees around the origin clockwise and translated by (W, 0). - uint32_t orientation; + ui::Rotation orientation = ui::ROTATION_0; Rect viewport; Rect frame; diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h index 86468a49f3..44f29eaa6f 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -531,10 +532,8 @@ public: * mapped to. displayRect is specified post-orientation, that is * it uses the orientation seen by the end-user. */ - void setDisplayProjection(const sp& token, - uint32_t orientation, - const Rect& layerStackRect, - const Rect& displayRect); + void setDisplayProjection(const sp& token, ui::Rotation orientation, + const Rect& layerStackRect, const Rect& displayRect); void setDisplaySize(const sp& token, uint32_t width, uint32_t height); void setAnimationTransaction(); void setEarlyWakeup(); @@ -548,10 +547,8 @@ public: static status_t getHdrCapabilities(const sp& display, HdrCapabilities* outCapabilities); - static void setDisplayProjection(const sp& token, - uint32_t orientation, - const Rect& layerStackRect, - const Rect& displayRect); + static void setDisplayProjection(const sp& token, ui::Rotation orientation, + const Rect& layerStackRect, const Rect& displayRect); inline sp getClient() { return mClient; } @@ -583,23 +580,23 @@ class ScreenshotClient { public: // if cropping isn't required, callers may pass in a default Rect, e.g.: // capture(display, producer, Rect(), reqWidth, ...); - static status_t capture(const sp& display, const ui::Dataspace reqDataSpace, - const ui::PixelFormat reqPixelFormat, Rect sourceCrop, + static status_t capture(const sp& display, ui::Dataspace reqDataSpace, + ui::PixelFormat reqPixelFormat, const Rect& sourceCrop, uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform, - uint32_t rotation, bool captureSecureLayers, + ui::Rotation rotation, bool captureSecureLayers, sp* outBuffer, bool& outCapturedSecureLayers); - static status_t capture(const sp& display, const ui::Dataspace reqDataSpace, - const ui::PixelFormat reqPixelFormat, Rect sourceCrop, + static status_t capture(const sp& display, ui::Dataspace reqDataSpace, + ui::PixelFormat reqPixelFormat, const Rect& sourceCrop, uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform, - uint32_t rotation, sp* outBuffer); + ui::Rotation rotation, sp* outBuffer); static status_t capture(uint64_t displayOrLayerStack, ui::Dataspace* outDataspace, sp* outBuffer); - static status_t captureLayers(const sp& layerHandle, const ui::Dataspace reqDataSpace, - const ui::PixelFormat reqPixelFormat, Rect sourceCrop, + static status_t captureLayers(const sp& layerHandle, ui::Dataspace reqDataSpace, + ui::PixelFormat reqPixelFormat, const Rect& sourceCrop, float frameScale, sp* outBuffer); static status_t captureChildLayers( - const sp& layerHandle, const ui::Dataspace reqDataSpace, - const ui::PixelFormat reqPixelFormat, Rect sourceCrop, + const sp& layerHandle, ui::Dataspace reqDataSpace, + ui::PixelFormat reqPixelFormat, const Rect& sourceCrop, const std::unordered_set, ISurfaceComposer::SpHash>& excludeHandles, float frameScale, sp* outBuffer); diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp index 0f618f181c..85cf62f44f 100644 --- a/libs/gui/tests/BLASTBufferQueue_test.cpp +++ b/libs/gui/tests/BLASTBufferQueue_test.cpp @@ -127,7 +127,7 @@ protected: ASSERT_EQ(NO_ERROR, igbProducer->connect(new DummyProducerListener, NATIVE_WINDOW_API_CPU, false, &qbOutput)); - ASSERT_NE(ui::Transform::orientation_flags::ROT_INVALID, qbOutput.transformHint); + ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint); producer = igbProducer; } @@ -266,7 +266,7 @@ TEST_F(BLASTBufferQueueTest, onFrameAvailable_Apply) { NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE); igbProducer->queueBuffer(slot, input, &qbOutput); - ASSERT_NE(ui::Transform::orientation_flags::ROT_INVALID, qbOutput.transformHint); + ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint); adapter.waitForCallbacks(); @@ -349,7 +349,7 @@ TEST_F(BLASTBufferQueueTest, SetCrop_Item) { NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE); igbProducer->queueBuffer(slot, input, &qbOutput); - ASSERT_NE(ui::Transform::orientation_flags::ROT_INVALID, qbOutput.transformHint); + ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint); adapter.waitForCallbacks(); // capture screen and verify that it is red @@ -410,7 +410,7 @@ TEST_F(BLASTBufferQueueTest, SetCrop_ScalingModeScaleCrop) { NATIVE_WINDOW_SCALING_MODE_SCALE_CROP, 0, Fence::NO_FENCE); igbProducer->queueBuffer(slot, input, &qbOutput); - ASSERT_NE(ui::Transform::orientation_flags::ROT_INVALID, qbOutput.transformHint); + ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint); adapter.waitForCallbacks(); // capture screen and verify that it is red @@ -456,7 +456,7 @@ public: NATIVE_WINDOW_SCALING_MODE_FREEZE, tr, Fence::NO_FENCE); igbProducer->queueBuffer(slot, input, &qbOutput); - ASSERT_NE(ui::Transform::orientation_flags::ROT_INVALID, qbOutput.transformHint); + ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint); adapter.waitForCallbacks(); bool capturedSecureLayers; diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index 0445755ade..f9540b2a41 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -742,11 +742,10 @@ public: status_t setActiveColorMode(const sp& /*display*/, ColorMode /*colorMode*/) override { return NO_ERROR; } status_t captureScreen(const sp& /*display*/, sp* /*outBuffer*/, - bool& /* outCapturedSecureLayers */, - const ui::Dataspace /*reqDataspace*/, - const ui::PixelFormat /*reqPixelFormat*/, Rect /*sourceCrop*/, + bool& /*outCapturedSecureLayers*/, ui::Dataspace /*reqDataspace*/, + ui::PixelFormat /*reqPixelFormat*/, const Rect& /*sourceCrop*/, uint32_t /*reqWidth*/, uint32_t /*reqHeight*/, - bool /*useIdentityTransform*/, Rotation /*rotation*/, + bool /*useIdentityTransform*/, ui::Rotation, bool /*captureSecureLayers*/) override { return NO_ERROR; } @@ -766,7 +765,7 @@ public: } virtual status_t captureLayers( const sp& /*parentHandle*/, sp* /*outBuffer*/, - const ui::Dataspace /*reqDataspace*/, const ui::PixelFormat /*reqPixelFormat*/, + ui::Dataspace /*reqDataspace*/, ui::PixelFormat /*reqPixelFormat*/, const Rect& /*sourceCrop*/, const std::unordered_set, ISurfaceComposer::SpHash>& /*excludeHandles*/, diff --git a/libs/input/TouchVideoFrame.cpp b/libs/input/TouchVideoFrame.cpp index 8a4298a36f..145b4ae03b 100644 --- a/libs/input/TouchVideoFrame.cpp +++ b/libs/input/TouchVideoFrame.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#include #include namespace android { diff --git a/libs/input/tests/TouchVideoFrame_test.cpp b/libs/input/tests/TouchVideoFrame_test.cpp index 815424ee31..1ec935859d 100644 --- a/libs/input/tests/TouchVideoFrame_test.cpp +++ b/libs/input/tests/TouchVideoFrame_test.cpp @@ -16,6 +16,7 @@ #include +#include #include namespace android { diff --git a/libs/ui/include/ui/DisplayInfo.h b/libs/ui/include/ui/DisplayInfo.h index 07722104de..38f8d6be02 100644 --- a/libs/ui/include/ui/DisplayInfo.h +++ b/libs/ui/include/ui/DisplayInfo.h @@ -20,6 +20,7 @@ #include #include +#include #include namespace android { @@ -33,7 +34,7 @@ struct DisplayInfo { float ydpi{0}; float fps{0}; float density{0}; - uint8_t orientation{0}; + ui::Rotation orientation{ui::ROTATION_0}; bool secure{false}; nsecs_t appVsyncOffset{0}; nsecs_t presentationDeadline{0}; @@ -42,14 +43,6 @@ struct DisplayInfo { uint32_t layerStack{NO_LAYER_STACK}; }; -/* Display orientations as defined in Surface.java and ISurfaceComposer.h. */ -enum { - DISPLAY_ORIENTATION_0 = 0, - DISPLAY_ORIENTATION_90 = 1, - DISPLAY_ORIENTATION_180 = 2, - DISPLAY_ORIENTATION_270 = 3 -}; - } // namespace android #endif // ANDROID_COMPOSER_DISPLAY_INFO_H diff --git a/libs/ui/include/ui/Rotation.h b/libs/ui/include/ui/Rotation.h new file mode 100644 index 0000000000..89008f6694 --- /dev/null +++ b/libs/ui/include/ui/Rotation.h @@ -0,0 +1,57 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +namespace android::ui { + +enum class Rotation { Rotation0 = 0, Rotation90 = 1, Rotation180 = 2, Rotation270 = 3 }; + +// Equivalent to Surface.java constants. +constexpr auto ROTATION_0 = Rotation::Rotation0; +constexpr auto ROTATION_90 = Rotation::Rotation90; +constexpr auto ROTATION_180 = Rotation::Rotation180; +constexpr auto ROTATION_270 = Rotation::Rotation270; + +constexpr auto toRotation(std::underlying_type_t rotation) { + return static_cast(rotation); +} + +constexpr auto toRotationInt(Rotation rotation) { + return static_cast>(rotation); +} + +constexpr Rotation operator+(Rotation lhs, Rotation rhs) { + constexpr auto N = toRotationInt(ROTATION_270) + 1; + return toRotation((toRotationInt(lhs) + toRotationInt(rhs)) % N); +} + +constexpr const char* toCString(Rotation rotation) { + switch (rotation) { + case ROTATION_0: + return "ROTATION_0"; + case ROTATION_90: + return "ROTATION_90"; + case ROTATION_180: + return "ROTATION_180"; + case ROTATION_270: + return "ROTATION_270"; + } +} + +} // namespace android::ui diff --git a/libs/ui/include/ui/Transform.h b/libs/ui/include/ui/Transform.h index de07684d79..c6bb598d7f 100644 --- a/libs/ui/include/ui/Transform.h +++ b/libs/ui/include/ui/Transform.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef ANDROID_TRANSFORM_H -#define ANDROID_TRANSFORM_H +#pragma once #include #include @@ -28,6 +27,7 @@ #include #include #include +#include namespace android { @@ -42,13 +42,13 @@ public: explicit Transform(uint32_t orientation, int w = 0, int h = 0); ~Transform(); - enum orientation_flags { - ROT_0 = 0x00000000, - FLIP_H = HAL_TRANSFORM_FLIP_H, - FLIP_V = HAL_TRANSFORM_FLIP_V, - ROT_90 = HAL_TRANSFORM_ROT_90, - ROT_180 = FLIP_H|FLIP_V, - ROT_270 = ROT_180|ROT_90, + enum RotationFlags : uint32_t { + ROT_0 = 0, + FLIP_H = HAL_TRANSFORM_FLIP_H, + FLIP_V = HAL_TRANSFORM_FLIP_V, + ROT_90 = HAL_TRANSFORM_ROT_90, + ROT_180 = FLIP_H | FLIP_V, + ROT_270 = ROT_180 | ROT_90, ROT_INVALID = 0x80 }; @@ -100,6 +100,8 @@ public: void dump(std::string& result, const char* name) const; void dump(const char* name) const; + static RotationFlags toRotationFlags(Rotation); + private: struct mat33 { vec3 v[3]; @@ -117,13 +119,26 @@ private: mutable uint32_t mType; }; -static inline void PrintTo(const Transform& t, ::std::ostream* os) { +inline void PrintTo(const Transform& t, ::std::ostream* os) { std::string out; t.dump(out, "ui::Transform"); *os << out; } +inline Transform::RotationFlags Transform::toRotationFlags(Rotation rotation) { + switch (rotation) { + case ROTATION_0: + return ROT_0; + case ROTATION_90: + return ROT_90; + case ROTATION_180: + return ROT_180; + case ROTATION_270: + return ROT_270; + default: + return ROT_INVALID; + } +} + } // namespace ui } // namespace android - -#endif /* ANDROID_TRANSFORM_H */ diff --git a/libs/ui/include_vndk/ui/Rotation.h b/libs/ui/include_vndk/ui/Rotation.h new file mode 120000 index 0000000000..d84fb4b46b --- /dev/null +++ b/libs/ui/include_vndk/ui/Rotation.h @@ -0,0 +1 @@ +../../include/ui/Rotation.h \ No newline at end of file diff --git a/opengl/tests/lib/WindowSurface.cpp b/opengl/tests/lib/WindowSurface.cpp index a0bd4e2409..4dcc1ca4db 100644 --- a/opengl/tests/lib/WindowSurface.cpp +++ b/opengl/tests/lib/WindowSurface.cpp @@ -48,8 +48,7 @@ WindowSurface::WindowSurface() { } uint32_t width, height; - if (mainDpyInfo.orientation != DISPLAY_ORIENTATION_0 && - mainDpyInfo.orientation != DISPLAY_ORIENTATION_180) { + if (mainDpyInfo.orientation != ui::ROTATION_0 && mainDpyInfo.orientation != ui::ROTATION_180) { // rotated width = mainDpyInfo.h; height = mainDpyInfo.w; diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp index bdecdb78d0..3b1b796076 100644 --- a/services/surfaceflinger/BufferLayer.cpp +++ b/services/surfaceflinger/BufferLayer.cpp @@ -207,7 +207,7 @@ std::optional BufferLayer::prepareClientComposition * the code below applies the primary display's inverse transform to * the texture transform */ - uint32_t transform = DisplayDevice::getPrimaryDisplayOrientationTransform(); + uint32_t transform = DisplayDevice::getPrimaryDisplayRotationFlags(); mat4 tr = inverseOrientation(transform); /** @@ -622,7 +622,7 @@ Rect BufferLayer::getBufferSize(const State& s) const { } if (getTransformToDisplayInverse()) { - uint32_t invTransform = DisplayDevice::getPrimaryDisplayOrientationTransform(); + uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags(); if (invTransform & ui::Transform::ROT_90) { std::swap(bufWidth, bufHeight); } @@ -658,7 +658,7 @@ FloatRect BufferLayer::computeSourceBounds(const FloatRect& parentBounds) const } if (getTransformToDisplayInverse()) { - uint32_t invTransform = DisplayDevice::getPrimaryDisplayOrientationTransform(); + uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags(); if (invTransform & ui::Transform::ROT_90) { std::swap(bufWidth, bufHeight); } diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp index 1e471e53ff..170956ca80 100644 --- a/services/surfaceflinger/BufferStateLayer.cpp +++ b/services/surfaceflinger/BufferStateLayer.cpp @@ -468,7 +468,7 @@ status_t BufferStateLayer::updateTexImage(bool& /*recomputeVisibleRegions*/, nse } if (s.transformToDisplayInverse) { - uint32_t invTransform = DisplayDevice::getPrimaryDisplayOrientationTransform(); + uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags(); if (invTransform & ui::Transform::ROT_90) { std::swap(bufferWidth, bufferHeight); } diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp index 84ec597d2f..e0dc3e7751 100644 --- a/services/surfaceflinger/DisplayDevice.cpp +++ b/services/surfaceflinger/DisplayDevice.cpp @@ -41,12 +41,7 @@ namespace android { using android::base::StringAppendF; -/* - * Initialize the display to the specified values. - * - */ - -uint32_t DisplayDevice::sPrimaryDisplayOrientation = 0; +ui::Transform::RotationFlags DisplayDevice::sPrimaryDisplayRotationFlags = ui::Transform::ROT_0; DisplayDeviceCreationArgs::DisplayDeviceCreationArgs(const sp& flinger, const wp& displayToken, @@ -57,13 +52,11 @@ DisplayDevice::DisplayDevice(DisplayDeviceCreationArgs&& args) : mFlinger(args.flinger), mDisplayToken(args.displayToken), mSequenceId(args.sequenceId), - mDisplayInstallOrientation(args.displayInstallOrientation), + mIsVirtual(args.isVirtual), mCompositionDisplay{mFlinger->getCompositionEngine().createDisplay( compositionengine::DisplayCreationArgs{args.isVirtual, args.displayId, args.powerAdvisor})}, - mIsVirtual(args.isVirtual), - mOrientation(), - mActiveConfig(0), + mPhysicalOrientation(args.physicalOrientation), mIsPrimary(args.isPrimary) { mCompositionDisplay->editState().isSecure = args.isSecure; mCompositionDisplay->createRenderSurface( @@ -88,7 +81,7 @@ DisplayDevice::DisplayDevice(DisplayDeviceCreationArgs&& args) setPowerMode(args.initialPowerMode); // initialize the display orientation transform. - setProjection(DisplayState::eOrientationDefault, Rect::INVALID_RECT, Rect::INVALID_RECT); + setProjection(ui::ROTATION_0, Rect::INVALID_RECT, Rect::INVALID_RECT); } DisplayDevice::~DisplayDevice() = default; @@ -131,7 +124,6 @@ bool DisplayDevice::isPoweredOn() const { return mPowerMode != HWC_POWER_MODE_OFF; } -// ---------------------------------------------------------------------------- void DisplayDevice::setActiveConfig(HwcConfigIndexType mode) { mActiveConfig = mode; } @@ -140,53 +132,19 @@ HwcConfigIndexType DisplayDevice::getActiveConfig() const { return mActiveConfig; } -// ---------------------------------------------------------------------------- - ui::Dataspace DisplayDevice::getCompositionDataSpace() const { return mCompositionDisplay->getState().dataspace; } -// ---------------------------------------------------------------------------- - void DisplayDevice::setLayerStack(uint32_t stack) { mCompositionDisplay->setLayerStackFilter(stack, isPrimary()); } -// ---------------------------------------------------------------------------- - -uint32_t DisplayDevice::displayStateOrientationToTransformOrientation(int orientation) { - switch (orientation) { - case DisplayState::eOrientationDefault: - return ui::Transform::ROT_0; - case DisplayState::eOrientation90: - return ui::Transform::ROT_90; - case DisplayState::eOrientation180: - return ui::Transform::ROT_180; - case DisplayState::eOrientation270: - return ui::Transform::ROT_270; - default: - return ui::Transform::ROT_INVALID; - } -} - -status_t DisplayDevice::orientationToTransfrom(int orientation, int w, int h, ui::Transform* tr) { - uint32_t flags = displayStateOrientationToTransformOrientation(orientation); - if (flags == ui::Transform::ROT_INVALID) { - return BAD_VALUE; - } - tr->set(flags, w, h); - return NO_ERROR; -} - void DisplayDevice::setDisplaySize(const int newWidth, const int newHeight) { mCompositionDisplay->setBounds(ui::Size(newWidth, newHeight)); } -void DisplayDevice::setProjection(int orientation, - const Rect& newViewport, const Rect& newFrame) { - Rect viewport(newViewport); - Rect frame(newFrame); - +void DisplayDevice::setProjection(ui::Rotation orientation, Rect viewport, Rect frame) { mOrientation = orientation; const Rect& displayBounds = getCompositionDisplay()->getState().bounds; @@ -194,7 +152,10 @@ void DisplayDevice::setProjection(int orientation, const int h = displayBounds.height(); ui::Transform R; - DisplayDevice::orientationToTransfrom(orientation, w, h, &R); + if (const auto flags = ui::Transform::toRotationFlags(orientation); + flags != ui::Transform::ROT_INVALID) { + R.set(flags, w, h); + } if (!frame.isValid()) { // the destination frame can be invalid if it has never been set, @@ -236,9 +197,10 @@ void DisplayDevice::setProjection(int orientation, // need to take care of primary display rotation for globalTransform // for case if the panel is not installed aligned with device orientation if (isPrimary()) { - DisplayDevice::orientationToTransfrom( - (orientation + mDisplayInstallOrientation) % (DisplayState::eOrientation270 + 1), - w, h, &R); + if (const auto flags = ui::Transform::toRotationFlags(orientation + mPhysicalOrientation); + flags != ui::Transform::ROT_INVALID) { + R.set(flags, w, h); + } } // The viewport and frame are both in the logical orientation. @@ -258,19 +220,18 @@ void DisplayDevice::setProjection(int orientation, uint32_t transformOrientation; if (isPrimary()) { - sPrimaryDisplayOrientation = displayStateOrientationToTransformOrientation(orientation); - transformOrientation = displayStateOrientationToTransformOrientation( - (orientation + mDisplayInstallOrientation) % (DisplayState::eOrientation270 + 1)); + sPrimaryDisplayRotationFlags = ui::Transform::toRotationFlags(orientation); + transformOrientation = ui::Transform::toRotationFlags(orientation + mPhysicalOrientation); } else { - transformOrientation = displayStateOrientationToTransformOrientation(orientation); + transformOrientation = ui::Transform::toRotationFlags(orientation); } getCompositionDisplay()->setProjection(globalTransform, transformOrientation, frame, viewport, scissor, needsFiltering); } -uint32_t DisplayDevice::getPrimaryDisplayOrientationTransform() { - return sPrimaryDisplayOrientation; +ui::Transform::RotationFlags DisplayDevice::getPrimaryDisplayRotationFlags() { + return sPrimaryDisplayRotationFlags; } std::string DisplayDevice::getDebugName() const { diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h index 79a118571e..605e7c8ab4 100644 --- a/services/surfaceflinger/DisplayDevice.h +++ b/services/surfaceflinger/DisplayDevice.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef ANDROID_DISPLAY_DEVICE_H -#define ANDROID_DISPLAY_DEVICE_H +#pragma once #include @@ -83,14 +82,17 @@ public: int getWidth() const; int getHeight() const; - int getInstallOrientation() const { return mDisplayInstallOrientation; } void setLayerStack(uint32_t stack); void setDisplaySize(const int newWidth, const int newHeight); - void setProjection(int orientation, const Rect& viewport, const Rect& frame); - int getOrientation() const { return mOrientation; } - static uint32_t getPrimaryDisplayOrientationTransform(); + void setProjection(ui::Rotation orientation, Rect viewport, Rect frame); + + ui::Rotation getPhysicalOrientation() const { return mPhysicalOrientation; } + ui::Rotation getOrientation() const { return mOrientation; } + + static ui::Transform::RotationFlags getPrimaryDisplayRotationFlags(); + const ui::Transform& getTransform() const; const Rect& getViewport() const; const Rect& getFrame() const; @@ -156,37 +158,21 @@ public: void dump(std::string& result) const; private: - /* - * Constants, set during initialization - */ const sp mFlinger; const wp mDisplayToken; const int32_t mSequenceId; + const bool mIsVirtual; - const int mDisplayInstallOrientation; const std::shared_ptr mCompositionDisplay; std::string mDisplayName; - const bool mIsVirtual; - /* - * Can only accessed from the main thread, these members - * don't need synchronization. - */ - - /* - * Transaction state - */ - static uint32_t displayStateOrientationToTransformOrientation(int orientation); - static status_t orientationToTransfrom(int orientation, - int w, int h, ui::Transform* tr); + const ui::Rotation mPhysicalOrientation; + ui::Rotation mOrientation = ui::ROTATION_0; - int mOrientation; - static uint32_t sPrimaryDisplayOrientation; + static ui::Transform::RotationFlags sPrimaryDisplayRotationFlags; - // Current power mode - int mPowerMode; - // Current active config + int mPowerMode = HWC_POWER_MODE_OFF; HwcConfigIndexType mActiveConfig; // TODO(b/74619554): Remove special cases for primary display. @@ -202,7 +188,7 @@ struct DisplayDeviceState { uint32_t layerStack = NO_LAYER_STACK; Rect viewport; Rect frame; - uint8_t orientation = 0; + ui::Rotation orientation = ui::ROTATION_0; uint32_t width = 0; uint32_t height = 0; std::string displayName; @@ -227,7 +213,7 @@ struct DisplayDeviceCreationArgs { bool isSecure{false}; sp nativeWindow; sp displaySurface; - int displayInstallOrientation{DisplayState::eOrientationDefault}; + ui::Rotation physicalOrientation{ui::ROTATION_0}; bool hasWideColorGamut{false}; HdrCapabilities hdrCapabilities; int32_t supportedPerFrameMetadata{0}; @@ -239,31 +225,33 @@ struct DisplayDeviceCreationArgs { class DisplayRenderArea : public RenderArea { public: - DisplayRenderArea(const sp device, - ui::Transform::orientation_flags rotation = ui::Transform::ROT_0) - : DisplayRenderArea(device, device->getBounds(), device->getWidth(), device->getHeight(), - device->getCompositionDataSpace(), rotation) {} - DisplayRenderArea(const sp device, Rect sourceCrop, uint32_t reqWidth, - uint32_t reqHeight, ui::Dataspace reqDataSpace, - ui::Transform::orientation_flags rotation, bool allowSecureLayers = true) + DisplayRenderArea(const sp& display, + RotationFlags rotation = ui::Transform::ROT_0) + : DisplayRenderArea(display, display->getBounds(), display->getWidth(), + display->getHeight(), display->getCompositionDataSpace(), rotation) {} + + DisplayRenderArea(sp display, const Rect& sourceCrop, uint32_t reqWidth, + uint32_t reqHeight, ui::Dataspace reqDataSpace, RotationFlags rotation, + bool allowSecureLayers = true) : RenderArea(reqWidth, reqHeight, CaptureFill::OPAQUE, reqDataSpace, - device->getViewport(), - getDisplayRotation(rotation, device->getInstallOrientation())), - mDevice(device), + display->getViewport(), + applyInversePhysicalOrientation(rotation, + display->getPhysicalOrientation())), + mDisplay(std::move(display)), mSourceCrop(sourceCrop), mAllowSecureLayers(allowSecureLayers) {} - const ui::Transform& getTransform() const override { return mDevice->getTransform(); } - Rect getBounds() const override { return mDevice->getBounds(); } - int getHeight() const override { return mDevice->getHeight(); } - int getWidth() const override { return mDevice->getWidth(); } - bool isSecure() const override { return mAllowSecureLayers && mDevice->isSecure(); } - const sp getDisplayDevice() const override { return mDevice; } + const ui::Transform& getTransform() const override { return mDisplay->getTransform(); } + Rect getBounds() const override { return mDisplay->getBounds(); } + int getHeight() const override { return mDisplay->getHeight(); } + int getWidth() const override { return mDisplay->getWidth(); } + bool isSecure() const override { return mAllowSecureLayers && mDisplay->isSecure(); } + sp getDisplayDevice() const override { return mDisplay; } bool needsFiltering() const override { // check if the projection from the logical display to the physical // display needs filtering - if (mDevice->needsFiltering()) { + if (mDisplay->needsFiltering()) { return true; } @@ -281,7 +269,7 @@ public: Rect getSourceCrop() const override { // use the projected display viewport by default. if (mSourceCrop.isEmpty()) { - return mDevice->getScissor(); + return mDisplay->getScissor(); } // Recompute the device transformation for the source crop. @@ -289,27 +277,13 @@ public: ui::Transform translatePhysical; ui::Transform translateLogical; ui::Transform scale; - const Rect& viewport = mDevice->getViewport(); - const Rect& scissor = mDevice->getScissor(); - const Rect& frame = mDevice->getFrame(); - - const int orientation = mDevice->getInstallOrientation(); - // Install orientation is transparent to the callers. Apply it now. - uint32_t flags = 0x00; - switch (orientation) { - case DisplayState::eOrientation90: - flags = ui::Transform::ROT_90; - break; - case DisplayState::eOrientation180: - flags = ui::Transform::ROT_180; - break; - case DisplayState::eOrientation270: - flags = ui::Transform::ROT_270; - break; - default: - break; - } + const Rect& viewport = mDisplay->getViewport(); + const Rect& scissor = mDisplay->getScissor(); + const Rect& frame = mDisplay->getFrame(); + + const auto flags = ui::Transform::toRotationFlags(mDisplay->getPhysicalOrientation()); rotation.set(flags, getWidth(), getHeight()); + translateLogical.set(-viewport.left, -viewport.top); translatePhysical.set(scissor.left, scissor.top); scale.set(frame.getWidth() / float(viewport.getWidth()), 0, 0, @@ -320,49 +294,44 @@ public: } private: - // Install orientation is transparent to the callers. We need to cancel - // it out by modifying rotation flags. - static ui::Transform::orientation_flags getDisplayRotation( - ui::Transform::orientation_flags rotation, int orientation) { - if (orientation == DisplayState::eOrientationDefault) { - return rotation; - } - - // convert hw orientation into flag presentation - // here inverse transform needed - uint8_t hw_rot_90 = 0x00; - uint8_t hw_flip_hv = 0x00; - switch (orientation) { - case DisplayState::eOrientation90: - hw_rot_90 = ui::Transform::ROT_90; - hw_flip_hv = ui::Transform::ROT_180; + static RotationFlags applyInversePhysicalOrientation(RotationFlags orientation, + ui::Rotation physicalOrientation) { + uint32_t inverseRotate90 = 0; + uint32_t inverseReflect = 0; + + switch (physicalOrientation) { + case ui::ROTATION_0: + return orientation; + + case ui::ROTATION_90: + inverseRotate90 = ui::Transform::ROT_90; + inverseReflect = ui::Transform::ROT_180; break; - case DisplayState::eOrientation180: - hw_flip_hv = ui::Transform::ROT_180; + + case ui::ROTATION_180: + inverseReflect = ui::Transform::ROT_180; break; - case DisplayState::eOrientation270: - hw_rot_90 = ui::Transform::ROT_90; + + case ui::ROTATION_270: + inverseRotate90 = ui::Transform::ROT_90; break; } - // transform flags operation - // 1) flip H V if both have ROT_90 flag - // 2) XOR these flags - uint8_t rotation_rot_90 = rotation & ui::Transform::ROT_90; - uint8_t rotation_flip_hv = rotation & ui::Transform::ROT_180; - if (rotation_rot_90 & hw_rot_90) { - rotation_flip_hv = (~rotation_flip_hv) & ui::Transform::ROT_180; + const uint32_t rotate90 = orientation & ui::Transform::ROT_90; + uint32_t reflect = orientation & ui::Transform::ROT_180; + + // Apply reflection for double rotation. + if (rotate90 & inverseRotate90) { + reflect = ~reflect & ui::Transform::ROT_180; } - return static_cast( - (rotation_rot_90 ^ hw_rot_90) | (rotation_flip_hv ^ hw_flip_hv)); + return static_cast((rotate90 ^ inverseRotate90) | + (reflect ^ inverseReflect)); } - const sp mDevice; + const sp mDisplay; const Rect mSourceCrop; const bool mAllowSecureLayers; }; -}; // namespace android - -#endif // ANDROID_DISPLAY_DEVICE_H +} // namespace android diff --git a/services/surfaceflinger/LayerRejecter.cpp b/services/surfaceflinger/LayerRejecter.cpp index 1a60f1ed08..412f9779c8 100644 --- a/services/surfaceflinger/LayerRejecter.cpp +++ b/services/surfaceflinger/LayerRejecter.cpp @@ -49,7 +49,7 @@ bool LayerRejecter::reject(const sp& buf, const BufferItem& item) } if (mTransformToDisplayInverse) { - uint32_t invTransform = DisplayDevice::getPrimaryDisplayOrientationTransform(); + uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags(); if (invTransform & ui::Transform::ROT_90) { std::swap(bufWidth, bufHeight); } diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp index a484373caa..73de4f8b78 100644 --- a/services/surfaceflinger/RegionSamplingThread.cpp +++ b/services/surfaceflinger/RegionSamplingThread.cpp @@ -337,19 +337,7 @@ void RegionSamplingThread::captureSample() { } const auto device = mFlinger.getDefaultDisplayDevice(); - const auto orientation = [](uint32_t orientation) { - switch (orientation) { - default: - case DisplayState::eOrientationDefault: - return ui::Transform::ROT_0; - case DisplayState::eOrientation90: - return ui::Transform::ROT_90; - case DisplayState::eOrientation180: - return ui::Transform::ROT_180; - case DisplayState::eOrientation270: - return ui::Transform::ROT_270; - } - }(device->getOrientation()); + const auto orientation = ui::Transform::toRotationFlags(device->getOrientation()); std::vector descriptors; Region sampleRegion; diff --git a/services/surfaceflinger/RenderArea.h b/services/surfaceflinger/RenderArea.h index 532572f766..a7a6dd5fc1 100644 --- a/services/surfaceflinger/RenderArea.h +++ b/services/surfaceflinger/RenderArea.h @@ -17,13 +17,15 @@ class DisplayDevice; // physical render area. class RenderArea { public: + using RotationFlags = ui::Transform::RotationFlags; + enum class CaptureFill {CLEAR, OPAQUE}; static float getCaptureFillValue(CaptureFill captureFill); RenderArea(uint32_t reqWidth, uint32_t reqHeight, CaptureFill captureFill, ui::Dataspace reqDataSpace, const Rect& displayViewport, - ui::Transform::orientation_flags rotation = ui::Transform::ROT_0) + RotationFlags rotation = ui::Transform::ROT_0) : mReqWidth(reqWidth), mReqHeight(reqHeight), mReqDataSpace(reqDataSpace), @@ -66,20 +68,20 @@ public: virtual Rect getSourceCrop() const = 0; // Returns the rotation of the source crop and the layers. - ui::Transform::orientation_flags getRotationFlags() const { return mRotationFlags; }; + RotationFlags getRotationFlags() const { return mRotationFlags; } // Returns the size of the physical render area. - int getReqWidth() const { return mReqWidth; }; - int getReqHeight() const { return mReqHeight; }; + int getReqWidth() const { return mReqWidth; } + int getReqHeight() const { return mReqHeight; } // Returns the composition data space of the render area. ui::Dataspace getReqDataSpace() const { return mReqDataSpace; } // Returns the fill color of the physical render area. Regions not // covered by any rendered layer should be filled with this color. - CaptureFill getCaptureFill() const { return mCaptureFill; }; + CaptureFill getCaptureFill() const { return mCaptureFill; } - virtual const sp getDisplayDevice() const = 0; + virtual sp getDisplayDevice() const = 0; // Returns the source display viewport. const Rect& getDisplayViewport() const { return mDisplayViewport; } @@ -89,7 +91,7 @@ private: const uint32_t mReqHeight; const ui::Dataspace mReqDataSpace; const CaptureFill mCaptureFill; - const ui::Transform::orientation_flags mRotationFlags; + const RotationFlags mRotationFlags; const Rect mDisplayViewport; }; diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 8c1d168cdb..92f40ff00f 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -163,21 +163,6 @@ bool isWideColorMode(const ColorMode colorMode) { return false; } -ui::Transform::orientation_flags fromSurfaceComposerRotation(ISurfaceComposer::Rotation rotation) { - switch (rotation) { - case ISurfaceComposer::eRotateNone: - return ui::Transform::ROT_0; - case ISurfaceComposer::eRotate90: - return ui::Transform::ROT_90; - case ISurfaceComposer::eRotate180: - return ui::Transform::ROT_180; - case ISurfaceComposer::eRotate270: - return ui::Transform::ROT_270; - } - ALOGE("Invalid rotation passed to captureScreen(): %d\n", rotation); - return ui::Transform::ROT_0; -} - #pragma clang diagnostic pop class ConditionalLock { @@ -215,7 +200,7 @@ bool SurfaceFlinger::hasSyncFramework; bool SurfaceFlinger::useVrFlinger; int64_t SurfaceFlinger::maxFrameBufferAcquiredBuffers; bool SurfaceFlinger::hasWideColorDisplay; -int SurfaceFlinger::primaryDisplayOrientation = DisplayState::eOrientationDefault; +ui::Rotation SurfaceFlinger::internalDisplayOrientation = ui::ROTATION_0; bool SurfaceFlinger::useColorManagement; bool SurfaceFlinger::useContextPriority; Dataspace SurfaceFlinger::defaultCompositionDataspace = Dataspace::V0_SRGB; @@ -298,23 +283,21 @@ SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipI useContextPriority = use_context_priority(true); - auto tmpPrimaryDisplayOrientation = primary_display_orientation( - SurfaceFlingerProperties::primary_display_orientation_values::ORIENTATION_0); - switch (tmpPrimaryDisplayOrientation) { - case SurfaceFlingerProperties::primary_display_orientation_values::ORIENTATION_90: - SurfaceFlinger::primaryDisplayOrientation = DisplayState::eOrientation90; + using Values = SurfaceFlingerProperties::primary_display_orientation_values; + switch (primary_display_orientation(Values::ORIENTATION_0)) { + case Values::ORIENTATION_0: break; - case SurfaceFlingerProperties::primary_display_orientation_values::ORIENTATION_180: - SurfaceFlinger::primaryDisplayOrientation = DisplayState::eOrientation180; + case Values::ORIENTATION_90: + internalDisplayOrientation = ui::ROTATION_90; break; - case SurfaceFlingerProperties::primary_display_orientation_values::ORIENTATION_270: - SurfaceFlinger::primaryDisplayOrientation = DisplayState::eOrientation270; + case Values::ORIENTATION_180: + internalDisplayOrientation = ui::ROTATION_180; break; - default: - SurfaceFlinger::primaryDisplayOrientation = DisplayState::eOrientationDefault; + case Values::ORIENTATION_270: + internalDisplayOrientation = ui::ROTATION_270; break; } - ALOGV("Primary Display Orientation is set to %2d.", SurfaceFlinger::primaryDisplayOrientation); + ALOGV("Internal Display Orientation: %s", toCString(internalDisplayOrientation)); mInternalDisplayPrimaries = sysprop::getDisplayNativePrimaries(); @@ -789,9 +772,8 @@ status_t SurfaceFlinger::getDisplayConfigs(const sp& displayToken, } info.density = density; - // TODO: this needs to go away (currently needed only by webkit) const auto display = getDefaultDisplayDeviceLocked(); - info.orientation = display ? display->getOrientation() : 0; + info.orientation = display->getOrientation(); // This is for screenrecord const Rect viewport = display->getViewport(); @@ -804,7 +786,6 @@ status_t SurfaceFlinger::getDisplayConfigs(const sp& displayToken, // TODO: where should this value come from? static const int TV_DENSITY = 213; info.density = TV_DENSITY / 160.0f; - info.orientation = 0; const auto display = getDisplayDeviceLocked(displayToken); info.layerStack = display->getLayerStack(); @@ -835,7 +816,8 @@ status_t SurfaceFlinger::getDisplayConfigs(const sp& displayToken, info.secure = true; if (displayId == getInternalDisplayIdLocked() && - primaryDisplayOrientation & DisplayState::eOrientationSwapMask) { + (internalDisplayOrientation == ui::ROTATION_90 || + internalDisplayOrientation == ui::ROTATION_270)) { std::swap(info.w, info.h); } @@ -2279,8 +2261,8 @@ sp SurfaceFlinger::setupNewDisplayDeviceInternal( nativeWindow->setSwapInterval(nativeWindow.get(), 0); } - creationArgs.displayInstallOrientation = - isInternalDisplay ? primaryDisplayOrientation : DisplayState::eOrientationDefault; + creationArgs.physicalOrientation = + isInternalDisplay ? internalDisplayOrientation : ui::ROTATION_0; // virtual displays are always considered enabled creationArgs.initialPowerMode = state.isVirtual() ? HWC_POWER_MODE_NORMAL : HWC_POWER_MODE_OFF; @@ -3817,7 +3799,7 @@ void SurfaceFlinger::onInitializeDisplays() { DisplayState::eLayerStackChanged; d.token = token; d.layerStack = 0; - d.orientation = DisplayState::eOrientationDefault; + d.orientation = ui::ROTATION_0; d.frame.makeInvalid(); d.viewport.makeInvalid(); d.width = 0; @@ -4426,8 +4408,8 @@ void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, std::string& result) co if (const auto display = getDefaultDisplayDeviceLocked()) { display->getCompositionDisplay()->getState().undefinedRegion.dump(result, "undefinedRegion"); - StringAppendF(&result, " orientation=%d, isPoweredOn=%d\n", display->getOrientation(), - display->isPoweredOn()); + StringAppendF(&result, " orientation=%s, isPoweredOn=%d\n", + toCString(display->getOrientation()), display->isPoweredOn()); } StringAppendF(&result, " transaction-flags : %08x\n" @@ -5004,17 +4986,19 @@ private: status_t SurfaceFlinger::captureScreen(const sp& displayToken, sp* outBuffer, bool& outCapturedSecureLayers, - const Dataspace reqDataspace, - const ui::PixelFormat reqPixelFormat, Rect sourceCrop, - uint32_t reqWidth, uint32_t reqHeight, - bool useIdentityTransform, - ISurfaceComposer::Rotation rotation, - bool captureSecureLayers) { + Dataspace reqDataspace, ui::PixelFormat reqPixelFormat, + const Rect& sourceCrop, uint32_t reqWidth, + uint32_t reqHeight, bool useIdentityTransform, + ui::Rotation rotation, bool captureSecureLayers) { ATRACE_CALL(); if (!displayToken) return BAD_VALUE; - auto renderAreaRotation = fromSurfaceComposerRotation(rotation); + auto renderAreaRotation = ui::Transform::toRotationFlags(rotation); + if (renderAreaRotation == ui::Transform::ROT_INVALID) { + ALOGE("%s: Invalid rotation: %s", __FUNCTION__, toCString(rotation)); + renderAreaRotation = ui::Transform::ROT_0; + } sp display; { @@ -5076,7 +5060,7 @@ status_t SurfaceFlinger::captureScreen(uint64_t displayOrLayerStack, Dataspace* sp display; uint32_t width; uint32_t height; - ui::Transform::orientation_flags captureOrientation; + ui::Transform::RotationFlags captureOrientation; { Mutex::Autolock _l(mStateLock); display = getDisplayByIdOrLayerStack(displayOrLayerStack); @@ -5087,12 +5071,25 @@ status_t SurfaceFlinger::captureScreen(uint64_t displayOrLayerStack, Dataspace* width = uint32_t(display->getViewport().width()); height = uint32_t(display->getViewport().height()); - captureOrientation = fromSurfaceComposerRotation( - static_cast(display->getOrientation())); - if (captureOrientation == ui::Transform::orientation_flags::ROT_90) { - captureOrientation = ui::Transform::orientation_flags::ROT_270; - } else if (captureOrientation == ui::Transform::orientation_flags::ROT_270) { - captureOrientation = ui::Transform::orientation_flags::ROT_90; + const auto orientation = display->getOrientation(); + captureOrientation = ui::Transform::toRotationFlags(orientation); + + switch (captureOrientation) { + case ui::Transform::ROT_90: + captureOrientation = ui::Transform::ROT_270; + break; + + case ui::Transform::ROT_270: + captureOrientation = ui::Transform::ROT_90; + break; + + case ui::Transform::ROT_INVALID: + ALOGE("%s: Invalid orientation: %s", __FUNCTION__, toCString(orientation)); + captureOrientation = ui::Transform::ROT_0; + break; + + default: + break; } *outDataspace = pickDataspaceFromColorMode(display->getCompositionDisplay()->getState().colorMode); @@ -5140,7 +5137,7 @@ status_t SurfaceFlinger::captureLayers( } bool isSecure() const override { return false; } bool needsFiltering() const override { return mNeedsFiltering; } - const sp getDisplayDevice() const override { return nullptr; } + sp getDisplayDevice() const override { return nullptr; } Rect getSourceCrop() const override { if (mCrop.isEmpty()) { return getBounds(); diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 2f84b138d2..61876b2b36 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -229,7 +229,7 @@ public: // found on devices with wide color gamut (e.g. Display-P3) display. static bool hasWideColorDisplay; - static int primaryDisplayOrientation; + static ui::Rotation internalDisplayOrientation; // Indicate if device wants color management on its display. static bool useColorManagement; @@ -415,10 +415,10 @@ private: ISurfaceComposer::ConfigChanged configChanged = ISurfaceComposer::eConfigChangedSuppress) override; status_t captureScreen(const sp& displayToken, sp* outBuffer, - bool& outCapturedSecureLayers, const ui::Dataspace reqDataspace, - const ui::PixelFormat reqPixelFormat, Rect sourceCrop, - uint32_t reqWidth, uint32_t reqHeight, - bool useIdentityTransform, ISurfaceComposer::Rotation rotation, bool captureSecureLayers) override; + bool& outCapturedSecureLayers, ui::Dataspace reqDataspace, + ui::PixelFormat reqPixelFormat, const Rect& sourceCrop, + uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform, + ui::Rotation rotation, bool captureSecureLayers) override; status_t captureScreen(uint64_t displayOrLayerStack, ui::Dataspace* outDataspace, sp* outBuffer) override; status_t captureLayers( diff --git a/services/surfaceflinger/SurfaceInterceptor.cpp b/services/surfaceflinger/SurfaceInterceptor.cpp index 5e8910a3c5..8e0462ab52 100644 --- a/services/surfaceflinger/SurfaceInterceptor.cpp +++ b/services/surfaceflinger/SurfaceInterceptor.cpp @@ -137,8 +137,8 @@ void SurfaceInterceptor::addInitialDisplayStateLocked(Increment* increment, addDisplaySurfaceLocked(transaction, display.sequenceId, display.surface); addDisplayLayerStackLocked(transaction, display.sequenceId, display.layerStack); addDisplaySizeLocked(transaction, display.sequenceId, display.width, display.height); - addDisplayProjectionLocked(transaction, display.sequenceId, display.orientation, - display.viewport, display.frame); + addDisplayProjectionLocked(transaction, display.sequenceId, toRotationInt(display.orientation), + display.viewport, display.frame); } status_t SurfaceInterceptor::writeProtoFileLocked() { @@ -467,8 +467,8 @@ void SurfaceInterceptor::addDisplayChangesLocked(Transaction* transaction, addDisplaySizeLocked(transaction, sequenceId, state.width, state.height); } if (state.what & DisplayState::eDisplayProjectionChanged) { - addDisplayProjectionLocked(transaction, sequenceId, state.orientation, state.viewport, - state.frame); + addDisplayProjectionLocked(transaction, sequenceId, toRotationInt(state.orientation), + state.viewport, state.frame); } } diff --git a/services/surfaceflinger/tests/Credentials_test.cpp b/services/surfaceflinger/tests/Credentials_test.cpp index b1bb7fdef9..f6188738b2 100644 --- a/services/surfaceflinger/tests/Credentials_test.cpp +++ b/services/surfaceflinger/tests/Credentials_test.cpp @@ -23,7 +23,6 @@ using ui::ColorMode; namespace { const String8 DISPLAY_NAME("Credentials Display Test"); const String8 SURFACE_NAME("Test Surface Name"); -const uint32_t ROTATION = 0; const float FRAME_SCALE = 1.0f; } // namespace @@ -262,7 +261,7 @@ TEST_F(CredentialsTest, CaptureTest) { sp outBuffer; return ScreenshotClient::capture(display, ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888, Rect(), 0 /*reqWidth*/, - 0 /*reqHeight*/, false, ROTATION, &outBuffer); + 0 /*reqHeight*/, false, ui::ROTATION_0, &outBuffer); }; ASSERT_NO_FATAL_FAILURE(checkWithPrivileges(condition, NO_ERROR, PERMISSION_DENIED)); } diff --git a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp index 627de7a12e..92698f0142 100644 --- a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp +++ b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp @@ -189,12 +189,12 @@ TEST_P(LayerRenderTypeTransactionTest, SetSizeWithScaleToWindow_BufferQueue) { } TEST_P(LayerRenderTypeTransactionTest, CreateLayer_BufferState) { - uint32_t transformHint = ui::Transform::orientation_flags::ROT_INVALID; + uint32_t transformHint = ui::Transform::ROT_INVALID; sp layer; ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState, /*parent*/ nullptr, &transformHint)); - ASSERT_NE(ui::Transform::orientation_flags::ROT_INVALID, transformHint); + ASSERT_NE(ui::Transform::ROT_INVALID, transformHint); } void LayerRenderTypeTransactionTest::setRelativeZBasicHelper(uint32_t layerType) { diff --git a/services/surfaceflinger/tests/LayerTransaction_test.cpp b/services/surfaceflinger/tests/LayerTransaction_test.cpp index 35c51e1915..7816c667bb 100644 --- a/services/surfaceflinger/tests/LayerTransaction_test.cpp +++ b/services/surfaceflinger/tests/LayerTransaction_test.cpp @@ -53,7 +53,7 @@ TEST_F(LayerTransactionTest, SetFlagsSecureEUidSystem) { ASSERT_EQ(NO_ERROR, composer->captureScreen(mDisplay, &outBuffer, outCapturedSecureLayers, ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888, Rect(), 0, - 0, false, ISurfaceComposer::eRotateNone, true)); + 0, false, ui::ROTATION_0, true)); ASSERT_EQ(true, outCapturedSecureLayers); ScreenCapture sc(outBuffer); sc.expectColor(Rect(0, 0, 32, 32), Color::RED); diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp index 76e8171255..55c3ab8b22 100644 --- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp +++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp @@ -2040,8 +2040,8 @@ TEST_F(HandleTransactionLockedTest, processesDisplayLayerStackChanges) { TEST_F(HandleTransactionLockedTest, processesDisplayTransformChanges) { using Case = NonHwcVirtualDisplayCase; - constexpr int oldTransform = 0; - constexpr int newTransform = 2; + constexpr ui::Rotation oldTransform = ui::ROTATION_0; + constexpr ui::Rotation newTransform = ui::ROTATION_180; // -------------------------------------------------------------------- // Preconditions @@ -2414,7 +2414,7 @@ TEST_F(DisplayTransactionTest, setDisplayStateLockedRequestsUpdateIfLayerStackCh TEST_F(DisplayTransactionTest, setDisplayStateLockedDoesNothingIfProjectionDidNotChange) { using Case = SimplePrimaryDisplayCase; - constexpr int initialOrientation = 180; + constexpr ui::Rotation initialOrientation = ui::ROTATION_180; const Rect initialFrame = {1, 2, 3, 4}; const Rect initialViewport = {5, 6, 7, 8}; @@ -2458,8 +2458,8 @@ TEST_F(DisplayTransactionTest, setDisplayStateLockedDoesNothingIfProjectionDidNo TEST_F(DisplayTransactionTest, setDisplayStateLockedRequestsUpdateIfOrientationChanged) { using Case = SimplePrimaryDisplayCase; - constexpr int initialOrientation = 90; - constexpr int desiredOrientation = 180; + constexpr ui::Rotation initialOrientation = ui::ROTATION_90; + constexpr ui::Rotation desiredOrientation = ui::ROTATION_180; // -------------------------------------------------------------------- // Preconditions @@ -2721,7 +2721,7 @@ TEST_F(DisplayTransactionTest, onInitializeDisplaysSetsUpPrimaryDisplay) { // The layer stack state should be set to zero EXPECT_EQ(0u, primaryDisplayState.layerStack); // The orientation state should be set to zero - EXPECT_EQ(0, primaryDisplayState.orientation); + EXPECT_EQ(ui::ROTATION_0, primaryDisplayState.orientation); // The frame state should be set to INVALID EXPECT_EQ(Rect::INVALID_RECT, primaryDisplayState.frame); diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h index b5245e2579..9728c80888 100644 --- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h +++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h @@ -371,7 +371,6 @@ public: */ auto& mutableHasWideColorDisplay() { return SurfaceFlinger::hasWideColorDisplay; } - auto& mutablePrimaryDisplayOrientation() { return SurfaceFlinger::primaryDisplayOrientation; } auto& mutableUseColorManagement() { return SurfaceFlinger::useColorManagement; } auto& mutableCurrentState() { return mFlinger->mCurrentState; } -- cgit v1.2.3-59-g8ed1b From 2f98494d4d689a9484942435e4101114daadd91f Mon Sep 17 00:00:00 2001 From: Leon Scroggins III Date: Fri, 22 Nov 2019 17:02:23 -0500 Subject: Add NDK Apis for image decoding Bug: 135133301 Test: Ib84462ea5fa8a7779eaa44494775e182e52ecaca Change-Id: I8fd341c538f6b0d96f7abed77d58b003a3aefd12 --- include/android/imagedecoder.h | 299 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 299 insertions(+) create mode 100644 include/android/imagedecoder.h (limited to 'include') diff --git a/include/android/imagedecoder.h b/include/android/imagedecoder.h new file mode 100644 index 0000000000..50daabaaff --- /dev/null +++ b/include/android/imagedecoder.h @@ -0,0 +1,299 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @addtogroup ImageDecoder + * @{ + */ + +/** + * @file imageDecoder.h + */ + +#ifndef ANDROID_IMAGE_DECODER_H +#define ANDROID_IMAGE_DECODER_H + +#include "bitmap.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct AAsset; +struct ARect; + +#if __ANDROID_API__ >= 30 + +/** AImageDecoder functions result code. */ +enum { + // Decoding was successful and complete. + ANDROID_IMAGE_DECODER_SUCCESS = 0, + // The input was incomplete. In decodeImage, this means a partial + // image was decoded. Undecoded lines are all zeroes. + // In AImageDecoder_create*, no AImageDecoder was created. + ANDROID_IMAGE_DECODER_INCOMPLETE = -1, + // The input contained an error after decoding some lines. Similar to + // INCOMPLETE, above. + ANDROID_IMAGE_DECODER_ERROR = -2, + // Could not convert, e.g. attempting to decode an image with + // alpha to an opaque format. + ANDROID_IMAGE_DECODER_INVALID_CONVERSION = -3, + // The scale is invalid. It may have overflowed, or it may be incompatible + // with the current alpha setting. + ANDROID_IMAGE_DECODER_INVALID_SCALE = -4, + // Some other parameter was bad (e.g. pixels) + ANDROID_IMAGE_DECODER_BAD_PARAMETER = -5, + // Input was invalid i.e. broken before decoding any pixels. + ANDROID_IMAGE_DECODER_INVALID_INPUT = -6, + // A seek was required, and failed. + ANDROID_IMAGE_DECODER_SEEK_ERROR = -7, + // Some other error (e.g. OOM) + ANDROID_IMAGE_DECODER_INTERNAL_ERROR = -8, + // We did not recognize the format + ANDROID_IMAGE_DECODER_UNSUPPORTED_FORMAT = -9 +}; + +struct AImageDecoder; + +/** + * Opaque handle for decoding images. + * + * Create using one of the following: + * - {@link AImageDecoder_createFromAAsset} + * - {@link AImageDecoder_createFromFd} + * - {@link AImageDecoder_createFromBuffer} + */ +typedef struct AImageDecoder AImageDecoder; + +/** + * Create a new AImageDecoder from an AAsset. + * + * @param asset {@link AAsset} containing encoded image data. Client is still + * responsible for calling {@link AAsset_close} on it. + * @param outDecoder On success (i.e. return value is + * {@link ANDROID_IMAGE_DECODER_SUCCESS}), this will be set to + * a newly created {@link AImageDecoder}. Caller is + * responsible for calling {@link AImageDecoder_delete} on it. + * @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success or a value + * indicating reason for the failure. + */ +int AImageDecoder_createFromAAsset(AAsset* asset, AImageDecoder** outDecoder) __INTRODUCED_IN(30); + +/** + * Create a new AImageDecoder from a file descriptor. + * + * @param fd Seekable, readable, open file descriptor for encoded data. + * Client is still responsible for closing it, which may be done + * *after* deleting the returned AImageDecoder. + * @param outDecoder On success (i.e. return value is + * {@link ANDROID_IMAGE_DECODER_SUCCESS}), this will be set to + * a newly created {@link AImageDecoder}. Caller is + * responsible for calling {@link AImageDecoder_delete} on it. + * @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success or a value + * indicating reason for the failure. + */ +int AImageDecoder_createFromFd(int fd, AImageDecoder** outDecoder) __INTRODUCED_IN(30); + +/** + * Create a new AImageDecoder from a buffer. + * + * @param buffer Pointer to encoded data. Must be valid for the entire time + * the AImageDecoder is used. + * @param length Byte length of buffer. + * @param outDecoder On success (i.e. return value is + * {@link ANDROID_IMAGE_DECODER_SUCCESS}), this will be set to + * a newly created {@link AImageDecoder}. Caller is + * responsible for calling {@link AImageDecoder_delete} on it. + * @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success or a value + * indicating reason for the failure. + */ +int AImageDecoder_createFromBuffer(const void* buffer, size_t length, + AImageDecoder** outDecoder) __INTRODUCED_IN(30); + +/** + * Delete the AImageDecoder. + */ +void AImageDecoder_delete(AImageDecoder* decoder) __INTRODUCED_IN(30); + +/** + * Choose the desired output format. + * + * @param format AndroidBitmapFormat to use + * @return {@link ANDROID_IMAGE_DECODER_SUCCESS} if the format is compatible + * with the image and {@link ANDROID_IMAGE_DECODER_INVALID_CONVERSION} + * otherwise. In the latter case, the AImageDecoder uses the + * format it was already planning to use (either its default + * or a previously successful setting from this function). + */ +int AImageDecoder_setAndroidBitmapFormat(AImageDecoder*, + int32_t format) __INTRODUCED_IN(30); + +/* + * Choose the desired output format. + * + * Must be one of: + * {@link ANDROID_BITMAP_FLAGS_ALPHA_PREMUL} + * {@link ANDROID_BITMAP_FLAGS_ALPHA_OPAQUE} + * {@link ANDROID_BITMAP_FLAGS_ALPHA_UNPREMUL} + * + * Note: An OPAQUE image may be set to any of them. + * A non-OPAQUE image may not be set to OPAQUE + * + * @return - {@link ANDROID_IMAGE_DECODER_SUCCESS} on success + * - {@link ANDROID_IMAGE_DECODER_INVALID_CONVERSION} if the conversion + * is not possible + * - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER} for bad parameters + */ +int AImageDecoder_setAlphaFlags(AImageDecoder*, int alphaFlags) __INTRODUCED_IN(30); + +/** + * Specify the output size for a decoded image. + * + * Future calls to {@link AImageDecoder_decodeImage} will sample or scale the + * encoded image to reach the desired size. If a crop rect is set (via + * {@link AImageDecoder_setCrop}), it must be contained within the dimensions + * specified by width and height, and the output image will be the size of the + * crop rect. + * + * @param width Width of the output (prior to cropping). + * This will affect future calls to + * {@link AImageDecoder_getMinimumStride}, which will now return + * a value based on this width. + * @param height Height of the output (prior to cropping). + * @return - {@link ANDROID_IMAGE_DECODER_SUCCESS} on success + * - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER} if the AImageDecoder + * pointer is null, width or height is <= 0, or any existing crop is + * not contained by the image dimensions. + */ +int AImageDecoder_setTargetSize(AImageDecoder*, int width, int height) __INTRODUCED_IN(30); + +/** + * Specify how to crop the output after scaling (if any). + * + * Future calls to {@link AImageDecoder_decodeImage} will crop their output to + * the specified {@link ARect}. Clients will only need to allocate enough memory + * for the cropped ARect. + * + * @param crop Rectangle describing a crop of the decode. It must be contained inside of + * the (possibly scaled, by {@link AImageDecoder_setTargetSize}) + * image dimensions. This will affect future calls to + * {@link AImageDecoder_getMinimumStride}, which will now return a + * value based on the width of the crop. An empty ARect - + * specifically { 0, 0, 0, 0 } - may be used to remove the cropping + * behavior. Any other empty or unsorted ARects will result in + * returning ANDROID_IMAGE_DECODER_BAD_PARAMETER. + * @return - {@link ANDROID_IMAGE_DECODER_SUCCESS} on success + * - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER} if the AImageDecoder + * pointer is null or the crop is not contained by the image + * dimensions. + */ +int AImageDecoder_setCrop(AImageDecoder*, ARect crop) __INTRODUCED_IN(30); + +/** + * Opaque handle for reading header info. + */ +struct AImageDecoderHeaderInfo; +typedef struct AImageDecoderHeaderInfo AImageDecoderHeaderInfo; + +/** + * Return an opaque handle for reading header info. + * + * This is owned by the {@link AImageDecoder} and will be destroyed when the + * AImageDecoder is destroyed via {@link AImageDecoder_delete}. + */ +const AImageDecoderHeaderInfo* AImageDecoder_getHeaderInfo( + const AImageDecoder*) __INTRODUCED_IN(30); + +/** + * Report the native width of the encoded image. + */ +int32_t AImageDecoderHeaderInfo_getWidth(const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30); + +/** + * Report the native height of the encoded image. + */ +int32_t AImageDecoderHeaderInfo_getHeight(const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30); + +/** + * Report the mimeType of the encoded image. + * + * @return a string literal describing the mime type. + */ +const char* AImageDecoderHeaderInfo_getMimeType( + const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30); + +/** + * Report whether the encoded image represents an animation. + */ +bool AImageDecoderHeaderInfo_isAnimated( + const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30); + +/** + * Report the AndroidBitmapFormat the AImageDecoder will decode to + * by default. AImageDecoder will try to choose one that is sensible + * for the image and the system. Note that this does not indicate the + * encoded format of the image. + */ +AndroidBitmapFormat AImageDecoderHeaderInfo_getAndroidBitmapFormat( + const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30); + +/** + * Report how the AImageDecoder will handle alpha by default. If the image + * contains no alpha (according to its header), this will return + * {@link ANDROID_BITMAP_FLAGS_ALPHA_OPAQUE}. If the image may contain alpha, + * this returns {@link ANDROID_BITMAP_FLAGS_ALPHA_PREMUL}. + * + * For animated images only the opacity of the first frame is reported. + */ +int AImageDecoderHeaderInfo_getAlphaFlags( + const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30); + +/** + * Return the minimum stride that can be used, taking the specified + * (or default) (possibly scaled) width, crop rect and + * {@link AndroidBitmapFormat} into account. + */ +size_t AImageDecoder_getMinimumStride(AImageDecoder*) __INTRODUCED_IN(30); + +/** + * Decode the image into pixels, using the settings of the AImageDecoder. + * + * @param decoder Opaque object representing the decoder. + * @param pixels On success, will be filled with the result + * of the decode. Must be large enough to fit |size| bytes. + * @param stride Width in bytes of a single row. Must be at least + * {@link AImageDecoder_getMinimumStride}. + * @param size Size of the pixel buffer in bytes. Must be at least + * stride * (height - 1) + + * {@link AImageDecoder_getMinimumStride}. + * @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success, or an error code + * from the same enum describing the failure. + */ +int AImageDecoder_decodeImage(AImageDecoder* decoder, + void* pixels, size_t stride, + size_t size) __INTRODUCED_IN(30); + +#endif // __ANDROID_API__ >= 30 + +#ifdef __cplusplus +} +#endif + +#endif // ANDROID_IMAGE_DECODER_H + +/** @} */ -- cgit v1.2.3-59-g8ed1b From a2a087942db377c3d643ed2949c5bf9668460a26 Mon Sep 17 00:00:00 2001 From: Kweku Adams Date: Fri, 10 Jan 2020 17:25:25 +0000 Subject: Revert "Add AndroidBitmap_getDataSpace" Revert "Test AndroidBitmap_getDataSpace" Revert submission 9940762-_getDataSpace Reason for revert: Breaks build: http://screen/kBMYHusKiOV.png Reverted Changes: Ie05a45da3: Implement AndroidBitmap_getDataSpace I7a5fcb726: Test AndroidBitmap_getDataSpace Ia46dfb39d: Add AndroidBitmap_getDataSpace Change-Id: Ie6c9065f515442f3ea59d3930208deffb9a3bd2a --- include/android/bitmap.h | 13 ------------- 1 file changed, 13 deletions(-) (limited to 'include') diff --git a/include/android/bitmap.h b/include/android/bitmap.h index 41718b2904..01cf2f88ea 100644 --- a/include/android/bitmap.h +++ b/include/android/bitmap.h @@ -100,19 +100,6 @@ typedef struct { int AndroidBitmap_getInfo(JNIEnv* env, jobject jbitmap, AndroidBitmapInfo* info); -#if __ANDROID_API__ >= 30 - -/** - * Given a java bitmap object, return its ADataSpace. - * - * Note that ADataSpace only exposes a few values. This may return - * ADATASPACE_UNKNOWN, even for Named ColorSpaces, if they have no - * corresponding ADataSpace. - */ -int32_t AndroidBitmap_getDataSpace(JNIEnv* env, jobject jbitmap) __INTRODUCED_IN(30); - -#endif // __ANDROID_API__ >= 30 - /** * Given a java bitmap object, attempt to lock the pixel address. * Locking will ensure that the memory for the pixels will not move -- cgit v1.2.3-59-g8ed1b From 3dd6455a54bb94c4059519fe9370ec3ecf33dc92 Mon Sep 17 00:00:00 2001 From: Leon Scroggins III Date: Fri, 10 Jan 2020 13:41:44 -0500 Subject: Reland "Add AndroidBitmap_getDataSpace" Originally reviewed in Ia46dfb39d0f2708ce873343ec74bcc52e7bccd3a (and reverted in Ie6c9065f515442f3ea59d3930208deffb9a3bd2a) Bug:135133301 Test: I7a5fcb726fba0c832bbb86a424d7534a7cfa35b6 This supplements AndroidBitmap_getInfo, allowing NDK clients to know how to interpret the colors in an android.graphics.Bitmap. Change-Id: If822af556ac5ee6d412bf31afb9d07de6dd777eb --- include/android/bitmap.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'include') diff --git a/include/android/bitmap.h b/include/android/bitmap.h index 01cf2f88ea..41718b2904 100644 --- a/include/android/bitmap.h +++ b/include/android/bitmap.h @@ -100,6 +100,19 @@ typedef struct { int AndroidBitmap_getInfo(JNIEnv* env, jobject jbitmap, AndroidBitmapInfo* info); +#if __ANDROID_API__ >= 30 + +/** + * Given a java bitmap object, return its ADataSpace. + * + * Note that ADataSpace only exposes a few values. This may return + * ADATASPACE_UNKNOWN, even for Named ColorSpaces, if they have no + * corresponding ADataSpace. + */ +int32_t AndroidBitmap_getDataSpace(JNIEnv* env, jobject jbitmap) __INTRODUCED_IN(30); + +#endif // __ANDROID_API__ >= 30 + /** * Given a java bitmap object, attempt to lock the pixel address. * Locking will ensure that the memory for the pixels will not move -- cgit v1.2.3-59-g8ed1b From 7f2d5b668c1bd20688cd6f394045a88c403c02c4 Mon Sep 17 00:00:00 2001 From: Leon Scroggins III Date: Fri, 13 Dec 2019 14:57:07 -0500 Subject: Add NDK Bitmap compression Bug: 135133301 Test: Ifbcb41388a48afc64bb22623bb7e981b288b2457 Add an enum and a method for compression. Change-Id: I81954dba9045f59e3e12012e7df4a14ef20c55ed --- include/android/bitmap.h | 79 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) (limited to 'include') diff --git a/include/android/bitmap.h b/include/android/bitmap.h index 41718b2904..d920a90846 100644 --- a/include/android/bitmap.h +++ b/include/android/bitmap.h @@ -26,6 +26,7 @@ #ifndef ANDROID_BITMAP_H #define ANDROID_BITMAP_H +#include #include #include @@ -133,6 +134,84 @@ int AndroidBitmap_lockPixels(JNIEnv* env, jobject jbitmap, void** addrPtr); */ int AndroidBitmap_unlockPixels(JNIEnv* env, jobject jbitmap); +#if __ANDROID_API__ >= 30 + +// Note: these values match android.graphics.Bitmap#compressFormat. + +/** + * Specifies the formats that can be compressed to with + * {@link AndroidBitmap_compress}. + */ +enum AndroidBitmapCompressFormat { + /** + * Compress to the JPEG format. quality of 0 means + * compress for the smallest size. 100 means compress for max + * visual quality. + */ + ANDROID_BITMAP_COMPRESS_FORMAT_JPEG = 0, + /** + * Compress to the PNG format. PNG is lossless, so quality is + * ignored. + */ + ANDROID_BITMAP_COMPRESS_FORMAT_PNG = 1, + /** + * Compress to the WEBP lossy format. quality of 0 means + * compress for the smallest size. 100 means compress for max + * visual quality. + */ + ANDROID_BITMAP_COMPRESS_FORMAT_WEBP_LOSSY = 3, + /** + * Compress to the WEBP lossless format. quality refers to how + * much effort to put into compression. A value of 0 means to + * compress quickly, resulting in a relatively large file size. + * 100 means to spend more time compressing, resulting in a + * smaller file. + */ + ANDROID_BITMAP_COMPRESS_FORMAT_WEBP_LOSSLESS = 4, +}; + +/** + * User-defined function for writing the output of compression. + * + * @param userContext Pointer to user-defined data passed to + * {@link AndroidBitmap_compress}. + * @param data Compressed data of |size| bytes to write. + * @param size Length in bytes of data to write. + * @return Whether the operation succeeded. + */ +typedef bool (*AndroidBitmap_compress_write_fn)(void* userContext, + const void* data, + size_t size) __INTRODUCED_IN(30); + +/** + * Compress |pixels| as described by |info|. + * + * @param info Description of the pixels to compress. + * @param dataspace {@link ADataSpace} describing the color space of the + * pixels. + * @param pixels Pointer to pixels to compress. + * @param format (@link AndroidBitmapCompressFormat} to compress to. + * @param quality Hint to the compressor, 0-100. The value is interpreted + * differently depending on the + * {@link AndroidBitmapCompressFormat}. + * @param userContext User-defined data which will be passed to the supplied + * {@link AndroidBitmap_compress_write_fn} each time it is + * called. May be null. + * @parm fn Function that writes the compressed data. Will be called each time + * the compressor has compressed more data that is ready to be + * written. May be called more than once for each call to this method. + * May not be null. + * @return AndroidBitmap functions result code. + */ +int AndroidBitmap_compress(const AndroidBitmapInfo* info, + int32_t dataspace, + const void* pixels, + int32_t format, int32_t quality, + void* userContext, + AndroidBitmap_compress_write_fn fn) __INTRODUCED_IN(30); + +#endif // __ANDROID_API__ >= 30 + #ifdef __cplusplus } #endif -- cgit v1.2.3-59-g8ed1b From 1be112f2af35ea040d0203c765f7e229ba793a08 Mon Sep 17 00:00:00 2001 From: Leon Scroggins III Date: Wed, 15 Jan 2020 04:26:44 -0500 Subject: Rename setAlphaFlags to setUnpremultipliedRequired Bug: 135133301 Test: I48e49ee08ab1954eddf62ecae87942aeb128c10d There is never any reason to request OPAQUE. If the image is already opaque, using PREMUL or UNPREMUL has no effect. If the image is not opaque, the requesting OPAQUE is an error. This behavior is not helpful. In addition, this matches the Java API for android.graphics.ImageDecoder. Lastly, the old API was confusing for animated images. It is possible for the first frame to be opaque, while a later frame is not. Requesting OPAQUE seems reasonable for this image, until decoding the non-opaque frame, at which point the inconsistency shows. Having a setting of unpremul or not makes it obvious what will happen for the later frame. Change-Id: I3381582e27894e1072db9b8635f3762b801f5d69 --- include/android/imagedecoder.h | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/android/imagedecoder.h b/include/android/imagedecoder.h index 50daabaaff..4b6446c33d 100644 --- a/include/android/imagedecoder.h +++ b/include/android/imagedecoder.h @@ -143,23 +143,20 @@ void AImageDecoder_delete(AImageDecoder* decoder) __INTRODUCED_IN(30); int AImageDecoder_setAndroidBitmapFormat(AImageDecoder*, int32_t format) __INTRODUCED_IN(30); -/* - * Choose the desired output format. - * - * Must be one of: - * {@link ANDROID_BITMAP_FLAGS_ALPHA_PREMUL} - * {@link ANDROID_BITMAP_FLAGS_ALPHA_OPAQUE} - * {@link ANDROID_BITMAP_FLAGS_ALPHA_UNPREMUL} +/** + * Specify whether the output's pixels should be unpremultiplied. * - * Note: An OPAQUE image may be set to any of them. - * A non-OPAQUE image may not be set to OPAQUE + * By default, the decoder will premultiply the pixels, if they have alpha. Pass + * false to this method to leave them unpremultiplied. This has no effect on an + * opaque image. * + * @param required Pass true to leave the pixels unpremultiplied. * @return - {@link ANDROID_IMAGE_DECODER_SUCCESS} on success * - {@link ANDROID_IMAGE_DECODER_INVALID_CONVERSION} if the conversion * is not possible * - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER} for bad parameters */ -int AImageDecoder_setAlphaFlags(AImageDecoder*, int alphaFlags) __INTRODUCED_IN(30); +int AImageDecoder_setUnpremultipliedRequired(AImageDecoder*, bool required) __INTRODUCED_IN(30); /** * Specify the output size for a decoded image. -- cgit v1.2.3-59-g8ed1b From 421eb1c2edc0f69d80cb9182a6d5a970501f8699 Mon Sep 17 00:00:00 2001 From: Arthur Hung Date: Thu, 16 Jan 2020 00:09:42 +0800 Subject: Touch cancellation handling Cancel motion event that are accidental touches when a user is holding the device and a touch stream is in progress. - Process MT_TOOL_PALM and cancel touch when sent from firmware. Bug: 117933934 Test: atest inputflinger_tests Test: atest InputProcessTest Change-Id: I80ba8b462572e5214dd1073ef0568886f71e21c9 --- include/android/input.h | 2 + .../reader/mapper/MultiTouchInputMapper.cpp | 10 +++ services/inputflinger/tests/InputReader_test.cpp | 91 ++++++++++++++++++++++ 3 files changed, 103 insertions(+) (limited to 'include') diff --git a/include/android/input.h b/include/android/input.h index f51cd79504..dbfd61eb05 100644 --- a/include/android/input.h +++ b/include/android/input.h @@ -793,6 +793,8 @@ enum { AMOTION_EVENT_TOOL_TYPE_MOUSE = 3, /** eraser */ AMOTION_EVENT_TOOL_TYPE_ERASER = 4, + /** palm */ + AMOTION_EVENT_TOOL_TYPE_PALM = 5, }; /** diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp index c567c8bf80..f42ddcf461 100644 --- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp @@ -209,6 +209,8 @@ int32_t MultiTouchMotionAccumulator::Slot::getToolType() const { return AMOTION_EVENT_TOOL_TYPE_FINGER; case MT_TOOL_PEN: return AMOTION_EVENT_TOOL_TYPE_STYLUS; + case MT_TOOL_PALM: + return AMOTION_EVENT_TOOL_TYPE_PALM; } } return AMOTION_EVENT_TOOL_TYPE_UNKNOWN; @@ -247,6 +249,14 @@ void MultiTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) { continue; } + if (inSlot->getToolType() == AMOTION_EVENT_TOOL_TYPE_PALM) { + if (!mCurrentMotionAborted) { + ALOGI("Canceling touch gesture from device %s because the palm event was detected", + getDeviceName().c_str()); + cancelTouch(when); + } + } + if (outCount >= MAX_POINTERS) { #if DEBUG_POINTERS ALOGD("MultiTouch device %s emitted more than maximum of %d pointers; " diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index 1fc8217df8..a9d7793a17 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -6798,4 +6798,95 @@ TEST_F(MultiTouchInputMapperTest, Viewports_SurfaceRange) { ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); } +TEST_F(MultiTouchInputMapperTest, Process_ShouldHandleSingleTouch) { + MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice); + addConfigurationProperty("touch.deviceType", "touchScreen"); + prepareDisplay(DISPLAY_ORIENTATION_0); + prepareAxes(POSITION | ID | SLOT | TOOL_TYPE); + addMapperAndConfigure(mapper); + + NotifyMotionArgs motionArgs; + + constexpr int32_t x1 = 100, y1 = 200, x2 = 120, y2 = 220, x3 = 140, y3 = 240; + // finger down + processId(mapper, 1); + processPosition(mapper, x1, y1); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action); + ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType); + + // finger move + processId(mapper, 1); + processPosition(mapper, x2, y2); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action); + ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType); + + // finger up. + processId(mapper, -1); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action); + ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType); + + // new finger down + processId(mapper, 1); + processPosition(mapper, x3, y3); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action); + ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType); +} + +/** + * Test touch should be canceled when received the MT_TOOL_PALM event, and the following MOVE and + * UP events should be ignored. + */ +TEST_F(MultiTouchInputMapperTest, Process_ShouldHandlePalmToolType) { + MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice); + addConfigurationProperty("touch.deviceType", "touchScreen"); + prepareDisplay(DISPLAY_ORIENTATION_0); + prepareAxes(POSITION | ID | SLOT | TOOL_TYPE); + addMapperAndConfigure(mapper); + + NotifyMotionArgs motionArgs; + + // default tool type is finger + constexpr int32_t x1 = 100, y1 = 200, x2 = 120, y2 = 220, x3 = 140, y3 = 240; + processId(mapper, 1); + processPosition(mapper, x1, y1); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action); + ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType); + + // Tool changed to MT_TOOL_PALM expect sending the cancel event. + processToolType(mapper, MT_TOOL_PALM); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, motionArgs.action); + + // Ignore the following MOVE and UP events if had detect a palm event. + processId(mapper, 1); + processPosition(mapper, x2, y2); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); + + // finger up. + processId(mapper, -1); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); + + // new finger down + processToolType(mapper, MT_TOOL_FINGER); + processId(mapper, 1); + processPosition(mapper, x3, y3); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action); + ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType); +} + } // namespace android -- cgit v1.2.3-59-g8ed1b From f27256b956d6a48ce8d74aaab36617a41d00cee2 Mon Sep 17 00:00:00 2001 From: Leon Scroggins III Date: Sun, 19 Jan 2020 21:13:04 -0500 Subject: Add AImageDecoder_computeSampledSize Bug: 135133301 Test: If9ed79d8dcf1169369ba454723f4ac8d26427b7b This allows an NDK client to find an efficient target size to use. Change-Id: Iabc3db1547d4863f9aa0324bc438d994eadeef01 --- include/android/imagedecoder.h | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'include') diff --git a/include/android/imagedecoder.h b/include/android/imagedecoder.h index 4b6446c33d..c9fde0d3b7 100644 --- a/include/android/imagedecoder.h +++ b/include/android/imagedecoder.h @@ -179,6 +179,28 @@ int AImageDecoder_setUnpremultipliedRequired(AImageDecoder*, bool required) __IN */ int AImageDecoder_setTargetSize(AImageDecoder*, int width, int height) __INTRODUCED_IN(30); + +/** + * Compute the dimensions to use for a given sampleSize. + * + * Although AImageDecoder can scale to an arbitrary target size (see + * {@link AImageDecoder_setTargetSize}), some sizes may be more efficient than + * others. This computes the most efficient target size to use to reach a + * particular sampleSize. + * + * @param sampleSize A subsampling rate of the original image. Must be greater + * than or equal to 1. A sampleSize of 2 means to skip every + * other pixel/line, resulting in a width and height that are + * 1/2 of the original dimensions, with 1/4 the number of + * pixels. + * @param width Out parameter for the width sampled by sampleSize, and rounded + * direction that the decoder can do most efficiently. + * @param height Out parameter for the height sampled by sampleSize, and rounded + * direction that the decoder can do most efficiently. + * @return ANDROID_IMAGE_DECODER result code. + */ +int AImageDecoder_computeSampledSize(const AImageDecoder*, int sampleSize, + int* width, int* height) __INTRODUCED_IN(30); /** * Specify how to crop the output after scaling (if any). * -- cgit v1.2.3-59-g8ed1b From d4672a8091fcf52175c8307caea60366ed784650 Mon Sep 17 00:00:00 2001 From: Leon Scroggins III Date: Sun, 19 Jan 2020 19:25:41 -0500 Subject: Add NDK methods for HARDWARE Bitmaps Bug: 135133301 Test: I2c1e58c41e49c72fb4bdbc64989da103885d34bf Add a flag for AndroidBitmapInfo.flags that represents whether the Bitmap has Config.HARDWARE. Add a method for retrieving the AHardwareBuffer if it is a HARDWARE Bitmap. Change-Id: I69b78491bc29e770d863aa01752e8c923298afb3 --- include/android/bitmap.h | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/android/bitmap.h b/include/android/bitmap.h index d920a90846..5e03c6c5aa 100644 --- a/include/android/bitmap.h +++ b/include/android/bitmap.h @@ -79,6 +79,14 @@ enum { ANDROID_BITMAP_FLAGS_ALPHA_SHIFT = 0, }; +enum { + /** If this bit is set in AndroidBitmapInfo.flags, the Bitmap uses the + * HARDWARE Config, and its AHardwareBuffer can be retrieved via + * AndroidBitmap_getHardwareBuffer. + */ + ANDROID_BITMAP_FLAGS_IS_HARDWARE = 1 << 31, +}; + /** Bitmap info, see AndroidBitmap_getInfo(). */ typedef struct { /** The bitmap width in pixels. */ @@ -90,7 +98,9 @@ typedef struct { /** The bitmap pixel format. See {@link AndroidBitmapFormat} */ int32_t format; /** Two bits are used to encode alpha. Use ANDROID_BITMAP_FLAGS_ALPHA_MASK - * and ANDROID_BITMAP_FLAGS_ALPHA_SHIFT to retrieve them. */ + * and ANDROID_BITMAP_FLAGS_ALPHA_SHIFT to retrieve them. One bit is used + * to encode whether the Bitmap uses the HARDWARE Config. Use + * ANDROID_BITMAP_FLAGS_IS_HARDWARE to know.*/ uint32_t flags; } AndroidBitmapInfo; @@ -210,6 +220,25 @@ int AndroidBitmap_compress(const AndroidBitmapInfo* info, void* userContext, AndroidBitmap_compress_write_fn fn) __INTRODUCED_IN(30); +struct AHardwareBuffer; + +/** + * Retrieve the native object associated with a HARDWARE Bitmap. + * + * Client must not modify it while a Bitmap is wrapping it. + * + * @param bitmap Handle to an android.graphics.Bitmap. + * @param outBuffer On success, is set to a pointer to the + * AHardwareBuffer associated with bitmap. This acquires + * a reference on the buffer, and the client must call + * AHardwareBuffer_release when finished with it. + * @return AndroidBitmap functions result code. + * ANDROID_BITMAP_RESULT_BAD_PARAMETER if bitmap is not a + * HARDWARE Bitmap. + */ +int AndroidBitmap_getHardwareBuffer(JNIEnv* env, jobject bitmap, + AHardwareBuffer** outBuffer) __INTRODUCED_IN(30); + #endif // __ANDROID_API__ >= 30 #ifdef __cplusplus -- cgit v1.2.3-59-g8ed1b From 20d480ce9c6bfdd54d2b1e5e34306744968a4f37 Mon Sep 17 00:00:00 2001 From: Leon Scroggins III Date: Wed, 15 Jan 2020 15:32:59 -0500 Subject: Add AImageDecoder HeaderInfo_get/_setDataSpace Bug: 135133301 Test: Iffe659e50078139188c3325545624640ae177cc2 Default to ADATASPACE_UNKNOWN for images that do not have a matching ADataSpace. This matches the behavior of android.graphics.ImageDecoder and BitmapFactory, which would use an "Unknown" ColorSpace for the same images. It also means that a client who knows the true underlying profile information to treat it as if it came from that profile. Further, it means that if we add more ADataSpace values in the future, the decode will be unchanged. A client can still choose to use a known ADataSpace using _setDataSpace. Change-Id: Id3ecf3bab17e4905fb2b75410ec756233d600c97 --- include/android/imagedecoder.h | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) (limited to 'include') diff --git a/include/android/imagedecoder.h b/include/android/imagedecoder.h index 4b6446c33d..68401ea5a2 100644 --- a/include/android/imagedecoder.h +++ b/include/android/imagedecoder.h @@ -158,6 +158,25 @@ int AImageDecoder_setAndroidBitmapFormat(AImageDecoder*, */ int AImageDecoder_setUnpremultipliedRequired(AImageDecoder*, bool required) __INTRODUCED_IN(30); +/** + * Choose the dataspace for the output. + * + * Not supported for {@link ANDROID_BITMAP_FORMAT_A_8}, which does not support + * an ADataSpace. + * + * @param dataspace The {@link ADataSpace} to decode into. An ADataSpace + * specifies how to interpret the colors. By default, + * AImageDecoder will decode into the ADataSpace specified by + * {@link AImageDecoderHeaderInfo_getDataSpace}. If this + * parameter is set to a different ADataSpace, AImageDecoder + * will transform the output into the specified ADataSpace. + * @return - {@link ANDROID_IMAGE_DECODER_SUCCESS} on success + * - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER} for a null + * AImageDecoder or an integer that does not correspond to an + * ADataSpace value. + */ +int AImageDecoder_setDataSpace(AImageDecoder*, int32_t dataspace) __INTRODUCED_IN(30); + /** * Specify the output size for a decoded image. * @@ -260,6 +279,25 @@ AndroidBitmapFormat AImageDecoderHeaderInfo_getAndroidBitmapFormat( int AImageDecoderHeaderInfo_getAlphaFlags( const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30); +/** + * Report the dataspace the AImageDecoder will decode to by default. + * AImageDecoder will try to choose one that is sensible for the + * image and the system. Note that this may not exactly match the ICC + * profile (or other color information) stored in the encoded image. + * + * @return The {@link ADataSpace} most closely representing the way the colors + * are encoded (or {@link ADATASPACE_UNKNOWN} if there is not an + * approximate ADataSpace). This specifies how to interpret the colors + * in the decoded image, unless {@link AImageDecoder_setDataSpace} is + * called to decode to a different ADataSpace. + * + * Note that ADataSpace only exposes a few values. This may return + * ADATASPACE_UNKNOWN, even for Named ColorSpaces, if they have no + * corresponding ADataSpace. + */ +int32_t AImageDecoderHeaderInfo_getDataSpace( + const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30); + /** * Return the minimum stride that can be used, taking the specified * (or default) (possibly scaled) width, crop rect and -- cgit v1.2.3-59-g8ed1b From c04a63ec68d8bb0491b291b6402206c137479d5c Mon Sep 17 00:00:00 2001 From: Leon Scroggins III Date: Wed, 22 Jan 2020 11:52:48 -0500 Subject: Rename callback for AndroidBitmap_compress Bug: 135133301 Test: No change in behavior, no new tests Rename from AndroidBitmap_compress_write_fn to AndroidBitmap_CompressWriteFunc to better fit in with media callbacks, as suggested in feedback. Change-Id: Id5d039761054cf8e7fb906624a277714c21156de --- include/android/bitmap.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/android/bitmap.h b/include/android/bitmap.h index d920a90846..f169d811ec 100644 --- a/include/android/bitmap.h +++ b/include/android/bitmap.h @@ -179,7 +179,7 @@ enum AndroidBitmapCompressFormat { * @param size Length in bytes of data to write. * @return Whether the operation succeeded. */ -typedef bool (*AndroidBitmap_compress_write_fn)(void* userContext, +typedef bool (*AndroidBitmap_CompressWriteFunc)(void* userContext, const void* data, size_t size) __INTRODUCED_IN(30); @@ -195,7 +195,7 @@ typedef bool (*AndroidBitmap_compress_write_fn)(void* userContext, * differently depending on the * {@link AndroidBitmapCompressFormat}. * @param userContext User-defined data which will be passed to the supplied - * {@link AndroidBitmap_compress_write_fn} each time it is + * {@link AndroidBitmap_CompressWriteFunc} each time it is * called. May be null. * @parm fn Function that writes the compressed data. Will be called each time * the compressor has compressed more data that is ready to be @@ -208,7 +208,7 @@ int AndroidBitmap_compress(const AndroidBitmapInfo* info, const void* pixels, int32_t format, int32_t quality, void* userContext, - AndroidBitmap_compress_write_fn fn) __INTRODUCED_IN(30); + AndroidBitmap_CompressWriteFunc fn) __INTRODUCED_IN(30); #endif // __ANDROID_API__ >= 30 -- cgit v1.2.3-59-g8ed1b From fdb1fc8fcb8c9cc780f35f7af720ccb267afef1f Mon Sep 17 00:00:00 2001 From: Anthony Stange Date: Thu, 16 Jan 2020 15:02:48 -0500 Subject: Add hinge angle sensor type to sensor NDK Bug: 144139857 Test: N/A Change-Id: Id809a8f65b31143759978702d18e6ac944d62ac3 --- include/android/sensor.h | 4 ++++ libs/sensor/Sensor.cpp | 4 ++++ libs/sensor/SensorManager.cpp | 2 +- services/sensorservice/SensorServiceUtils.cpp | 1 + 4 files changed, 10 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/android/sensor.h b/include/android/sensor.h index 3ebe79fd2e..12c00ad8a2 100644 --- a/include/android/sensor.h +++ b/include/android/sensor.h @@ -245,6 +245,10 @@ enum { * {@link ASENSOR_TYPE_ACCELEROMETER_UNCALIBRATED} */ ASENSOR_TYPE_ACCELEROMETER_UNCALIBRATED = 35, + /** + * {@link ASENSOR_TYPE_HINGE_ANGLE} + */ + ASENSOR_TYPE_HINGE_ANGLE = 36, }; /** diff --git a/libs/sensor/Sensor.cpp b/libs/sensor/Sensor.cpp index abc910302c..9d817ae0bd 100644 --- a/libs/sensor/Sensor.cpp +++ b/libs/sensor/Sensor.cpp @@ -268,6 +268,10 @@ Sensor::Sensor(struct sensor_t const& hwSensor, const uuid_t& uuid, int halVersi mStringType = SENSOR_STRING_TYPE_ACCELEROMETER_UNCALIBRATED; mFlags |= SENSOR_FLAG_CONTINUOUS_MODE; break; + case SENSOR_TYPE_HINGE_ANGLE: + mStringType = SENSOR_STRING_TYPE_HINGE_ANGLE; + mFlags |= SENSOR_FLAG_ON_CHANGE_MODE; + break; default: // Only pipe the stringType, requiredPermission and flags for custom sensors. if (halVersion > SENSORS_DEVICE_API_VERSION_1_0 && hwSensor.stringType) { diff --git a/libs/sensor/SensorManager.cpp b/libs/sensor/SensorManager.cpp index bf8b9f73fe..a4a5d135c0 100644 --- a/libs/sensor/SensorManager.cpp +++ b/libs/sensor/SensorManager.cpp @@ -209,7 +209,7 @@ Sensor const* SensorManager::getDefaultSensor(int type) type == SENSOR_TYPE_TILT_DETECTOR || type == SENSOR_TYPE_WAKE_GESTURE || type == SENSOR_TYPE_GLANCE_GESTURE || type == SENSOR_TYPE_PICK_UP_GESTURE || type == SENSOR_TYPE_WRIST_TILT_GESTURE || - type == SENSOR_TYPE_LOW_LATENCY_OFFBODY_DETECT) { + type == SENSOR_TYPE_LOW_LATENCY_OFFBODY_DETECT || type == SENSOR_TYPE_HINGE_ANGLE) { wakeUpSensor = true; } // For now we just return the first sensor of that type we find. diff --git a/services/sensorservice/SensorServiceUtils.cpp b/services/sensorservice/SensorServiceUtils.cpp index 34cd8ddd7b..fdd56b364d 100644 --- a/services/sensorservice/SensorServiceUtils.cpp +++ b/services/sensorservice/SensorServiceUtils.cpp @@ -55,6 +55,7 @@ size_t eventSizeBySensorType(int type) { case SENSOR_TYPE_MOTION_DETECT: case SENSOR_TYPE_HEART_BEAT: case SENSOR_TYPE_LOW_LATENCY_OFFBODY_DETECT: + case SENSOR_TYPE_HINGE_ANGLE: return 1; default: -- cgit v1.2.3-59-g8ed1b From 6d88a487b0fe7f72314f81e81c9786005032afa3 Mon Sep 17 00:00:00 2001 From: Steven Thomas Date: Mon, 2 Dec 2019 22:00:47 -0800 Subject: Add setFrameRate() api setFrameRate() is a new api in Android 11 that will enable apps to specify their intended frame rate. Bug: 143912624 Bug: 137287430 Test: Added a new CTS test - android.graphics.cts.SetFrameRateTest. Change-Id: I0150055fbffd37f2d644829e9dadbfc517045d8e --- include/android/surface_control.h | 27 +++++++++++++++++++ libs/nativewindow/ANativeWindow.cpp | 7 +++++ libs/nativewindow/include/android/native_window.h | 33 +++++++++++++++++++++++ libs/nativewindow/libnativewindow.map.txt | 1 + 4 files changed, 68 insertions(+) (limited to 'include') diff --git a/include/android/surface_control.h b/include/android/surface_control.h index 31abb6622e..157b4242fe 100644 --- a/include/android/surface_control.h +++ b/include/android/surface_control.h @@ -407,6 +407,33 @@ void ASurfaceTransaction_setHdrMetadata_cta861_3(ASurfaceTransaction* transactio #endif // __ANDROID_API__ >= 29 +#if __ANDROID_API__ >= 30 + +/* + * Sets the intended frame rate for |surface_control|. + * + * On devices that are capable of running the display at different refresh rates, the system may + * choose a display refresh rate to better match this surface's frame rate. Usage of this API won't + * directly affect the application's frame production pipeline. However, because the system may + * change the display refresh rate, calls to this function may result in changes to Choreographer + * callback timings, and changes to the time interval at which the system releases buffers back to + * the application. + * + * |frameRate| is the intended frame rate of this surface. 0 is a special value that indicates the + * app will accept the system's choice for the display frame rate, which is the default behavior if + * this function isn't called. The frameRate param does *not* need to be a valid refresh rate for + * this device's display - e.g., it's fine to pass 30fps to a device that can only run the display + * at 60fps. + * + * Available since API level 30. + */ +void ASurfaceTransaction_setFrameRate(ASurfaceTransaction* transaction, + ASurfaceControl* surface_control, + float frameRate) + __INTRODUCED_IN(30); + +#endif // __ANDROID_API__ >= 30 + __END_DECLS #endif // ANDROID_SURFACE_CONTROL_H diff --git a/libs/nativewindow/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp index 842af18c8c..a60bc4dad3 100644 --- a/libs/nativewindow/ANativeWindow.cpp +++ b/libs/nativewindow/ANativeWindow.cpp @@ -158,6 +158,13 @@ int32_t ANativeWindow_getBuffersDataSpace(ANativeWindow* window) { return query(window, NATIVE_WINDOW_DATASPACE); } +int32_t ANativeWindow_setFrameRate(ANativeWindow* window, float frameRate) { + if (!window || !query(window, NATIVE_WINDOW_IS_VALID) || frameRate < 0) { + return -EINVAL; + } + return native_window_set_frame_rate(window, frameRate); +} + /************************************************************************************************** * vndk-stable **************************************************************************************************/ diff --git a/libs/nativewindow/include/android/native_window.h b/libs/nativewindow/include/android/native_window.h index 3e436e3b07..0637db390d 100644 --- a/libs/nativewindow/include/android/native_window.h +++ b/libs/nativewindow/include/android/native_window.h @@ -230,6 +230,39 @@ int32_t ANativeWindow_getBuffersDataSpace(ANativeWindow* window) __INTRODUCED_IN #endif // __ANDROID_API__ >= 28 +#if __ANDROID_API__ >= 30 + +/** + * Sets the intended frame rate for this window. + * + * On devices that are capable of running the display at different refresh + * rates, the system may choose a display refresh rate to better match this + * window's frame rate. Usage of this API won't introduce frame rate throttling, + * or affect other aspects of the application's frame production + * pipeline. However, because the system may change the display refresh rate, + * calls to this function may result in changes to Choreographer callback + * timings, and changes to the time interval at which the system releases + * buffers back to the application. + * + * Note that this only has an effect for windows presented on the display. If + * this ANativeWindow is consumed by something other than the system compositor, + * e.g. a media codec, this call has no effect. + * + * Available since API level 30. + * + * \param frameRate The intended frame rate of this window. 0 is a special value + * that indicates the app will accept the system's choice for the display frame + * rate, which is the default behavior if this function isn't called. The + * frameRate param does *not* need to be a valid refresh rate for this device's + * display - e.g., it's fine to pass 30fps to a device that can only run the + * display at 60fps. + * + * \return 0 for success, -EINVAL if the window or frame rate are invalid. + */ +int32_t ANativeWindow_setFrameRate(ANativeWindow* window, float frameRate) __INTRODUCED_IN(30); + +#endif // __ANDROID_API__ >= 30 + #ifdef __cplusplus }; #endif diff --git a/libs/nativewindow/libnativewindow.map.txt b/libs/nativewindow/libnativewindow.map.txt index f59e8f0546..3002da29e7 100644 --- a/libs/nativewindow/libnativewindow.map.txt +++ b/libs/nativewindow/libnativewindow.map.txt @@ -43,6 +43,7 @@ LIBNATIVEWINDOW { ANativeWindow_setDequeueTimeout; # apex # introduced=30 ANativeWindow_setSharedBufferMode; # llndk ANativeWindow_setSwapInterval; # llndk + ANativeWindow_setFrameRate; # introduced=30 ANativeWindow_setUsage; # llndk ANativeWindow_unlockAndPost; local: -- cgit v1.2.3-59-g8ed1b From 59262f93650e4fa7205c64680045faa52f4da5fa Mon Sep 17 00:00:00 2001 From: Leon Scroggins III Date: Wed, 22 Jan 2020 17:29:06 -0500 Subject: Update decodeImage to account for alignment Bug: 147749998 Test: I902de3410c45a21cf27b48a02cdc5d514b7ada60 Document that the stride must be pixel-aligned. Change-Id: Ifbd667298f542f249092ae8eca744b4ed65afaee --- include/android/imagedecoder.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/android/imagedecoder.h b/include/android/imagedecoder.h index 469b088c89..c9a041e2ef 100644 --- a/include/android/imagedecoder.h +++ b/include/android/imagedecoder.h @@ -337,7 +337,8 @@ size_t AImageDecoder_getMinimumStride(AImageDecoder*) __INTRODUCED_IN(30); * {@link AImageDecoder_getMinimumStride}. * @param size Size of the pixel buffer in bytes. Must be at least * stride * (height - 1) + - * {@link AImageDecoder_getMinimumStride}. + * {@link AImageDecoder_getMinimumStride}. Must also be a multiple + * of the bytes per pixel of the {@link AndroidBitmapFormat}. * @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success, or an error code * from the same enum describing the failure. */ -- cgit v1.2.3-59-g8ed1b From 5d0445cc3e16af2e49ae87580ecc420be54b4608 Mon Sep 17 00:00:00 2001 From: Leon Scroggins III Date: Thu, 23 Jan 2020 09:43:43 -0500 Subject: Clean up imagedecoder header file Bug: 135133301 Test: Ibf7c0e563feeb08ce6dbabb5e86ddb385c9dff54 Remove AImageDecoderHeaderInfo_isAnimated. We are punting animation support to S, so there is no reason to report whether the image is animated. Use int32_t for width and height. We already return these for AImageDecoderHeaderInfo_getWidth/getHeight, so use the same type for AImageDecoder_setTargetSize/computeSampledSize. Use int32_t for AImageDecoderHeaderInfo_getAndroidBitmapFormat. This matches the convention for what to return when the value is logically an enum. Change-Id: I93df851dd9fee2eb8d097e2158fb95003a0474db --- include/android/imagedecoder.h | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/include/android/imagedecoder.h b/include/android/imagedecoder.h index 469b088c89..31efa65cbe 100644 --- a/include/android/imagedecoder.h +++ b/include/android/imagedecoder.h @@ -133,7 +133,7 @@ void AImageDecoder_delete(AImageDecoder* decoder) __INTRODUCED_IN(30); /** * Choose the desired output format. * - * @param format AndroidBitmapFormat to use + * @param format {@link AndroidBitmapFormat} to use for the output. * @return {@link ANDROID_IMAGE_DECODER_SUCCESS} if the format is compatible * with the image and {@link ANDROID_IMAGE_DECODER_INVALID_CONVERSION} * otherwise. In the latter case, the AImageDecoder uses the @@ -196,7 +196,7 @@ int AImageDecoder_setDataSpace(AImageDecoder*, int32_t dataspace) __INTRODUCED_I * pointer is null, width or height is <= 0, or any existing crop is * not contained by the image dimensions. */ -int AImageDecoder_setTargetSize(AImageDecoder*, int width, int height) __INTRODUCED_IN(30); +int AImageDecoder_setTargetSize(AImageDecoder*, int32_t width, int32_t height) __INTRODUCED_IN(30); /** @@ -219,7 +219,7 @@ int AImageDecoder_setTargetSize(AImageDecoder*, int width, int height) __INTRODU * @return ANDROID_IMAGE_DECODER result code. */ int AImageDecoder_computeSampledSize(const AImageDecoder*, int sampleSize, - int* width, int* height) __INTRODUCED_IN(30); + int32_t* width, int32_t* height) __INTRODUCED_IN(30); /** * Specify how to crop the output after scaling (if any). * @@ -276,18 +276,12 @@ const char* AImageDecoderHeaderInfo_getMimeType( const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30); /** - * Report whether the encoded image represents an animation. - */ -bool AImageDecoderHeaderInfo_isAnimated( - const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30); - -/** - * Report the AndroidBitmapFormat the AImageDecoder will decode to + * Report the {@link AndroidBitmapFormat} the AImageDecoder will decode to * by default. AImageDecoder will try to choose one that is sensible * for the image and the system. Note that this does not indicate the * encoded format of the image. */ -AndroidBitmapFormat AImageDecoderHeaderInfo_getAndroidBitmapFormat( +int32_t AImageDecoderHeaderInfo_getAndroidBitmapFormat( const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30); /** -- cgit v1.2.3-59-g8ed1b From 9bb0f07ac583e56123c9c223ee93cca64c1e8493 Mon Sep 17 00:00:00 2001 From: Steven Thomas Date: Thu, 23 Jan 2020 16:37:13 -0800 Subject: Clarify frame rate as frames per second Bug: 137287430 Test: n/a Change-Id: Icf1ad4c5b9551d3690af2329290d5cc30c8016b0 --- include/android/surface_control.h | 10 +++++----- libs/nativewindow/include/android/native_window.h | 12 ++++++------ 2 files changed, 11 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/include/android/surface_control.h b/include/android/surface_control.h index 157b4242fe..eeb8330efd 100644 --- a/include/android/surface_control.h +++ b/include/android/surface_control.h @@ -419,11 +419,11 @@ void ASurfaceTransaction_setHdrMetadata_cta861_3(ASurfaceTransaction* transactio * callback timings, and changes to the time interval at which the system releases buffers back to * the application. * - * |frameRate| is the intended frame rate of this surface. 0 is a special value that indicates the - * app will accept the system's choice for the display frame rate, which is the default behavior if - * this function isn't called. The frameRate param does *not* need to be a valid refresh rate for - * this device's display - e.g., it's fine to pass 30fps to a device that can only run the display - * at 60fps. + * |frameRate| is the intended frame rate of this surface, in frames per second. 0 is a special + * value that indicates the app will accept the system's choice for the display frame rate, which is + * the default behavior if this function isn't called. The frameRate param does *not* need to be a + * valid refresh rate for this device's display - e.g., it's fine to pass 30fps to a device that can + * only run the display at 60fps. * * Available since API level 30. */ diff --git a/libs/nativewindow/include/android/native_window.h b/libs/nativewindow/include/android/native_window.h index 0637db390d..262aee3501 100644 --- a/libs/nativewindow/include/android/native_window.h +++ b/libs/nativewindow/include/android/native_window.h @@ -250,12 +250,12 @@ int32_t ANativeWindow_getBuffersDataSpace(ANativeWindow* window) __INTRODUCED_IN * * Available since API level 30. * - * \param frameRate The intended frame rate of this window. 0 is a special value - * that indicates the app will accept the system's choice for the display frame - * rate, which is the default behavior if this function isn't called. The - * frameRate param does *not* need to be a valid refresh rate for this device's - * display - e.g., it's fine to pass 30fps to a device that can only run the - * display at 60fps. + * \param frameRate The intended frame rate of this window, in frames per + * second. 0 is a special value that indicates the app will accept the system's + * choice for the display frame rate, which is the default behavior if this + * function isn't called. The frameRate param does *not* need to be a valid + * refresh rate for this device's display - e.g., it's fine to pass 30fps to a + * device that can only run the display at 60fps. * * \return 0 for success, -EINVAL if the window or frame rate are invalid. */ -- cgit v1.2.3-59-g8ed1b From 9c858ac6573d32a98d24cc0da0722592873ae3ec Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Thu, 23 Jan 2020 14:20:11 -0600 Subject: Add hmac to InputEvent and scale to MotionEvent We need to pass some additional data to the app to be able to construct the MotionEvent. For proper support of getRawX/getRawY, add the xScale and yScale to MotionEvent. For the verification of input events, add hmac to InputEvent. Bug: 134977432 Bug: 140786233 Test: atest libinput_tests inputflinger_tests Change-Id: Ia3400ebbd9698549aad4d97a3b789ab7e10f6b65 --- include/input/Input.h | 40 ++++---- include/input/InputTransport.h | 31 +++--- libs/input/Input.cpp | 60 +++++++----- libs/input/InputTransport.cpp | 76 +++++++-------- libs/input/KeyCharacterMap.cpp | 6 +- libs/input/tests/InputEvent_test.cpp | 69 ++++++++------ .../input/tests/InputPublisherAndConsumer_test.cpp | 64 ++++++++----- libs/input/tests/StructLayout_test.cpp | 52 +++++----- libs/input/tests/VelocityTracker_test.cpp | 10 +- .../benchmarks/InputDispatcher_benchmarks.cpp | 3 +- .../inputflinger/dispatcher/InputDispatcher.cpp | 26 ++--- .../inputflinger/tests/InputDispatcher_test.cpp | 106 +++++++++++---------- 12 files changed, 300 insertions(+), 243 deletions(-) (limited to 'include') diff --git a/include/input/Input.h b/include/input/Input.h index f8718479f9..1cf58efcaf 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -258,6 +259,11 @@ const char* motionClassificationToString(MotionClassification classification); */ constexpr float AMOTION_EVENT_INVALID_CURSOR_POSITION = std::numeric_limits::quiet_NaN(); +/** + * Invalid value of HMAC - SHA256. Any events with this HMAC value will be marked as not verified. + */ +constexpr std::array INVALID_HMAC = {0}; + /* * Pointer coordinate data. */ @@ -356,14 +362,17 @@ public: inline void setDisplayId(int32_t displayId) { mDisplayId = displayId; } + inline std::array getHmac() const { return mHmac; } protected: - void initialize(int32_t deviceId, int32_t source, int32_t displayId); + void initialize(int32_t deviceId, int32_t source, int32_t displayId, + std::array hmac); void initialize(const InputEvent& from); int32_t mDeviceId; int32_t mSource; int32_t mDisplayId; + std::array mHmac; }; /* @@ -396,18 +405,10 @@ public: static const char* getLabel(int32_t keyCode); static int32_t getKeyCodeFromLabel(const char* label); - void initialize( - int32_t deviceId, - int32_t source, - int32_t displayId, - int32_t action, - int32_t flags, - int32_t keyCode, - int32_t scanCode, - int32_t metaState, - int32_t repeatCount, - nsecs_t downTime, - nsecs_t eventTime); + void initialize(int32_t deviceId, int32_t source, int32_t displayId, + std::array hmac, int32_t action, int32_t flags, int32_t keyCode, + int32_t scanCode, int32_t metaState, int32_t repeatCount, nsecs_t downTime, + nsecs_t eventTime); void initialize(const KeyEvent& from); protected: @@ -463,6 +464,10 @@ public: inline void setActionButton(int32_t button) { mActionButton = button; } + inline float getXScale() const { return mXScale; } + + inline float getYScale() const { return mYScale; } + inline float getXOffset() const { return mXOffset; } inline float getYOffset() const { return mYOffset; } @@ -624,9 +629,10 @@ public: ssize_t findPointerIndex(int32_t pointerId) const; - void initialize(int32_t deviceId, int32_t source, int32_t displayId, int32_t action, - int32_t actionButton, int32_t flags, int32_t edgeFlags, int32_t metaState, - int32_t buttonState, MotionClassification classification, float xOffset, + void initialize(int32_t deviceId, int32_t source, int32_t displayId, + std::array hmac, int32_t action, int32_t actionButton, + int32_t flags, int32_t edgeFlags, int32_t metaState, int32_t buttonState, + MotionClassification classification, float xScale, float yScale, float xOffset, float yOffset, float xPrecision, float yPrecision, float rawXCursorPosition, float rawYCursorPosition, nsecs_t downTime, nsecs_t eventTime, size_t pointerCount, const PointerProperties* pointerProperties, @@ -676,6 +682,8 @@ protected: int32_t mMetaState; int32_t mButtonState; MotionClassification mClassification; + float mXScale; + float mYScale; float mXOffset; float mYOffset; float mXPrecision; diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h index ae47438ac8..06fd3bb364 100644 --- a/include/input/InputTransport.h +++ b/include/input/InputTransport.h @@ -76,6 +76,9 @@ struct InputMessage { } header; // Body *must* be 8 byte aligned. + // For keys and motions, rely on the fact that std::array takes up exactly as much space + // as the underlying data. This is not guaranteed by C++, but it simplifies the conversions. + static_assert(sizeof(std::array) == 32); union Body { struct Key { uint32_t seq; @@ -84,6 +87,7 @@ struct InputMessage { int32_t deviceId; int32_t source; int32_t displayId; + std::array hmac; int32_t action; int32_t flags; int32_t keyCode; @@ -103,6 +107,7 @@ struct InputMessage { int32_t deviceId; int32_t source; int32_t displayId; + std::array hmac; int32_t action; int32_t actionButton; int32_t flags; @@ -112,6 +117,8 @@ struct InputMessage { uint8_t empty2[3]; // 3 bytes to fill gap created by classification int32_t edgeFlags; nsecs_t downTime __attribute__((aligned(8))); + float xScale; + float yScale; float xOffset; float yOffset; float xPrecision; @@ -269,19 +276,10 @@ public: * Returns BAD_VALUE if seq is 0. * Other errors probably indicate that the channel is broken. */ - status_t publishKeyEvent( - uint32_t seq, - int32_t deviceId, - int32_t source, - int32_t displayId, - int32_t action, - int32_t flags, - int32_t keyCode, - int32_t scanCode, - int32_t metaState, - int32_t repeatCount, - nsecs_t downTime, - nsecs_t eventTime); + status_t publishKeyEvent(uint32_t seq, int32_t deviceId, int32_t source, int32_t displayId, + std::array hmac, int32_t action, int32_t flags, + int32_t keyCode, int32_t scanCode, int32_t metaState, + int32_t repeatCount, nsecs_t downTime, nsecs_t eventTime); /* Publishes a motion event to the input channel. * @@ -292,9 +290,10 @@ public: * Other errors probably indicate that the channel is broken. */ status_t publishMotionEvent(uint32_t seq, int32_t deviceId, int32_t source, int32_t displayId, - int32_t action, int32_t actionButton, int32_t flags, - int32_t edgeFlags, int32_t metaState, int32_t buttonState, - MotionClassification classification, float xOffset, float yOffset, + std::array hmac, int32_t action, int32_t actionButton, + int32_t flags, int32_t edgeFlags, int32_t metaState, + int32_t buttonState, MotionClassification classification, + float xScale, float yScale, float xOffset, float yOffset, float xPrecision, float yPrecision, float xCursorPosition, float yCursorPosition, nsecs_t downTime, nsecs_t eventTime, uint32_t pointerCount, const PointerProperties* pointerProperties, diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index 8ccbc7f650..bff1b97769 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -57,16 +57,19 @@ const char* inputEventTypeToString(int32_t type) { return "UNKNOWN"; } -void InputEvent::initialize(int32_t deviceId, int32_t source, int32_t displayId) { +void InputEvent::initialize(int32_t deviceId, int32_t source, int32_t displayId, + std::array hmac) { mDeviceId = deviceId; mSource = source; mDisplayId = displayId; + mHmac = hmac; } void InputEvent::initialize(const InputEvent& from) { mDeviceId = from.mDeviceId; mSource = from.mSource; mDisplayId = from.mDisplayId; + mHmac = from.mHmac; } // --- KeyEvent --- @@ -79,19 +82,11 @@ int32_t KeyEvent::getKeyCodeFromLabel(const char* label) { return getKeyCodeByLabel(label); } -void KeyEvent::initialize( - int32_t deviceId, - int32_t source, - int32_t displayId, - int32_t action, - int32_t flags, - int32_t keyCode, - int32_t scanCode, - int32_t metaState, - int32_t repeatCount, - nsecs_t downTime, - nsecs_t eventTime) { - InputEvent::initialize(deviceId, source, displayId); +void KeyEvent::initialize(int32_t deviceId, int32_t source, int32_t displayId, + std::array hmac, int32_t action, int32_t flags, + int32_t keyCode, int32_t scanCode, int32_t metaState, int32_t repeatCount, + nsecs_t downTime, nsecs_t eventTime) { + InputEvent::initialize(deviceId, source, displayId, hmac); mAction = action; mFlags = flags; mKeyCode = keyCode; @@ -250,15 +245,16 @@ void PointerProperties::copyFrom(const PointerProperties& other) { // --- MotionEvent --- -void MotionEvent::initialize(int32_t deviceId, int32_t source, int32_t displayId, int32_t action, - int32_t actionButton, int32_t flags, int32_t edgeFlags, - int32_t metaState, int32_t buttonState, - MotionClassification classification, float xOffset, float yOffset, - float xPrecision, float yPrecision, float rawXCursorPosition, - float rawYCursorPosition, nsecs_t downTime, nsecs_t eventTime, - size_t pointerCount, const PointerProperties* pointerProperties, +void MotionEvent::initialize(int32_t deviceId, int32_t source, int32_t displayId, + std::array hmac, int32_t action, int32_t actionButton, + int32_t flags, int32_t edgeFlags, int32_t metaState, + int32_t buttonState, MotionClassification classification, float xScale, + float yScale, float xOffset, float yOffset, float xPrecision, + float yPrecision, float rawXCursorPosition, float rawYCursorPosition, + nsecs_t downTime, nsecs_t eventTime, size_t pointerCount, + const PointerProperties* pointerProperties, const PointerCoords* pointerCoords) { - InputEvent::initialize(deviceId, source, displayId); + InputEvent::initialize(deviceId, source, displayId, hmac); mAction = action; mActionButton = actionButton; mFlags = flags; @@ -266,6 +262,8 @@ void MotionEvent::initialize(int32_t deviceId, int32_t source, int32_t displayId mMetaState = metaState; mButtonState = buttonState; mClassification = classification; + mXScale = xScale; + mYScale = yScale; mXOffset = xOffset; mYOffset = yOffset; mXPrecision = xPrecision; @@ -281,7 +279,7 @@ void MotionEvent::initialize(int32_t deviceId, int32_t source, int32_t displayId } void MotionEvent::copyFrom(const MotionEvent* other, bool keepHistory) { - InputEvent::initialize(other->mDeviceId, other->mSource, other->mDisplayId); + InputEvent::initialize(other->mDeviceId, other->mSource, other->mDisplayId, other->mHmac); mAction = other->mAction; mActionButton = other->mActionButton; mFlags = other->mFlags; @@ -289,6 +287,8 @@ void MotionEvent::copyFrom(const MotionEvent* other, bool keepHistory) { mMetaState = other->mMetaState; mButtonState = other->mButtonState; mClassification = other->mClassification; + mXScale = other->mXScale; + mYScale = other->mYScale; mXOffset = other->mXOffset; mYOffset = other->mYOffset; mXPrecision = other->mXPrecision; @@ -490,6 +490,12 @@ status_t MotionEvent::readFromParcel(Parcel* parcel) { mDeviceId = parcel->readInt32(); mSource = parcel->readInt32(); mDisplayId = parcel->readInt32(); + std::vector hmac; + status_t result = parcel->readByteVector(&hmac); + if (result != OK || hmac.size() != 32) { + return BAD_VALUE; + } + std::move(hmac.begin(), hmac.begin() + hmac.size(), mHmac.begin()); mAction = parcel->readInt32(); mActionButton = parcel->readInt32(); mFlags = parcel->readInt32(); @@ -497,6 +503,8 @@ status_t MotionEvent::readFromParcel(Parcel* parcel) { mMetaState = parcel->readInt32(); mButtonState = parcel->readInt32(); mClassification = static_cast(parcel->readByte()); + mXScale = parcel->readFloat(); + mYScale = parcel->readFloat(); mXOffset = parcel->readFloat(); mYOffset = parcel->readFloat(); mXPrecision = parcel->readFloat(); @@ -543,6 +551,8 @@ status_t MotionEvent::writeToParcel(Parcel* parcel) const { parcel->writeInt32(mDeviceId); parcel->writeInt32(mSource); parcel->writeInt32(mDisplayId); + std::vector hmac(mHmac.begin(), mHmac.end()); + parcel->writeByteVector(hmac); parcel->writeInt32(mAction); parcel->writeInt32(mActionButton); parcel->writeInt32(mFlags); @@ -550,6 +560,8 @@ status_t MotionEvent::writeToParcel(Parcel* parcel) const { parcel->writeInt32(mMetaState); parcel->writeInt32(mButtonState); parcel->writeByte(static_cast(mClassification)); + parcel->writeFloat(mXScale); + parcel->writeFloat(mYScale); parcel->writeFloat(mXOffset); parcel->writeFloat(mYOffset); parcel->writeFloat(mXPrecision); @@ -607,7 +619,7 @@ int32_t MotionEvent::getAxisFromLabel(const char* label) { void FocusEvent::initialize(bool hasFocus, bool inTouchMode) { InputEvent::initialize(ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID, AINPUT_SOURCE_UNKNOWN, - ADISPLAY_ID_NONE); + ADISPLAY_ID_NONE, INVALID_HMAC); mHasFocus = hasFocus; mInTouchMode = inTouchMode; } diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp index d53a557b28..d25a5cc0ef 100644 --- a/libs/input/InputTransport.cpp +++ b/libs/input/InputTransport.cpp @@ -147,6 +147,8 @@ void InputMessage::getSanitizedCopy(InputMessage* msg) const { msg->body.key.source = body.key.source; // int32_t displayId msg->body.key.displayId = body.key.displayId; + // std::array hmac + msg->body.key.hmac = body.key.hmac; // int32_t action msg->body.key.action = body.key.action; // int32_t flags @@ -174,6 +176,8 @@ void InputMessage::getSanitizedCopy(InputMessage* msg) const { msg->body.motion.source = body.motion.source; // int32_t displayId msg->body.motion.displayId = body.motion.displayId; + // std::array hmac + msg->body.motion.hmac = body.motion.hmac; // int32_t action msg->body.motion.action = body.motion.action; // int32_t actionButton @@ -190,6 +194,10 @@ void InputMessage::getSanitizedCopy(InputMessage* msg) const { msg->body.motion.edgeFlags = body.motion.edgeFlags; // nsecs_t downTime msg->body.motion.downTime = body.motion.downTime; + // float xScale + msg->body.motion.xScale = body.motion.xScale; + // float yScale + msg->body.motion.yScale = body.motion.yScale; // float xOffset msg->body.motion.xOffset = body.motion.xOffset; // float yOffset @@ -424,19 +432,11 @@ InputPublisher::InputPublisher(const sp& channel) : InputPublisher::~InputPublisher() { } -status_t InputPublisher::publishKeyEvent( - uint32_t seq, - int32_t deviceId, - int32_t source, - int32_t displayId, - int32_t action, - int32_t flags, - int32_t keyCode, - int32_t scanCode, - int32_t metaState, - int32_t repeatCount, - nsecs_t downTime, - nsecs_t eventTime) { +status_t InputPublisher::publishKeyEvent(uint32_t seq, int32_t deviceId, int32_t source, + int32_t displayId, std::array hmac, + int32_t action, int32_t flags, int32_t keyCode, + int32_t scanCode, int32_t metaState, int32_t repeatCount, + nsecs_t downTime, nsecs_t eventTime) { if (ATRACE_ENABLED()) { std::string message = StringPrintf("publishKeyEvent(inputChannel=%s, keyCode=%" PRId32 ")", mChannel->getName().c_str(), keyCode); @@ -461,6 +461,7 @@ status_t InputPublisher::publishKeyEvent( msg.body.key.deviceId = deviceId; msg.body.key.source = source; msg.body.key.displayId = displayId; + msg.body.key.hmac = hmac; msg.body.key.action = action; msg.body.key.flags = flags; msg.body.key.keyCode = keyCode; @@ -473,11 +474,12 @@ status_t InputPublisher::publishKeyEvent( } status_t InputPublisher::publishMotionEvent( - uint32_t seq, int32_t deviceId, int32_t source, int32_t displayId, int32_t action, - int32_t actionButton, int32_t flags, int32_t edgeFlags, int32_t metaState, - int32_t buttonState, MotionClassification classification, float xOffset, float yOffset, - float xPrecision, float yPrecision, float xCursorPosition, float yCursorPosition, - nsecs_t downTime, nsecs_t eventTime, uint32_t pointerCount, + uint32_t seq, int32_t deviceId, int32_t source, int32_t displayId, + std::array hmac, int32_t action, int32_t actionButton, int32_t flags, + int32_t edgeFlags, int32_t metaState, int32_t buttonState, + MotionClassification classification, float xScale, float yScale, float xOffset, + float yOffset, float xPrecision, float yPrecision, float xCursorPosition, + float yCursorPosition, nsecs_t downTime, nsecs_t eventTime, uint32_t pointerCount, const PointerProperties* pointerProperties, const PointerCoords* pointerCoords) { if (ATRACE_ENABLED()) { std::string message = StringPrintf( @@ -489,13 +491,14 @@ status_t InputPublisher::publishMotionEvent( ALOGD("channel '%s' publisher ~ publishMotionEvent: seq=%u, deviceId=%d, source=0x%x, " "displayId=%" PRId32 ", " "action=0x%x, actionButton=0x%08x, flags=0x%x, edgeFlags=0x%x, " - "metaState=0x%x, buttonState=0x%x, classification=%s, xOffset=%f, yOffset=%f, " + "metaState=0x%x, buttonState=0x%x, classification=%s, xScale=%.1f, yScale=%.1f, " + "xOffset=%.1f, yOffset=%.1f, " "xPrecision=%f, yPrecision=%f, downTime=%" PRId64 ", eventTime=%" PRId64 ", " "pointerCount=%" PRIu32, mChannel->getName().c_str(), seq, deviceId, source, displayId, action, actionButton, flags, edgeFlags, metaState, buttonState, - motionClassificationToString(classification), xOffset, yOffset, xPrecision, - yPrecision, downTime, eventTime, pointerCount); + motionClassificationToString(classification), xScale, yScale, xOffset, yOffset, + xPrecision, yPrecision, downTime, eventTime, pointerCount); } if (!seq) { @@ -515,6 +518,7 @@ status_t InputPublisher::publishMotionEvent( msg.body.motion.deviceId = deviceId; msg.body.motion.source = source; msg.body.motion.displayId = displayId; + msg.body.motion.hmac = hmac; msg.body.motion.action = action; msg.body.motion.actionButton = actionButton; msg.body.motion.flags = flags; @@ -522,6 +526,8 @@ status_t InputPublisher::publishMotionEvent( msg.body.motion.metaState = metaState; msg.body.motion.buttonState = buttonState; msg.body.motion.classification = classification; + msg.body.motion.xScale = xScale; + msg.body.motion.yScale = yScale; msg.body.motion.xOffset = xOffset; msg.body.motion.yOffset = yOffset; msg.body.motion.xPrecision = xPrecision; @@ -1136,18 +1142,10 @@ ssize_t InputConsumer::findTouchState(int32_t deviceId, int32_t source) const { } void InputConsumer::initializeKeyEvent(KeyEvent* event, const InputMessage* msg) { - event->initialize( - msg->body.key.deviceId, - msg->body.key.source, - msg->body.key.displayId, - msg->body.key.action, - msg->body.key.flags, - msg->body.key.keyCode, - msg->body.key.scanCode, - msg->body.key.metaState, - msg->body.key.repeatCount, - msg->body.key.downTime, - msg->body.key.eventTime); + event->initialize(msg->body.key.deviceId, msg->body.key.source, msg->body.key.displayId, + msg->body.key.hmac, msg->body.key.action, msg->body.key.flags, + msg->body.key.keyCode, msg->body.key.scanCode, msg->body.key.metaState, + msg->body.key.repeatCount, msg->body.key.downTime, msg->body.key.eventTime); } void InputConsumer::initializeFocusEvent(FocusEvent* event, const InputMessage* msg) { @@ -1164,15 +1162,15 @@ void InputConsumer::initializeMotionEvent(MotionEvent* event, const InputMessage } event->initialize(msg->body.motion.deviceId, msg->body.motion.source, - msg->body.motion.displayId, msg->body.motion.action, + msg->body.motion.displayId, msg->body.motion.hmac, msg->body.motion.action, msg->body.motion.actionButton, msg->body.motion.flags, msg->body.motion.edgeFlags, msg->body.motion.metaState, msg->body.motion.buttonState, msg->body.motion.classification, - msg->body.motion.xOffset, msg->body.motion.yOffset, - msg->body.motion.xPrecision, msg->body.motion.yPrecision, - msg->body.motion.xCursorPosition, msg->body.motion.yCursorPosition, - msg->body.motion.downTime, msg->body.motion.eventTime, pointerCount, - pointerProperties, pointerCoords); + msg->body.motion.xScale, msg->body.motion.yScale, msg->body.motion.xOffset, + msg->body.motion.yOffset, msg->body.motion.xPrecision, + msg->body.motion.yPrecision, msg->body.motion.xCursorPosition, + msg->body.motion.yCursorPosition, msg->body.motion.downTime, + msg->body.motion.eventTime, pointerCount, pointerProperties, pointerCoords); } void InputConsumer::addSample(MotionEvent* event, const InputMessage* msg) { diff --git a/libs/input/KeyCharacterMap.cpp b/libs/input/KeyCharacterMap.cpp index e189d20e28..6f9b162986 100644 --- a/libs/input/KeyCharacterMap.cpp +++ b/libs/input/KeyCharacterMap.cpp @@ -487,9 +487,9 @@ void KeyCharacterMap::addKey(Vector& outEvents, int32_t deviceId, int32_t keyCode, int32_t metaState, bool down, nsecs_t time) { outEvents.push(); KeyEvent& event = outEvents.editTop(); - event.initialize(deviceId, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE, - down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP, - 0, keyCode, 0, metaState, 0, time, time); + event.initialize(deviceId, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE, INVALID_HMAC, + down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP, 0, keyCode, 0, metaState, + 0, time, time); } void KeyCharacterMap::addMetaKeys(Vector& outEvents, diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp index b90857c99c..8c6890f542 100644 --- a/libs/input/tests/InputEvent_test.cpp +++ b/libs/input/tests/InputEvent_test.cpp @@ -26,7 +26,12 @@ namespace android { // Default display id. static constexpr int32_t DISPLAY_ID = ADISPLAY_ID_DEFAULT; -class BaseTest : public testing::Test { }; +class BaseTest : public testing::Test { +protected: + static constexpr std::array HMAC = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; +}; // --- PointerCoordsTest --- @@ -176,16 +181,17 @@ TEST_F(KeyEventTest, Properties) { KeyEvent event; // Initialize and get properties. - const nsecs_t ARBITRARY_DOWN_TIME = 1; - const nsecs_t ARBITRARY_EVENT_TIME = 2; - event.initialize(2, AINPUT_SOURCE_GAMEPAD, DISPLAY_ID, AKEY_EVENT_ACTION_DOWN, - AKEY_EVENT_FLAG_FROM_SYSTEM, AKEYCODE_BUTTON_X, 121, - AMETA_ALT_ON, 1, ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME); + constexpr nsecs_t ARBITRARY_DOWN_TIME = 1; + constexpr nsecs_t ARBITRARY_EVENT_TIME = 2; + event.initialize(2, AINPUT_SOURCE_GAMEPAD, DISPLAY_ID, HMAC, AKEY_EVENT_ACTION_DOWN, + AKEY_EVENT_FLAG_FROM_SYSTEM, AKEYCODE_BUTTON_X, 121, AMETA_ALT_ON, 1, + ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME); ASSERT_EQ(AINPUT_EVENT_TYPE_KEY, event.getType()); ASSERT_EQ(2, event.getDeviceId()); ASSERT_EQ(static_cast(AINPUT_SOURCE_GAMEPAD), event.getSource()); ASSERT_EQ(DISPLAY_ID, event.getDisplayId()); + EXPECT_EQ(HMAC, event.getHmac()); ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, event.getAction()); ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, event.getFlags()); ASSERT_EQ(AKEYCODE_BUTTON_X, event.getKeyCode()); @@ -210,19 +216,17 @@ TEST_F(KeyEventTest, Properties) { class MotionEventTest : public BaseTest { protected: - static const nsecs_t ARBITRARY_DOWN_TIME; - static const nsecs_t ARBITRARY_EVENT_TIME; - static const float X_OFFSET; - static const float Y_OFFSET; + static constexpr nsecs_t ARBITRARY_DOWN_TIME = 1; + static constexpr nsecs_t ARBITRARY_EVENT_TIME = 2; + static constexpr float X_SCALE = 2.0; + static constexpr float Y_SCALE = 3.0; + static constexpr float X_OFFSET = 1; + static constexpr float Y_OFFSET = 1.1; void initializeEventWithHistory(MotionEvent* event); void assertEqualsEventWithHistory(const MotionEvent* event); }; -const nsecs_t MotionEventTest::ARBITRARY_DOWN_TIME = 1; -const nsecs_t MotionEventTest::ARBITRARY_EVENT_TIME = 2; -const float MotionEventTest::X_OFFSET = 1.0f; -const float MotionEventTest::Y_OFFSET = 1.1f; void MotionEventTest::initializeEventWithHistory(MotionEvent* event) { PointerProperties pointerProperties[2]; @@ -254,12 +258,13 @@ void MotionEventTest::initializeEventWithHistory(MotionEvent* event) { pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 26); pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 27); pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 28); - event->initialize(2, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, AMOTION_EVENT_ACTION_MOVE, 0, + event->initialize(2, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, HMAC, AMOTION_EVENT_ACTION_MOVE, 0, AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, AMOTION_EVENT_EDGE_FLAG_TOP, AMETA_ALT_ON, AMOTION_EVENT_BUTTON_PRIMARY, MotionClassification::NONE, - X_OFFSET, Y_OFFSET, 2.0f, 2.1f, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_DOWN_TIME, - ARBITRARY_EVENT_TIME, 2, pointerProperties, pointerCoords); + X_SCALE, Y_SCALE, X_OFFSET, Y_OFFSET, 2.0f, 2.1f, + AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, + ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME, 2, pointerProperties, + pointerCoords); pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 110); pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 111); @@ -308,12 +313,15 @@ void MotionEventTest::assertEqualsEventWithHistory(const MotionEvent* event) { ASSERT_EQ(2, event->getDeviceId()); ASSERT_EQ(static_cast(AINPUT_SOURCE_TOUCHSCREEN), event->getSource()); ASSERT_EQ(DISPLAY_ID, event->getDisplayId()); + EXPECT_EQ(HMAC, event->getHmac()); ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, event->getAction()); ASSERT_EQ(AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, event->getFlags()); ASSERT_EQ(AMOTION_EVENT_EDGE_FLAG_TOP, event->getEdgeFlags()); ASSERT_EQ(AMETA_ALT_ON, event->getMetaState()); ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, event->getButtonState()); ASSERT_EQ(MotionClassification::NONE, event->getClassification()); + EXPECT_EQ(X_SCALE, event->getXScale()); + EXPECT_EQ(Y_SCALE, event->getYScale()); ASSERT_EQ(X_OFFSET, event->getXOffset()); ASSERT_EQ(Y_OFFSET, event->getYOffset()); ASSERT_EQ(2.0f, event->getXPrecision()); @@ -570,12 +578,13 @@ TEST_F(MotionEventTest, Transform) { pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, angle); } MotionEvent event; - event.initialize(0 /*deviceId*/, AINPUT_SOURCE_UNKNOWN, DISPLAY_ID, AMOTION_EVENT_ACTION_MOVE, - 0 /*actionButton*/, 0 /*flags*/, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, - 0 /*buttonState*/, MotionClassification::NONE, 0 /*xOffset*/, 0 /*yOffset*/, - 0 /*xPrecision*/, 0 /*yPrecision*/, 3 + RADIUS /*xCursorPosition*/, - 2 /*yCursorPosition*/, 0 /*downTime*/, 0 /*eventTime*/, pointerCount, - pointerProperties, pointerCoords); + event.initialize(0 /*deviceId*/, AINPUT_SOURCE_UNKNOWN, DISPLAY_ID, INVALID_HMAC, + AMOTION_EVENT_ACTION_MOVE, 0 /*actionButton*/, 0 /*flags*/, + AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/, + MotionClassification::NONE, 1 /*xScale*/, 1 /*yScale*/, 0 /*xOffset*/, + 0 /*yOffset*/, 0 /*xPrecision*/, 0 /*yPrecision*/, + 3 + RADIUS /*xCursorPosition*/, 2 /*yCursorPosition*/, 0 /*downTime*/, + 0 /*eventTime*/, pointerCount, pointerProperties, pointerCoords); float originalRawX = 0 + 3; float originalRawY = -RADIUS + 2; @@ -634,9 +643,10 @@ TEST_F(MotionEventTest, Initialize_SetsClassification) { } for (MotionClassification classification : classifications) { - event.initialize(0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, + event.initialize(0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, - 0, classification, 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + 0, classification, 1 /*xScale*/, 1 /*yScale*/, 0, 0, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, 0 /*downTime*/, 0 /*eventTime*/, pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(classification, event.getClassification()); @@ -654,9 +664,10 @@ TEST_F(MotionEventTest, Initialize_SetsCursorPosition) { pointerCoords[i].clear(); } - event.initialize(0 /*deviceId*/, AINPUT_SOURCE_MOUSE, DISPLAY_ID, AMOTION_EVENT_ACTION_DOWN, 0, - 0, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0, MotionClassification::NONE, 0, - 0, 0, 0, 280 /*xCursorPosition*/, 540 /*yCursorPosition*/, 0 /*downTime*/, + event.initialize(0 /*deviceId*/, AINPUT_SOURCE_MOUSE, DISPLAY_ID, INVALID_HMAC, + AMOTION_EVENT_ACTION_DOWN, 0, 0, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0, + MotionClassification::NONE, 1 /*xScale*/, 1 /*yScale*/, 0, 0, 0, 0, + 280 /*xCursorPosition*/, 540 /*yCursorPosition*/, 0 /*downTime*/, 0 /*eventTime*/, pointerCount, pointerProperties, pointerCoords); event.offsetLocation(20, 60); ASSERT_EQ(280, event.getRawXCursorPosition()); diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp index 2fc77e97a0..1e51ea8b2f 100644 --- a/libs/input/tests/InputPublisherAndConsumer_test.cpp +++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp @@ -75,6 +75,9 @@ void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() { constexpr int32_t deviceId = 1; constexpr int32_t source = AINPUT_SOURCE_KEYBOARD; constexpr int32_t displayId = ADISPLAY_ID_DEFAULT; + constexpr std::array hmac = {31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, + 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, + 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}; constexpr int32_t action = AKEY_EVENT_ACTION_DOWN; constexpr int32_t flags = AKEY_EVENT_FLAG_FROM_SYSTEM; constexpr int32_t keyCode = AKEYCODE_ENTER; @@ -84,8 +87,9 @@ void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() { constexpr nsecs_t downTime = 3; constexpr nsecs_t eventTime = 4; - status = mPublisher->publishKeyEvent(seq, deviceId, source, displayId, action, flags, - keyCode, scanCode, metaState, repeatCount, downTime, eventTime); + status = mPublisher->publishKeyEvent(seq, deviceId, source, displayId, hmac, action, flags, + keyCode, scanCode, metaState, repeatCount, downTime, + eventTime); ASSERT_EQ(OK, status) << "publisher publishKeyEvent should return OK"; @@ -105,6 +109,7 @@ void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() { EXPECT_EQ(deviceId, keyEvent->getDeviceId()); EXPECT_EQ(source, keyEvent->getSource()); EXPECT_EQ(displayId, keyEvent->getDisplayId()); + EXPECT_EQ(hmac, keyEvent->getHmac()); EXPECT_EQ(action, keyEvent->getAction()); EXPECT_EQ(flags, keyEvent->getFlags()); EXPECT_EQ(keyCode, keyEvent->getKeyCode()); @@ -136,6 +141,9 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { constexpr int32_t deviceId = 1; constexpr int32_t source = AINPUT_SOURCE_TOUCHSCREEN; constexpr int32_t displayId = ADISPLAY_ID_DEFAULT; + constexpr std::array hmac = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; constexpr int32_t action = AMOTION_EVENT_ACTION_MOVE; constexpr int32_t actionButton = 0; constexpr int32_t flags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED; @@ -143,6 +151,8 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { constexpr int32_t metaState = AMETA_ALT_LEFT_ON | AMETA_ALT_ON; constexpr int32_t buttonState = AMOTION_EVENT_BUTTON_PRIMARY; constexpr MotionClassification classification = MotionClassification::AMBIGUOUS_GESTURE; + constexpr float xScale = 2; + constexpr float yScale = 3; constexpr float xOffset = -10; constexpr float yOffset = -20; constexpr float xPrecision = 0.25; @@ -171,12 +181,12 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 3.5 * i); } - status = - mPublisher->publishMotionEvent(seq, deviceId, source, displayId, action, actionButton, - flags, edgeFlags, metaState, buttonState, classification, - xOffset, yOffset, xPrecision, yPrecision, - xCursorPosition, yCursorPosition, downTime, eventTime, - pointerCount, pointerProperties, pointerCoords); + status = mPublisher->publishMotionEvent(seq, deviceId, source, displayId, hmac, action, + actionButton, flags, edgeFlags, metaState, buttonState, + classification, xScale, yScale, xOffset, yOffset, + xPrecision, yPrecision, xCursorPosition, + yCursorPosition, downTime, eventTime, pointerCount, + pointerProperties, pointerCoords); ASSERT_EQ(OK, status) << "publisher publishMotionEvent should return OK"; @@ -196,12 +206,17 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { EXPECT_EQ(deviceId, motionEvent->getDeviceId()); EXPECT_EQ(source, motionEvent->getSource()); EXPECT_EQ(displayId, motionEvent->getDisplayId()); + EXPECT_EQ(hmac, motionEvent->getHmac()); EXPECT_EQ(action, motionEvent->getAction()); EXPECT_EQ(flags, motionEvent->getFlags()); EXPECT_EQ(edgeFlags, motionEvent->getEdgeFlags()); EXPECT_EQ(metaState, motionEvent->getMetaState()); EXPECT_EQ(buttonState, motionEvent->getButtonState()); EXPECT_EQ(classification, motionEvent->getClassification()); + EXPECT_EQ(xScale, motionEvent->getXScale()); + EXPECT_EQ(yScale, motionEvent->getYScale()); + EXPECT_EQ(xOffset, motionEvent->getXOffset()); + EXPECT_EQ(yOffset, motionEvent->getYOffset()); EXPECT_EQ(xPrecision, motionEvent->getXPrecision()); EXPECT_EQ(yPrecision, motionEvent->getYPrecision()); EXPECT_EQ(xCursorPosition, motionEvent->getRawXCursorPosition()); @@ -316,11 +331,12 @@ TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenSequenceNumberIsZer pointerCoords[i].clear(); } - status = - mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, MotionClassification::NONE, - 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0, - pointerCount, pointerProperties, pointerCoords); + status = mPublisher->publishMotionEvent(0, 0, 0, 0, INVALID_HMAC, 0, 0, 0, 0, 0, 0, + MotionClassification::NONE, 1 /* xScale */, + 1 /* yScale */, 0, 0, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0, + pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(BAD_VALUE, status) << "publisher publishMotionEvent should return BAD_VALUE"; } @@ -331,11 +347,12 @@ TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenPointerCountLessTha PointerProperties pointerProperties[pointerCount]; PointerCoords pointerCoords[pointerCount]; - status = - mPublisher->publishMotionEvent(1, 0, 0, 0, 0, 0, 0, 0, 0, 0, MotionClassification::NONE, - 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0, - pointerCount, pointerProperties, pointerCoords); + status = mPublisher->publishMotionEvent(1, 0, 0, 0, INVALID_HMAC, 0, 0, 0, 0, 0, 0, + MotionClassification::NONE, 1 /* xScale */, + 1 /* yScale */, 0, 0, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0, + pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(BAD_VALUE, status) << "publisher publishMotionEvent should return BAD_VALUE"; } @@ -351,11 +368,12 @@ TEST_F(InputPublisherAndConsumerTest, pointerCoords[i].clear(); } - status = - mPublisher->publishMotionEvent(1, 0, 0, 0, 0, 0, 0, 0, 0, 0, MotionClassification::NONE, - 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0, - pointerCount, pointerProperties, pointerCoords); + status = mPublisher->publishMotionEvent(1, 0, 0, 0, INVALID_HMAC, 0, 0, 0, 0, 0, 0, + MotionClassification::NONE, 1 /* xScale */, + 1 /* yScale */, 0, 0, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0, + pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(BAD_VALUE, status) << "publisher publishMotionEvent should return BAD_VALUE"; } diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp index 9ab0dba08d..aa8a2d488f 100644 --- a/libs/input/tests/StructLayout_test.cpp +++ b/libs/input/tests/StructLayout_test.cpp @@ -39,35 +39,39 @@ void TestInputMessageAlignment() { CHECK_OFFSET(InputMessage::Body::Key, deviceId, 16); CHECK_OFFSET(InputMessage::Body::Key, source, 20); CHECK_OFFSET(InputMessage::Body::Key, displayId, 24); - CHECK_OFFSET(InputMessage::Body::Key, action, 28); - CHECK_OFFSET(InputMessage::Body::Key, flags, 32); - CHECK_OFFSET(InputMessage::Body::Key, keyCode, 36); - CHECK_OFFSET(InputMessage::Body::Key, scanCode, 40); - CHECK_OFFSET(InputMessage::Body::Key, metaState, 44); - CHECK_OFFSET(InputMessage::Body::Key, repeatCount, 48); - CHECK_OFFSET(InputMessage::Body::Key, downTime, 56); + CHECK_OFFSET(InputMessage::Body::Key, hmac, 28); + CHECK_OFFSET(InputMessage::Body::Key, action, 60); + CHECK_OFFSET(InputMessage::Body::Key, flags, 64); + CHECK_OFFSET(InputMessage::Body::Key, keyCode, 68); + CHECK_OFFSET(InputMessage::Body::Key, scanCode, 72); + CHECK_OFFSET(InputMessage::Body::Key, metaState, 76); + CHECK_OFFSET(InputMessage::Body::Key, repeatCount, 80); + CHECK_OFFSET(InputMessage::Body::Key, downTime, 88); CHECK_OFFSET(InputMessage::Body::Motion, seq, 0); CHECK_OFFSET(InputMessage::Body::Motion, eventTime, 8); CHECK_OFFSET(InputMessage::Body::Motion, deviceId, 16); CHECK_OFFSET(InputMessage::Body::Motion, source, 20); CHECK_OFFSET(InputMessage::Body::Motion, displayId, 24); - CHECK_OFFSET(InputMessage::Body::Motion, action, 28); - CHECK_OFFSET(InputMessage::Body::Motion, actionButton, 32); - CHECK_OFFSET(InputMessage::Body::Motion, flags, 36); - CHECK_OFFSET(InputMessage::Body::Motion, metaState, 40); - CHECK_OFFSET(InputMessage::Body::Motion, buttonState, 44); - CHECK_OFFSET(InputMessage::Body::Motion, classification, 48); - CHECK_OFFSET(InputMessage::Body::Motion, edgeFlags, 52); - CHECK_OFFSET(InputMessage::Body::Motion, downTime, 56); - CHECK_OFFSET(InputMessage::Body::Motion, xOffset, 64); - CHECK_OFFSET(InputMessage::Body::Motion, yOffset, 68); - CHECK_OFFSET(InputMessage::Body::Motion, xPrecision, 72); - CHECK_OFFSET(InputMessage::Body::Motion, yPrecision, 76); - CHECK_OFFSET(InputMessage::Body::Motion, xCursorPosition, 80); - CHECK_OFFSET(InputMessage::Body::Motion, yCursorPosition, 84); - CHECK_OFFSET(InputMessage::Body::Motion, pointerCount, 88); - CHECK_OFFSET(InputMessage::Body::Motion, pointers, 96); + CHECK_OFFSET(InputMessage::Body::Motion, hmac, 28); + CHECK_OFFSET(InputMessage::Body::Motion, action, 60); + CHECK_OFFSET(InputMessage::Body::Motion, actionButton, 64); + CHECK_OFFSET(InputMessage::Body::Motion, flags, 68); + CHECK_OFFSET(InputMessage::Body::Motion, metaState, 72); + CHECK_OFFSET(InputMessage::Body::Motion, buttonState, 76); + CHECK_OFFSET(InputMessage::Body::Motion, classification, 80); + CHECK_OFFSET(InputMessage::Body::Motion, edgeFlags, 84); + CHECK_OFFSET(InputMessage::Body::Motion, downTime, 88); + CHECK_OFFSET(InputMessage::Body::Motion, xScale, 96); + CHECK_OFFSET(InputMessage::Body::Motion, yScale, 100); + CHECK_OFFSET(InputMessage::Body::Motion, xOffset, 104); + CHECK_OFFSET(InputMessage::Body::Motion, yOffset, 108); + CHECK_OFFSET(InputMessage::Body::Motion, xPrecision, 112); + CHECK_OFFSET(InputMessage::Body::Motion, yPrecision, 116); + CHECK_OFFSET(InputMessage::Body::Motion, xCursorPosition, 120); + CHECK_OFFSET(InputMessage::Body::Motion, yCursorPosition, 124); + CHECK_OFFSET(InputMessage::Body::Motion, pointerCount, 128); + CHECK_OFFSET(InputMessage::Body::Motion, pointers, 136); CHECK_OFFSET(InputMessage::Body::Focus, seq, 0); CHECK_OFFSET(InputMessage::Body::Focus, hasFocus, 4); @@ -86,7 +90,7 @@ void TestHeaderSize() { * the Motion type, where "pointerCount" variable affects the size and can change at runtime. */ void TestBodySize() { - static_assert(sizeof(InputMessage::Body::Key) == 64); + static_assert(sizeof(InputMessage::Body::Key) == 96); static_assert(sizeof(InputMessage::Body::Motion) == offsetof(InputMessage::Body::Motion, pointers) + sizeof(InputMessage::Body::Motion::Pointer) * MAX_POINTERS); diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp index 968e2fa6bc..731eb6a000 100644 --- a/libs/input/tests/VelocityTracker_test.cpp +++ b/libs/input/tests/VelocityTracker_test.cpp @@ -176,11 +176,11 @@ static std::vector createMotionEventStream( EXPECT_EQ(pointerIndex, pointerCount); MotionEvent event; - event.initialize(0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, action, - 0 /*actionButton*/, 0 /*flags*/, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, - 0 /*buttonState*/, MotionClassification::NONE, 0 /*xOffset*/, - 0 /*yOffset*/, 0 /*xPrecision*/, 0 /*yPrecision*/, - AMOTION_EVENT_INVALID_CURSOR_POSITION, + event.initialize(0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, INVALID_HMAC, + action, 0 /*actionButton*/, 0 /*flags*/, AMOTION_EVENT_EDGE_FLAG_NONE, + AMETA_NONE, 0 /*buttonState*/, MotionClassification::NONE, 1 /*xScale*/, + 1 /*yScale*/, 0 /*xOffset*/, 0 /*yOffset*/, 0 /*xPrecision*/, + 0 /*yPrecision*/, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, 0 /*downTime*/, entry.eventTime.count(), pointerCount, properties, coords); diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp index 246e735330..9a6ef21724 100644 --- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp +++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp @@ -203,9 +203,10 @@ static MotionEvent generateMotionEvent() { const nsecs_t currentTime = now(); MotionEvent event; - event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, /* actionButton */ 0, /* flags */ 0, /* edgeFlags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE, + 1 /* xScale */, 1 /* yScale */, /* xOffset */ 0, /* yOffset */ 0, /* xPrecision */ 0, /* yPrecision */ 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, currentTime, currentTime, diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 30fdf906fc..d8e91cc6ad 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -2410,7 +2410,7 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, status = connection->inputPublisher .publishKeyEvent(dispatchEntry->seq, keyEntry->deviceId, keyEntry->source, keyEntry->displayId, - dispatchEntry->resolvedAction, + INVALID_HMAC, dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags, keyEntry->keyCode, keyEntry->scanCode, keyEntry->metaState, keyEntry->repeatCount, keyEntry->downTime, @@ -2457,12 +2457,14 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, status = connection->inputPublisher .publishMotionEvent(dispatchEntry->seq, motionEntry->deviceId, motionEntry->source, motionEntry->displayId, - dispatchEntry->resolvedAction, + INVALID_HMAC, dispatchEntry->resolvedAction, motionEntry->actionButton, dispatchEntry->resolvedFlags, motionEntry->edgeFlags, motionEntry->metaState, motionEntry->buttonState, - motionEntry->classification, xOffset, yOffset, + motionEntry->classification, + dispatchEntry->windowXScale, + dispatchEntry->windowYScale, xOffset, yOffset, motionEntry->xPrecision, motionEntry->yPrecision, motionEntry->xCursorPosition, @@ -2930,8 +2932,9 @@ void InputDispatcher::notifyKey(const NotifyKeyArgs* args) { accelerateMetaShortcuts(args->deviceId, args->action, keyCode, metaState); KeyEvent event; - event.initialize(args->deviceId, args->source, args->displayId, args->action, flags, keyCode, - args->scanCode, metaState, repeatCount, args->downTime, args->eventTime); + event.initialize(args->deviceId, args->source, args->displayId, INVALID_HMAC, args->action, + flags, keyCode, args->scanCode, metaState, repeatCount, args->downTime, + args->eventTime); android::base::Timer t; mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags); @@ -3024,9 +3027,10 @@ void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) { mLock.unlock(); MotionEvent event; - event.initialize(args->deviceId, args->source, args->displayId, args->action, - args->actionButton, args->flags, args->edgeFlags, args->metaState, - args->buttonState, args->classification, 0, 0, args->xPrecision, + event.initialize(args->deviceId, args->source, args->displayId, INVALID_HMAC, + args->action, args->actionButton, args->flags, args->edgeFlags, + args->metaState, args->buttonState, args->classification, 1 /*xScale*/, + 1 /*yScale*/, 0 /* xOffset */, 0 /* yOffset */, args->xPrecision, args->yPrecision, args->xCursorPosition, args->yCursorPosition, args->downTime, args->eventTime, args->pointerCount, args->pointerProperties, args->pointerCoords); @@ -3126,7 +3130,7 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, int32_t injec accelerateMetaShortcuts(keyEvent.getDeviceId(), action, /*byref*/ keyCode, /*byref*/ metaState); keyEvent.initialize(keyEvent.getDeviceId(), keyEvent.getSource(), - keyEvent.getDisplayId(), action, flags, keyCode, + keyEvent.getDisplayId(), INVALID_HMAC, action, flags, keyCode, keyEvent.getScanCode(), metaState, keyEvent.getRepeatCount(), keyEvent.getDownTime(), keyEvent.getEventTime()); @@ -4682,8 +4686,8 @@ void InputDispatcher::doPokeUserActivityLockedInterruptible(CommandEntry* comman KeyEvent InputDispatcher::createKeyEvent(const KeyEntry& entry) { KeyEvent event; - event.initialize(entry.deviceId, entry.source, entry.displayId, entry.action, entry.flags, - entry.keyCode, entry.scanCode, entry.metaState, entry.repeatCount, + event.initialize(entry.deviceId, entry.source, entry.displayId, INVALID_HMAC, entry.action, + entry.flags, entry.keyCode, entry.scanCode, entry.metaState, entry.repeatCount, entry.downTime, entry.eventTime); return event; } diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 98ebf50819..094452a0c0 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -225,18 +225,18 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesKeyEvents) { KeyEvent event; // Rejects undefined key actions. - event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE, - /*action*/ -1, 0, - AKEYCODE_A, KEY_A, AMETA_NONE, 0, ARBITRARY_TIME, ARBITRARY_TIME); + event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE, INVALID_HMAC, + /*action*/ -1, 0, AKEYCODE_A, KEY_A, AMETA_NONE, 0, ARBITRARY_TIME, + ARBITRARY_TIME); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) << "Should reject key events with undefined action."; // Rejects ACTION_MULTIPLE since it is not supported despite being defined in the API. - event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE, - AKEY_EVENT_ACTION_MULTIPLE, 0, - AKEYCODE_A, KEY_A, AMETA_NONE, 0, ARBITRARY_TIME, ARBITRARY_TIME); + event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE, INVALID_HMAC, + AKEY_EVENT_ACTION_MULTIPLE, 0, AKEYCODE_A, KEY_A, AMETA_NONE, 0, + ARBITRARY_TIME, ARBITRARY_TIME); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) @@ -260,10 +260,10 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { constexpr MotionClassification classification = MotionClassification::NONE; // Rejects undefined motion actions. - event.initialize(DEVICE_ID, source, DISPLAY_ID, - /*action*/ -1, 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0, - AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, - ARBITRARY_TIME, ARBITRARY_TIME, + event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, + /*action*/ -1, 0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */, + 1 /* yScale */, 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, @@ -271,24 +271,24 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { << "Should reject motion events with undefined action."; // Rejects pointer down with invalid index. - event.initialize(DEVICE_ID, source, DISPLAY_ID, + event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0, - AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, - ARBITRARY_TIME, ARBITRARY_TIME, + 0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, + 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) << "Should reject motion events with pointer down index too large."; - event.initialize(DEVICE_ID, source, DISPLAY_ID, + event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_POINTER_DOWN | (~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0, - AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, - ARBITRARY_TIME, ARBITRARY_TIME, + 0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, + 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, @@ -296,24 +296,24 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { << "Should reject motion events with pointer down index too small."; // Rejects pointer up with invalid index. - event.initialize(DEVICE_ID, source, DISPLAY_ID, + event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0, - AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, - ARBITRARY_TIME, ARBITRARY_TIME, + 0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, + 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) << "Should reject motion events with pointer up index too large."; - event.initialize(DEVICE_ID, source, DISPLAY_ID, + event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_POINTER_UP | (~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0, - AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, - ARBITRARY_TIME, ARBITRARY_TIME, + 0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, + 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, @@ -321,20 +321,20 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { << "Should reject motion events with pointer up index too small."; // Rejects motion events with invalid number of pointers. - event.initialize(DEVICE_ID, source, DISPLAY_ID, AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, - metaState, 0, classification, 0, 0, 0, 0, - AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, - ARBITRARY_TIME, ARBITRARY_TIME, + event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, + edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, 0, 0, + 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 0, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) << "Should reject motion events with 0 pointers."; - event.initialize(DEVICE_ID, source, DISPLAY_ID, AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, - metaState, 0, classification, 0, 0, 0, 0, - AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, - ARBITRARY_TIME, ARBITRARY_TIME, + event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, + edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, 0, 0, + 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ MAX_POINTERS + 1, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, @@ -343,10 +343,10 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { // Rejects motion events with invalid pointer ids. pointerProperties[0].id = -1; - event.initialize(DEVICE_ID, source, DISPLAY_ID, AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, - metaState, 0, classification, 0, 0, 0, 0, - AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, - ARBITRARY_TIME, ARBITRARY_TIME, + event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, + edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, 0, 0, + 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, @@ -354,10 +354,10 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { << "Should reject motion events with pointer ids less than 0."; pointerProperties[0].id = MAX_POINTER_ID + 1; - event.initialize(DEVICE_ID, source, DISPLAY_ID, AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, - metaState, 0, classification, 0, 0, 0, 0, - AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, - ARBITRARY_TIME, ARBITRARY_TIME, + event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, + edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, 0, 0, + 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, @@ -367,10 +367,10 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { // Rejects motion events with duplicate pointer ids. pointerProperties[0].id = 1; pointerProperties[1].id = 1; - event.initialize(DEVICE_ID, source, DISPLAY_ID, AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, - metaState, 0, classification, 0, 0, 0, 0, - AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, - ARBITRARY_TIME, ARBITRARY_TIME, + event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, + edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, 0, 0, + 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 2, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, @@ -645,9 +645,9 @@ static int32_t injectKeyDown(const sp& dispatcher, nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); // Define a valid key down event. - event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, displayId, - AKEY_EVENT_ACTION_DOWN, /* flags */ 0, - AKEYCODE_A, KEY_A, AMETA_NONE, /* repeatCount */ 0, currentTime, currentTime); + event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, displayId, INVALID_HMAC, + AKEY_EVENT_ACTION_DOWN, /* flags */ 0, AKEYCODE_A, KEY_A, AMETA_NONE, + /* repeatCount */ 0, currentTime, currentTime); // Inject event until dispatch out. return dispatcher->injectInputEvent( @@ -674,10 +674,12 @@ static int32_t injectMotionEvent(const sp& dispatcher, int32_t nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); // Define a valid motion down event. - event.initialize(DEVICE_ID, source, displayId, action, /* actionButton */ 0, /* flags */ 0, + event.initialize(DEVICE_ID, source, displayId, INVALID_HMAC, action, /* actionButton */ 0, + /* flags */ 0, /* edgeFlags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE, - /* xOffset */ 0, /* yOffset */ 0, /* xPrecision */ 0, - /* yPrecision */ 0, xCursorPosition, yCursorPosition, currentTime, currentTime, + /* xScale */ 1, /* yScale */ 1, /* xOffset */ 0, /* yOffset */ 0, + /* xPrecision */ 0, /* yPrecision */ 0, xCursorPosition, yCursorPosition, + currentTime, currentTime, /*pointerCount*/ 1, pointerProperties, pointerCoords); // Inject event until dispatch out. -- cgit v1.2.3-59-g8ed1b From a9f397b562df067d27888789954c539f6c0fa624 Mon Sep 17 00:00:00 2001 From: Leon Scroggins III Date: Mon, 27 Jan 2020 12:42:56 -0500 Subject: Update imagedecoder.h to build as a C file Bug: 135133301 Test: build as a C file AAsset is a forward-declared struct, so it needs to be declared in parameters as a struct in order to compile as a C file. In addition, include rect.h explicitly for documentation purposes. This also means that ARect as a parameter does not need to be declared as "struct ARect". Change-Id: I30c5176010ec3b5b0405f0b383654a7cfca65b62 --- include/android/imagedecoder.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/android/imagedecoder.h b/include/android/imagedecoder.h index 469b088c89..cabd626c35 100644 --- a/include/android/imagedecoder.h +++ b/include/android/imagedecoder.h @@ -27,7 +27,7 @@ #define ANDROID_IMAGE_DECODER_H #include "bitmap.h" - +#include #include #ifdef __cplusplus @@ -35,7 +35,6 @@ extern "C" { #endif struct AAsset; -struct ARect; #if __ANDROID_API__ >= 30 @@ -92,7 +91,8 @@ typedef struct AImageDecoder AImageDecoder; * @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success or a value * indicating reason for the failure. */ -int AImageDecoder_createFromAAsset(AAsset* asset, AImageDecoder** outDecoder) __INTRODUCED_IN(30); +int AImageDecoder_createFromAAsset(struct AAsset* asset, AImageDecoder** outDecoder) + __INTRODUCED_IN(30); /** * Create a new AImageDecoder from a file descriptor. -- cgit v1.2.3-59-g8ed1b From 3826d47fa79b505683c20317fa1b8dafb0c8bd39 Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Mon, 27 Jan 2020 10:44:40 -0600 Subject: Use uint32_t for input source The input source type originates from EventHub as an unsigned integer. But later, it somehow becomes a signed value. Even the InputListeners specify the source as uint32_t. To maintain the consistency prior to adding new API that would again use source, convert the source to uint32_t. This also helps remove some static_casts, as well as ensures that the order of input arguments is harder to confuse. Bug: 134977432 Test: atest libinput_tests inputflinger_tests inputflinger_benchmarks Change-Id: Iba10da08ee3a1004d50827785f570dab72a919d1 --- include/input/Input.h | 14 +++++++------- libs/input/Input.cpp | 12 ++++++------ libs/input/tests/InputEvent_test.cpp | 8 ++++---- libs/input/tests/InputPublisherAndConsumer_test.cpp | 4 ++-- 4 files changed, 19 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/include/input/Input.h b/include/input/Input.h index 1cf58efcaf..cf0814cf2f 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -354,9 +354,9 @@ public: inline int32_t getDeviceId() const { return mDeviceId; } - inline int32_t getSource() const { return mSource; } + inline uint32_t getSource() const { return mSource; } - inline void setSource(int32_t source) { mSource = source; } + inline void setSource(uint32_t source) { mSource = source; } inline int32_t getDisplayId() const { return mDisplayId; } @@ -365,12 +365,12 @@ public: inline std::array getHmac() const { return mHmac; } protected: - void initialize(int32_t deviceId, int32_t source, int32_t displayId, + void initialize(int32_t deviceId, uint32_t source, int32_t displayId, std::array hmac); void initialize(const InputEvent& from); int32_t mDeviceId; - int32_t mSource; + uint32_t mSource; int32_t mDisplayId; std::array mHmac; }; @@ -405,7 +405,7 @@ public: static const char* getLabel(int32_t keyCode); static int32_t getKeyCodeFromLabel(const char* label); - void initialize(int32_t deviceId, int32_t source, int32_t displayId, + void initialize(int32_t deviceId, uint32_t source, int32_t displayId, std::array hmac, int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState, int32_t repeatCount, nsecs_t downTime, nsecs_t eventTime); @@ -629,7 +629,7 @@ public: ssize_t findPointerIndex(int32_t pointerId) const; - void initialize(int32_t deviceId, int32_t source, int32_t displayId, + void initialize(int32_t deviceId, uint32_t source, int32_t displayId, std::array hmac, int32_t action, int32_t actionButton, int32_t flags, int32_t edgeFlags, int32_t metaState, int32_t buttonState, MotionClassification classification, float xScale, float yScale, float xOffset, @@ -657,7 +657,7 @@ public: status_t writeToParcel(Parcel* parcel) const; #endif - static bool isTouchEvent(int32_t source, int32_t action); + static bool isTouchEvent(uint32_t source, int32_t action); inline bool isTouchEvent() const { return isTouchEvent(mSource, mAction); } diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index bff1b97769..3a5fc7fe6e 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -57,7 +57,7 @@ const char* inputEventTypeToString(int32_t type) { return "UNKNOWN"; } -void InputEvent::initialize(int32_t deviceId, int32_t source, int32_t displayId, +void InputEvent::initialize(int32_t deviceId, uint32_t source, int32_t displayId, std::array hmac) { mDeviceId = deviceId; mSource = source; @@ -82,7 +82,7 @@ int32_t KeyEvent::getKeyCodeFromLabel(const char* label) { return getKeyCodeByLabel(label); } -void KeyEvent::initialize(int32_t deviceId, int32_t source, int32_t displayId, +void KeyEvent::initialize(int32_t deviceId, uint32_t source, int32_t displayId, std::array hmac, int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState, int32_t repeatCount, nsecs_t downTime, nsecs_t eventTime) { @@ -245,7 +245,7 @@ void PointerProperties::copyFrom(const PointerProperties& other) { // --- MotionEvent --- -void MotionEvent::initialize(int32_t deviceId, int32_t source, int32_t displayId, +void MotionEvent::initialize(int32_t deviceId, uint32_t source, int32_t displayId, std::array hmac, int32_t action, int32_t actionButton, int32_t flags, int32_t edgeFlags, int32_t metaState, int32_t buttonState, MotionClassification classification, float xScale, @@ -488,7 +488,7 @@ status_t MotionEvent::readFromParcel(Parcel* parcel) { } mDeviceId = parcel->readInt32(); - mSource = parcel->readInt32(); + mSource = parcel->readUint32(); mDisplayId = parcel->readInt32(); std::vector hmac; status_t result = parcel->readByteVector(&hmac); @@ -549,7 +549,7 @@ status_t MotionEvent::writeToParcel(Parcel* parcel) const { parcel->writeInt32(sampleCount); parcel->writeInt32(mDeviceId); - parcel->writeInt32(mSource); + parcel->writeUint32(mSource); parcel->writeInt32(mDisplayId); std::vector hmac(mHmac.begin(), mHmac.end()); parcel->writeByteVector(hmac); @@ -590,7 +590,7 @@ status_t MotionEvent::writeToParcel(Parcel* parcel) const { } #endif -bool MotionEvent::isTouchEvent(int32_t source, int32_t action) { +bool MotionEvent::isTouchEvent(uint32_t source, int32_t action) { if (source & AINPUT_SOURCE_CLASS_POINTER) { // Specifically excludes HOVER_MOVE and SCROLL. switch (action & AMOTION_EVENT_ACTION_MASK) { diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp index 8c6890f542..3d7c34e542 100644 --- a/libs/input/tests/InputEvent_test.cpp +++ b/libs/input/tests/InputEvent_test.cpp @@ -189,7 +189,7 @@ TEST_F(KeyEventTest, Properties) { ASSERT_EQ(AINPUT_EVENT_TYPE_KEY, event.getType()); ASSERT_EQ(2, event.getDeviceId()); - ASSERT_EQ(static_cast(AINPUT_SOURCE_GAMEPAD), event.getSource()); + ASSERT_EQ(AINPUT_SOURCE_GAMEPAD, event.getSource()); ASSERT_EQ(DISPLAY_ID, event.getDisplayId()); EXPECT_EQ(HMAC, event.getHmac()); ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, event.getAction()); @@ -203,7 +203,7 @@ TEST_F(KeyEventTest, Properties) { // Set source. event.setSource(AINPUT_SOURCE_JOYSTICK); - ASSERT_EQ(static_cast(AINPUT_SOURCE_JOYSTICK), event.getSource()); + ASSERT_EQ(AINPUT_SOURCE_JOYSTICK, event.getSource()); // Set display id. constexpr int32_t newDisplayId = 2; @@ -311,7 +311,7 @@ void MotionEventTest::assertEqualsEventWithHistory(const MotionEvent* event) { // Check properties. ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, event->getType()); ASSERT_EQ(2, event->getDeviceId()); - ASSERT_EQ(static_cast(AINPUT_SOURCE_TOUCHSCREEN), event->getSource()); + ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, event->getSource()); ASSERT_EQ(DISPLAY_ID, event->getDisplayId()); EXPECT_EQ(HMAC, event->getHmac()); ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, event->getAction()); @@ -448,7 +448,7 @@ TEST_F(MotionEventTest, Properties) { // Set source. event.setSource(AINPUT_SOURCE_JOYSTICK); - ASSERT_EQ(static_cast(AINPUT_SOURCE_JOYSTICK), event.getSource()); + ASSERT_EQ(AINPUT_SOURCE_JOYSTICK, event.getSource()); // Set displayId. constexpr int32_t newDisplayId = 2; diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp index 1e51ea8b2f..b002893c43 100644 --- a/libs/input/tests/InputPublisherAndConsumer_test.cpp +++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp @@ -73,7 +73,7 @@ void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() { constexpr uint32_t seq = 15; constexpr int32_t deviceId = 1; - constexpr int32_t source = AINPUT_SOURCE_KEYBOARD; + constexpr uint32_t source = AINPUT_SOURCE_KEYBOARD; constexpr int32_t displayId = ADISPLAY_ID_DEFAULT; constexpr std::array hmac = {31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, @@ -139,7 +139,7 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { constexpr uint32_t seq = 15; constexpr int32_t deviceId = 1; - constexpr int32_t source = AINPUT_SOURCE_TOUCHSCREEN; + constexpr uint32_t source = AINPUT_SOURCE_TOUCHSCREEN; constexpr int32_t displayId = ADISPLAY_ID_DEFAULT; constexpr std::array hmac = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, -- cgit v1.2.3-59-g8ed1b From 97fea5f41c5da1765889660a881f5d7b655de544 Mon Sep 17 00:00:00 2001 From: Leon Scroggins III Date: Thu, 30 Jan 2020 12:18:20 -0500 Subject: Update imagedecoder documentation Bug: 147839368 Test: No change in behavior Update documentation in response to feedback and after reviewing https://android.devsite.corp.google.com/ndk/reference/group/image-decoder.html#group___image_decoder_1ga8c3a19f8ae30a936a17b3879f849ea60 - Use @defgroup instead of @addtogroup, since this is the only file for this group. - Add a top-level description, including supported formats - Describe the ANDROID_IMAGE_DECODER result enum, and use proper format for the individual values so they will be added to the documentation page. - for createFromAAsset, specify that the AAsset can be closed *after* deleting the AImageDecoder, as we do in createFromFd - reformat some return lines to look more consistent - Remove reference to animated images - Move comment describing AImageDecoderHeaderInfo to before the typedef, so it will be included in the documentation, and expand the comment - More thorough description for getWidth/getHeight - Move comment describing limitation on stride to stride (it had been on the description for size) - Clarify various comments, fix links, etc. Change-Id: I26482ea8373081ee8d47455f7028f2bb6ec6a58e --- include/android/imagedecoder.h | 196 +++++++++++++++++++++++++++-------------- 1 file changed, 129 insertions(+), 67 deletions(-) (limited to 'include') diff --git a/include/android/imagedecoder.h b/include/android/imagedecoder.h index 0d943b7800..6e5da52cb5 100644 --- a/include/android/imagedecoder.h +++ b/include/android/imagedecoder.h @@ -15,12 +15,24 @@ */ /** - * @addtogroup ImageDecoder + * @defgroup ImageDecoder + * + * Functions for converting encoded images into RGBA pixels. + * + * Similar to the Java counterpart android.graphics.ImageDecoder, it can be used + * to decode images like PNG, JPEG, GIF, WEBP and HEIF. It has similar options + * for scaling, cropping, and choosing the output format. Unlike the Java API, + * which can create an android.graphics.Bitmap or + * android.graphics.drawable.Drawable object, AImageDecoder decodes directly + * into memory provided by the client. For more information, see the + * Image decoder + * developer guide. * @{ */ /** - * @file imageDecoder.h + * @file imagedecoder.h + * @brief API for decoding images. */ #ifndef ANDROID_IMAGE_DECODER_H @@ -38,32 +50,54 @@ struct AAsset; #if __ANDROID_API__ >= 30 -/** AImageDecoder functions result code. */ +/** + * {@link AImageDecoder} functions result code. Many functions will return one of these + * to indicate success ({@link ANDROID_IMAGE_DECODER_SUCCESS}) or the reason + * for the failure. On failure, any out-parameters should be considered + * uninitialized, except where specified. + */ enum { - // Decoding was successful and complete. + /** + * Decoding was successful and complete. + */ ANDROID_IMAGE_DECODER_SUCCESS = 0, - // The input was incomplete. In decodeImage, this means a partial - // image was decoded. Undecoded lines are all zeroes. - // In AImageDecoder_create*, no AImageDecoder was created. + /** + * The input was incomplete. + */ ANDROID_IMAGE_DECODER_INCOMPLETE = -1, - // The input contained an error after decoding some lines. Similar to - // INCOMPLETE, above. + /** + * The input contained an error after decoding some lines. + */ ANDROID_IMAGE_DECODER_ERROR = -2, - // Could not convert, e.g. attempting to decode an image with - // alpha to an opaque format. + /** + * Could not convert. For example, attempting to decode an image with + * alpha to an opaque format. + */ ANDROID_IMAGE_DECODER_INVALID_CONVERSION = -3, - // The scale is invalid. It may have overflowed, or it may be incompatible - // with the current alpha setting. + /** + * The scale is invalid. It may have overflowed, or it may be incompatible + * with the current alpha setting. + */ ANDROID_IMAGE_DECODER_INVALID_SCALE = -4, - // Some other parameter was bad (e.g. pixels) + /** + * Some other parameter was bad. + */ ANDROID_IMAGE_DECODER_BAD_PARAMETER = -5, - // Input was invalid i.e. broken before decoding any pixels. + /** + * Input was invalid before decoding any pixels. + */ ANDROID_IMAGE_DECODER_INVALID_INPUT = -6, - // A seek was required, and failed. + /** + * A seek was required and it failed. + */ ANDROID_IMAGE_DECODER_SEEK_ERROR = -7, - // Some other error (e.g. OOM) + /** + * Some other error. For example, an internal allocation failed. + */ ANDROID_IMAGE_DECODER_INTERNAL_ERROR = -8, - // We did not recognize the format + /** + * AImageDecoder did not recognize the format. + */ ANDROID_IMAGE_DECODER_UNSUPPORTED_FORMAT = -9 }; @@ -76,36 +110,45 @@ struct AImageDecoder; * - {@link AImageDecoder_createFromAAsset} * - {@link AImageDecoder_createFromFd} * - {@link AImageDecoder_createFromBuffer} + * + * After creation, {@link AImageDecoder_getHeaderInfo} can be used to retrieve + * information about the encoded image. Other functions, like + * {@link AImageDecoder_setTargetSize}, can be used to specify how to decode, and + * {@link AImageDecoder_decode} will decode into client provided memory. + * + * {@link AImageDecoder} objects are NOT thread-safe, and should not be shared across + * threads. */ typedef struct AImageDecoder AImageDecoder; /** - * Create a new AImageDecoder from an AAsset. + * Create a new {@link AImageDecoder} from an {@link AAsset}. * * @param asset {@link AAsset} containing encoded image data. Client is still - * responsible for calling {@link AAsset_close} on it. + * responsible for calling {@link AAsset_close} on it, which may be + * done after deleting the returned {@link AImageDecoder}. * @param outDecoder On success (i.e. return value is * {@link ANDROID_IMAGE_DECODER_SUCCESS}), this will be set to * a newly created {@link AImageDecoder}. Caller is * responsible for calling {@link AImageDecoder_delete} on it. * @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success or a value - * indicating reason for the failure. + * indicating the reason for the failure. */ int AImageDecoder_createFromAAsset(struct AAsset* asset, AImageDecoder** outDecoder) __INTRODUCED_IN(30); /** - * Create a new AImageDecoder from a file descriptor. + * Create a new {@link AImageDecoder} from a file descriptor. * * @param fd Seekable, readable, open file descriptor for encoded data. * Client is still responsible for closing it, which may be done - * *after* deleting the returned AImageDecoder. + * after deleting the returned {@link AImageDecoder}. * @param outDecoder On success (i.e. return value is * {@link ANDROID_IMAGE_DECODER_SUCCESS}), this will be set to * a newly created {@link AImageDecoder}. Caller is * responsible for calling {@link AImageDecoder_delete} on it. * @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success or a value - * indicating reason for the failure. + * indicating the reason for the failure. */ int AImageDecoder_createFromFd(int fd, AImageDecoder** outDecoder) __INTRODUCED_IN(30); @@ -113,7 +156,7 @@ int AImageDecoder_createFromFd(int fd, AImageDecoder** outDecoder) __INTRODUCED_ * Create a new AImageDecoder from a buffer. * * @param buffer Pointer to encoded data. Must be valid for the entire time - * the AImageDecoder is used. + * the {@link AImageDecoder} is used. * @param length Byte length of buffer. * @param outDecoder On success (i.e. return value is * {@link ANDROID_IMAGE_DECODER_SUCCESS}), this will be set to @@ -136,7 +179,7 @@ void AImageDecoder_delete(AImageDecoder* decoder) __INTRODUCED_IN(30); * @param format {@link AndroidBitmapFormat} to use for the output. * @return {@link ANDROID_IMAGE_DECODER_SUCCESS} if the format is compatible * with the image and {@link ANDROID_IMAGE_DECODER_INVALID_CONVERSION} - * otherwise. In the latter case, the AImageDecoder uses the + * otherwise. In the latter case, the {@link AImageDecoder} uses the * format it was already planning to use (either its default * or a previously successful setting from this function). */ @@ -146,23 +189,25 @@ int AImageDecoder_setAndroidBitmapFormat(AImageDecoder*, /** * Specify whether the output's pixels should be unpremultiplied. * - * By default, the decoder will premultiply the pixels, if they have alpha. Pass - * false to this method to leave them unpremultiplied. This has no effect on an + * By default, {@link AImageDecoder_decodeImage} will premultiply the pixels, if they have alpha. + * Pass true to this method to leave them unpremultiplied. This has no effect on an * opaque image. * - * @param required Pass true to leave the pixels unpremultiplied. - * @return - {@link ANDROID_IMAGE_DECODER_SUCCESS} on success + * @param unpremultipliedRequired Pass true to leave the pixels unpremultiplied. + * @return an enum describing whether the call succeeded. + * - {@link ANDROID_IMAGE_DECODER_SUCCESS} on success * - {@link ANDROID_IMAGE_DECODER_INVALID_CONVERSION} if the conversion * is not possible * - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER} for bad parameters */ -int AImageDecoder_setUnpremultipliedRequired(AImageDecoder*, bool required) __INTRODUCED_IN(30); +int AImageDecoder_setUnpremultipliedRequired(AImageDecoder*, + bool unpremultipliedRequired) __INTRODUCED_IN(30); /** * Choose the dataspace for the output. * - * Not supported for {@link ANDROID_BITMAP_FORMAT_A_8}, which does not support - * an ADataSpace. + * Ignored by {@link ANDROID_BITMAP_FORMAT_A_8}, which does not support + * an {@link ADataSpace}. * * @param dataspace The {@link ADataSpace} to decode into. An ADataSpace * specifies how to interpret the colors. By default, @@ -170,10 +215,10 @@ int AImageDecoder_setUnpremultipliedRequired(AImageDecoder*, bool required) __IN * {@link AImageDecoderHeaderInfo_getDataSpace}. If this * parameter is set to a different ADataSpace, AImageDecoder * will transform the output into the specified ADataSpace. - * @return - {@link ANDROID_IMAGE_DECODER_SUCCESS} on success - * - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER} for a null - * AImageDecoder or an integer that does not correspond to an - * ADataSpace value. + * @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success or + * {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER} for a null + * {@link AImageDecoder} or an integer that does not correspond to an + * {@link ADataSpace} value. */ int AImageDecoder_setDataSpace(AImageDecoder*, int32_t dataspace) __INTRODUCED_IN(30); @@ -191,10 +236,11 @@ int AImageDecoder_setDataSpace(AImageDecoder*, int32_t dataspace) __INTRODUCED_I * {@link AImageDecoder_getMinimumStride}, which will now return * a value based on this width. * @param height Height of the output (prior to cropping). - * @return - {@link ANDROID_IMAGE_DECODER_SUCCESS} on success - * - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER} if the AImageDecoder - * pointer is null, width or height is <= 0, or any existing crop is - * not contained by the image dimensions. + * @return an enum describing whether the call succeeded. + * @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success or + * {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER} if the {@link AImageDecoder} + * pointer is null, width or height is <= 0, or any existing crop is + * not contained by the new image dimensions. */ int AImageDecoder_setTargetSize(AImageDecoder*, int32_t width, int32_t height) __INTRODUCED_IN(30); @@ -213,10 +259,11 @@ int AImageDecoder_setTargetSize(AImageDecoder*, int32_t width, int32_t height) _ * 1/2 of the original dimensions, with 1/4 the number of * pixels. * @param width Out parameter for the width sampled by sampleSize, and rounded - * direction that the decoder can do most efficiently. + * in the direction that the decoder can do most efficiently. * @param height Out parameter for the height sampled by sampleSize, and rounded - * direction that the decoder can do most efficiently. - * @return ANDROID_IMAGE_DECODER result code. + * in the direction that the decoder can do most efficiently. + * @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success or + * {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER} for bad input. */ int AImageDecoder_computeSampledSize(const AImageDecoder*, int sampleSize, int32_t* width, int32_t* height) __INTRODUCED_IN(30); @@ -234,18 +281,21 @@ int AImageDecoder_computeSampledSize(const AImageDecoder*, int sampleSize, * value based on the width of the crop. An empty ARect - * specifically { 0, 0, 0, 0 } - may be used to remove the cropping * behavior. Any other empty or unsorted ARects will result in - * returning ANDROID_IMAGE_DECODER_BAD_PARAMETER. - * @return - {@link ANDROID_IMAGE_DECODER_SUCCESS} on success - * - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER} if the AImageDecoder - * pointer is null or the crop is not contained by the image - * dimensions. + * returning {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER}. + * @return an enum describing whether the call succeeded. + * @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success or + * {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER} if the {@link AImageDecoder} + * pointer is null or the crop is not contained by the image + * dimensions. */ int AImageDecoder_setCrop(AImageDecoder*, ARect crop) __INTRODUCED_IN(30); +struct AImageDecoderHeaderInfo; /** - * Opaque handle for reading header info. + * Opaque handle for representing information about the encoded image. It can + * be passed to methods like {@link AImageDecoderHeaderInfo_getWidth} and + * {@link AImageDecoderHeaderInfo_getHeight}. */ -struct AImageDecoderHeaderInfo; typedef struct AImageDecoderHeaderInfo AImageDecoderHeaderInfo; /** @@ -258,12 +308,16 @@ const AImageDecoderHeaderInfo* AImageDecoder_getHeaderInfo( const AImageDecoder*) __INTRODUCED_IN(30); /** - * Report the native width of the encoded image. + * Report the native width of the encoded image. This is also the logical + * pixel width of the output, unless {@link AImageDecoder_setTargetSize} is + * used to choose a different size. */ int32_t AImageDecoderHeaderInfo_getWidth(const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30); /** - * Report the native height of the encoded image. + * Report the native height of the encoded image. This is also the logical + * pixel height of the output, unless {@link AImageDecoder_setTargetSize} is + * used to choose a different size. */ int32_t AImageDecoderHeaderInfo_getHeight(const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30); @@ -277,7 +331,7 @@ const char* AImageDecoderHeaderInfo_getMimeType( /** * Report the {@link AndroidBitmapFormat} the AImageDecoder will decode to - * by default. AImageDecoder will try to choose one that is sensible + * by default. {@link AImageDecoder} will try to choose one that is sensible * for the image and the system. Note that this does not indicate the * encoded format of the image. */ @@ -285,18 +339,17 @@ int32_t AImageDecoderHeaderInfo_getAndroidBitmapFormat( const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30); /** - * Report how the AImageDecoder will handle alpha by default. If the image + * Report how the {@link AImageDecoder} will handle alpha by default. If the image * contains no alpha (according to its header), this will return * {@link ANDROID_BITMAP_FLAGS_ALPHA_OPAQUE}. If the image may contain alpha, - * this returns {@link ANDROID_BITMAP_FLAGS_ALPHA_PREMUL}. - * - * For animated images only the opacity of the first frame is reported. + * this returns {@link ANDROID_BITMAP_FLAGS_ALPHA_PREMUL}, because + * {@link AImageDecoder_decodeImage} will premultiply pixels by default. */ int AImageDecoderHeaderInfo_getAlphaFlags( const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30); /** - * Report the dataspace the AImageDecoder will decode to by default. + * Report the dataspace the {@link AImageDecoder} will decode to by default. * AImageDecoder will try to choose one that is sensible for the * image and the system. Note that this may not exactly match the ICC * profile (or other color information) stored in the encoded image. @@ -315,26 +368,35 @@ int32_t AImageDecoderHeaderInfo_getDataSpace( const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30); /** - * Return the minimum stride that can be used, taking the specified - * (or default) (possibly scaled) width, crop rect and - * {@link AndroidBitmapFormat} into account. + * Return the minimum stride that can be used in + * {@link AImageDecoder_decodeImage). + * + * This stride provides no padding, meaning it will be exactly equal to the + * width times the number of bytes per pixel for the {@link AndroidBitmapFormat} + * being used. + * + * If the output is scaled (via {@link AImageDecoder_setTargetSize}) and/or + * cropped (via {@link AImageDecoder_setCrop}), this takes those into account. */ size_t AImageDecoder_getMinimumStride(AImageDecoder*) __INTRODUCED_IN(30); /** - * Decode the image into pixels, using the settings of the AImageDecoder. + * Decode the image into pixels, using the settings of the {@link AImageDecoder}. * * @param decoder Opaque object representing the decoder. * @param pixels On success, will be filled with the result - * of the decode. Must be large enough to fit |size| bytes. + * of the decode. Must be large enough to hold |size| bytes. * @param stride Width in bytes of a single row. Must be at least - * {@link AImageDecoder_getMinimumStride}. + * {@link AImageDecoder_getMinimumStride} and a multiple of the + * bytes per pixel of the {@link AndroidBitmapFormat}. * @param size Size of the pixel buffer in bytes. Must be at least * stride * (height - 1) + - * {@link AImageDecoder_getMinimumStride}. Must also be a multiple - * of the bytes per pixel of the {@link AndroidBitmapFormat}. + * {@link AImageDecoder_getMinimumStride}. * @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success, or an error code * from the same enum describing the failure. + * {@link ANDROID_IMAGE_DECODER_INCOMPLETE} or + * {@link ANDROID_IMAGE_DECODER_ERROR} means that a partial image was + * decoded, and undecoded lines have been initialized to all zeroes. */ int AImageDecoder_decodeImage(AImageDecoder* decoder, void* pixels, size_t stride, -- cgit v1.2.3-59-g8ed1b From 8cee65ec1d67ee8ea0a40099a26e1f94c7b06feb Mon Sep 17 00:00:00 2001 From: Leon Scroggins III Date: Thu, 30 Jan 2020 14:20:42 -0500 Subject: AImageDecoder: update comments around dataspaces Bug: 135133301 Test: I5e8bdcdae6837db23c0f4ef08f931f3bebe0ce0d A companion change (I489f31fef79dec11e97c8e8fb9207adb77a3d0c7) makes AImageDecoder default to doing no color conversion. Update comments to reflect that. Change-Id: Icc3b0f101598ce98c458ff43597ee05b3662664c --- include/android/imagedecoder.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/android/imagedecoder.h b/include/android/imagedecoder.h index 6e5da52cb5..7437ab1093 100644 --- a/include/android/imagedecoder.h +++ b/include/android/imagedecoder.h @@ -349,20 +349,20 @@ int AImageDecoderHeaderInfo_getAlphaFlags( const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30); /** - * Report the dataspace the {@link AImageDecoder} will decode to by default. - * AImageDecoder will try to choose one that is sensible for the - * image and the system. Note that this may not exactly match the ICC - * profile (or other color information) stored in the encoded image. + * Report the dataspace the AImageDecoder will decode to by default. * - * @return The {@link ADataSpace} most closely representing the way the colors - * are encoded (or {@link ADATASPACE_UNKNOWN} if there is not an - * approximate ADataSpace). This specifies how to interpret the colors + * By default, {@link AImageDecoder_decodeImage} will not do any color + * conversion. + * + * @return The {@link ADataSpace} representing the way the colors + * are encoded (or {@link ADATASPACE_UNKNOWN} if there is not a + * corresponding ADataSpace). This specifies how to interpret the colors * in the decoded image, unless {@link AImageDecoder_setDataSpace} is * called to decode to a different ADataSpace. * * Note that ADataSpace only exposes a few values. This may return - * ADATASPACE_UNKNOWN, even for Named ColorSpaces, if they have no - * corresponding ADataSpace. + * {@link ADATASPACE_UNKNOWN}, even for Named ColorSpaces, if they have + * no corresponding {@link ADataSpace}. */ int32_t AImageDecoderHeaderInfo_getDataSpace( const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30); -- cgit v1.2.3-59-g8ed1b From 9cb2ffdb01895391be745e45b8b7078677099d35 Mon Sep 17 00:00:00 2001 From: Leon Scroggins III Date: Thu, 6 Feb 2020 11:16:54 -0500 Subject: Specify when AndroidBitmapInfo.flags were introduced Bug: 148954909 Test: Generate docs Change-Id: Ie9d65ed4ab108a9f756ee4e5ae431696be6500cc --- include/android/bitmap.h | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/android/bitmap.h b/include/android/bitmap.h index 571a5ca1ef..412fc6b53f 100644 --- a/include/android/bitmap.h +++ b/include/android/bitmap.h @@ -97,15 +97,21 @@ typedef struct { uint32_t stride; /** The bitmap pixel format. See {@link AndroidBitmapFormat} */ int32_t format; - /** Two bits are used to encode alpha. Use ANDROID_BITMAP_FLAGS_ALPHA_MASK - * and ANDROID_BITMAP_FLAGS_ALPHA_SHIFT to retrieve them. One bit is used - * to encode whether the Bitmap uses the HARDWARE Config. Use - * ANDROID_BITMAP_FLAGS_IS_HARDWARE to know.*/ + /** Bitfield containing information about the bitmap. + * + *

Two bits are used to encode alpha. Use {@link ANDROID_BITMAP_FLAGS_ALPHA_MASK} + * and {@link ANDROID_BITMAP_FLAGS_ALPHA_SHIFT} to retrieve them.

+ * + *

One bit is used to encode whether the Bitmap uses the HARDWARE Config. Use + * {@link ANDROID_BITMAP_FLAGS_IS_HARDWARE} to know.

+ * + *

These flags were introduced in API level 30.

+ */ uint32_t flags; } AndroidBitmapInfo; /** - * Given a java bitmap object, fill out the AndroidBitmapInfo struct for it. + * Given a java bitmap object, fill out the {@link AndroidBitmapInfo} struct for it. * If the call fails, the info parameter will be ignored. */ int AndroidBitmap_getInfo(JNIEnv* env, jobject jbitmap, -- cgit v1.2.3-59-g8ed1b From bb5ffd2074fe150725eee7eb1f97d5814a2df249 Mon Sep 17 00:00:00 2001 From: Leon Scroggins III Date: Thu, 6 Feb 2020 11:45:16 -0500 Subject: AImageDecoder: respond to API review Bug: 148954890 Test: Generate docs List the formats supported. List the possible error codes that can be returned from each method and what it means. Change-Id: I192888c1d438db0308cc715ab7f4997543509e73 --- include/android/imagedecoder.h | 169 +++++++++++++++++++++++++++++++---------- 1 file changed, 128 insertions(+), 41 deletions(-) (limited to 'include') diff --git a/include/android/imagedecoder.h b/include/android/imagedecoder.h index 7437ab1093..3a87da0fee 100644 --- a/include/android/imagedecoder.h +++ b/include/android/imagedecoder.h @@ -20,9 +20,18 @@ * Functions for converting encoded images into RGBA pixels. * * Similar to the Java counterpart android.graphics.ImageDecoder, it can be used - * to decode images like PNG, JPEG, GIF, WEBP and HEIF. It has similar options - * for scaling, cropping, and choosing the output format. Unlike the Java API, - * which can create an android.graphics.Bitmap or + * to decode images in the following formats: + * - JPEG + * - PNG + * - GIF + * - WebP + * - BMP + * - ICO + * - WBMP + * - HEIF + * - Digital negatives (via the DNG SDK) + *

It has similar options for scaling, cropping, and choosing the output format. + * Unlike the Java API, which can create an android.graphics.Bitmap or * android.graphics.drawable.Drawable object, AImageDecoder decodes directly * into memory provided by the client. For more information, see the * Image decoder @@ -62,7 +71,7 @@ enum { */ ANDROID_IMAGE_DECODER_SUCCESS = 0, /** - * The input was incomplete. + * The input is incomplete. */ ANDROID_IMAGE_DECODER_INCOMPLETE = -1, /** @@ -80,7 +89,7 @@ enum { */ ANDROID_IMAGE_DECODER_INVALID_SCALE = -4, /** - * Some other parameter was bad. + * Some other parameter is invalid. */ ANDROID_IMAGE_DECODER_BAD_PARAMETER = -5, /** @@ -133,6 +142,19 @@ typedef struct AImageDecoder AImageDecoder; * responsible for calling {@link AImageDecoder_delete} on it. * @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success or a value * indicating the reason for the failure. + * + * Errors: + * - {@link ANDROID_IMAGE_DECODER_INCOMPLETE}: The asset was truncated before + * reading the image header. + * - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER}: One of the parameters is + * null. + * - {@link ANDROID_IMAGE_DECODER_INVALID_INPUT}: There is an error in the + * header. + * - {@link ANDROID_IMAGE_DECODER_SEEK_ERROR}: The asset failed to seek. + * - {@link ANDROID_IMAGE_DECODER_INTERNAL_ERROR}: Some other error, like a + * failure to allocate memory. + * - {@link ANDROID_IMAGE_DECODER_UNSUPPORTED_FORMAT}: The format is not + * supported. */ int AImageDecoder_createFromAAsset(struct AAsset* asset, AImageDecoder** outDecoder) __INTRODUCED_IN(30); @@ -149,6 +171,19 @@ int AImageDecoder_createFromAAsset(struct AAsset* asset, AImageDecoder** outDeco * responsible for calling {@link AImageDecoder_delete} on it. * @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success or a value * indicating the reason for the failure. + * + * Errors: + * - {@link ANDROID_IMAGE_DECODER_INCOMPLETE}: The file was truncated before + * reading the image header. + * - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER}: The {@link AImageDecoder} is + * null, or |fd| does not represent a valid, seekable file descriptor. + * - {@link ANDROID_IMAGE_DECODER_INVALID_INPUT}: There is an error in the + * header. + * - {@link ANDROID_IMAGE_DECODER_SEEK_ERROR}: The descriptor failed to seek. + * - {@link ANDROID_IMAGE_DECODER_INTERNAL_ERROR}: Some other error, like a + * failure to allocate memory. + * - {@link ANDROID_IMAGE_DECODER_UNSUPPORTED_FORMAT}: The format is not + * supported. */ int AImageDecoder_createFromFd(int fd, AImageDecoder** outDecoder) __INTRODUCED_IN(30); @@ -163,7 +198,19 @@ int AImageDecoder_createFromFd(int fd, AImageDecoder** outDecoder) __INTRODUCED_ * a newly created {@link AImageDecoder}. Caller is * responsible for calling {@link AImageDecoder_delete} on it. * @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success or a value - * indicating reason for the failure. + * indicating the reason for the failure. + * + * Errors: + * - {@link ANDROID_IMAGE_DECODER_INCOMPLETE}: The encoded image was truncated before + * reading the image header. + * - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER}: One of the parameters is + * invalid. + * - {@link ANDROID_IMAGE_DECODER_INVALID_INPUT}: There is an error in the + * header. + * - {@link ANDROID_IMAGE_DECODER_INTERNAL_ERROR}: Some other error, like a + * failure to allocate memory. + * - {@link ANDROID_IMAGE_DECODER_UNSUPPORTED_FORMAT}: The format is not + * supported. */ int AImageDecoder_createFromBuffer(const void* buffer, size_t length, AImageDecoder** outDecoder) __INTRODUCED_IN(30); @@ -177,11 +224,18 @@ void AImageDecoder_delete(AImageDecoder* decoder) __INTRODUCED_IN(30); * Choose the desired output format. * * @param format {@link AndroidBitmapFormat} to use for the output. - * @return {@link ANDROID_IMAGE_DECODER_SUCCESS} if the format is compatible - * with the image and {@link ANDROID_IMAGE_DECODER_INVALID_CONVERSION} - * otherwise. In the latter case, the {@link AImageDecoder} uses the - * format it was already planning to use (either its default - * or a previously successful setting from this function). + * @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success or a value + * indicating the reason for the failure. On failure, the + * {@link AImageDecoder} uses the format it was already planning + * to use (either its default or a previously successful setting + * from this function). + * + * Errors: + * - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER}: The + * {@link AImageDecoder} is null or |format| does not correspond to an + * {@link AndroidBitmapFormat}. + * - {@link ANDROID_IMAGE_DECODER_INVALID_CONVERSION}: The + * {@link AndroidBitmapFormat} is incompatible with the image. */ int AImageDecoder_setAndroidBitmapFormat(AImageDecoder*, int32_t format) __INTRODUCED_IN(30); @@ -194,11 +248,15 @@ int AImageDecoder_setAndroidBitmapFormat(AImageDecoder*, * opaque image. * * @param unpremultipliedRequired Pass true to leave the pixels unpremultiplied. - * @return an enum describing whether the call succeeded. - * - {@link ANDROID_IMAGE_DECODER_SUCCESS} on success - * - {@link ANDROID_IMAGE_DECODER_INVALID_CONVERSION} if the conversion - * is not possible - * - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER} for bad parameters + * @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success or a value + * indicating the reason for the failure. + * + * Errors: + * - {@link ANDROID_IMAGE_DECODER_INVALID_CONVERSION}: Unpremultiplied is not + * possible due to an existing scale set by + * {@link AImageDecoder_setTargetSize}. + * - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER}: The + * {@link AImageDecoder} is null. */ int AImageDecoder_setUnpremultipliedRequired(AImageDecoder*, bool unpremultipliedRequired) __INTRODUCED_IN(30); @@ -215,10 +273,13 @@ int AImageDecoder_setUnpremultipliedRequired(AImageDecoder*, * {@link AImageDecoderHeaderInfo_getDataSpace}. If this * parameter is set to a different ADataSpace, AImageDecoder * will transform the output into the specified ADataSpace. - * @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success or - * {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER} for a null - * {@link AImageDecoder} or an integer that does not correspond to an - * {@link ADataSpace} value. + * @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success or a value + * indicating the reason for the failure. + * + * Errors: + * - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER}: The + * {@link AImageDecoder} is null or |dataspace| does not correspond to an + * {@link ADataSpace} value. */ int AImageDecoder_setDataSpace(AImageDecoder*, int32_t dataspace) __INTRODUCED_IN(30); @@ -236,11 +297,16 @@ int AImageDecoder_setDataSpace(AImageDecoder*, int32_t dataspace) __INTRODUCED_I * {@link AImageDecoder_getMinimumStride}, which will now return * a value based on this width. * @param height Height of the output (prior to cropping). - * @return an enum describing whether the call succeeded. - * @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success or - * {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER} if the {@link AImageDecoder} - * pointer is null, width or height is <= 0, or any existing crop is - * not contained by the new image dimensions. + * @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success or a value + * indicating the reason for the failure. + * + * Errors: + * - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER}: The + * {@link AImageDecoder} is null. + * - {@link ANDROID_IMAGE_DECODER_INVALID_SCALE}: |width| or |height| is <= 0, + * the size is too big, any existing crop is not contained by the new image dimensions, + * or the scale is incompatible with a previous call to + * {@link AImageDecoder_setUnpremultipliedRequired}(true). */ int AImageDecoder_setTargetSize(AImageDecoder*, int32_t width, int32_t height) __INTRODUCED_IN(30); @@ -262,8 +328,12 @@ int AImageDecoder_setTargetSize(AImageDecoder*, int32_t width, int32_t height) _ * in the direction that the decoder can do most efficiently. * @param height Out parameter for the height sampled by sampleSize, and rounded * in the direction that the decoder can do most efficiently. - * @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success or - * {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER} for bad input. + * @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success or a value + * indicating the reason for the failure. + * + * Errors: + * - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER}: The + * {@link AImageDecoder}, |width| or |height| is null or |sampleSize| is < 1. */ int AImageDecoder_computeSampledSize(const AImageDecoder*, int sampleSize, int32_t* width, int32_t* height) __INTRODUCED_IN(30); @@ -282,18 +352,21 @@ int AImageDecoder_computeSampledSize(const AImageDecoder*, int sampleSize, * specifically { 0, 0, 0, 0 } - may be used to remove the cropping * behavior. Any other empty or unsorted ARects will result in * returning {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER}. - * @return an enum describing whether the call succeeded. - * @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success or - * {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER} if the {@link AImageDecoder} - * pointer is null or the crop is not contained by the image - * dimensions. + * @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success or a value + * indicating the reason for the failure. + * + * Errors: + * - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER}: The + * {@link AImageDecoder} is null or the crop is not contained by the + * (possibly scaled) image dimensions. */ int AImageDecoder_setCrop(AImageDecoder*, ARect crop) __INTRODUCED_IN(30); struct AImageDecoderHeaderInfo; /** - * Opaque handle for representing information about the encoded image. It can - * be passed to methods like {@link AImageDecoderHeaderInfo_getWidth} and + * Opaque handle for representing information about the encoded image. Retrieved + * using {@link AImageDecoder_getHeaderInfo} and passed to methods like + * {@link AImageDecoderHeaderInfo_getWidth} and * {@link AImageDecoderHeaderInfo_getHeight}. */ typedef struct AImageDecoderHeaderInfo AImageDecoderHeaderInfo; @@ -310,14 +383,16 @@ const AImageDecoderHeaderInfo* AImageDecoder_getHeaderInfo( /** * Report the native width of the encoded image. This is also the logical * pixel width of the output, unless {@link AImageDecoder_setTargetSize} is - * used to choose a different size. + * used to choose a different size or {@link AImageDecoder_setCrop} is used to + * set a crop rect. */ int32_t AImageDecoderHeaderInfo_getWidth(const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30); /** * Report the native height of the encoded image. This is also the logical * pixel height of the output, unless {@link AImageDecoder_setTargetSize} is - * used to choose a different size. + * used to choose a different size or {@link AImageDecoder_setCrop} is used to + * set a crop rect. */ int32_t AImageDecoderHeaderInfo_getHeight(const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30); @@ -392,11 +467,23 @@ size_t AImageDecoder_getMinimumStride(AImageDecoder*) __INTRODUCED_IN(30); * @param size Size of the pixel buffer in bytes. Must be at least * stride * (height - 1) + * {@link AImageDecoder_getMinimumStride}. - * @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success, or an error code - * from the same enum describing the failure. - * {@link ANDROID_IMAGE_DECODER_INCOMPLETE} or - * {@link ANDROID_IMAGE_DECODER_ERROR} means that a partial image was - * decoded, and undecoded lines have been initialized to all zeroes. + * @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success or a value + * indicating the reason for the failure. + * + * Errors: + * - {@link ANDROID_IMAGE_DECODER_INCOMPLETE}: The image was truncated. A + * partial image was decoded, and undecoded lines have been initialized to all + * zeroes. + * - {@link ANDROID_IMAGE_DECODER_ERROR}: The image contained an error. A + * partial image was decoded, and undecoded lines have been initialized to all + * zeroes. + * - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER}: The {@link AImageDecoder} or + * |pixels| is null, the stride is not large enough or not pixel aligned, or + * |size| is not large enough. + * - {@link ANDROID_IMAGE_DECODER_SEEK_ERROR}: The asset or file descriptor + * failed to seek. + * - {@link ANDROID_IMAGE_DECODER_INTERNAL_ERROR}: Some other error, like a + * failure to allocate memory. */ int AImageDecoder_decodeImage(AImageDecoder* decoder, void* pixels, size_t stride, -- cgit v1.2.3-59-g8ed1b From 4883c52b940bbfa4f396c8432a25bed5e5e6d51b Mon Sep 17 00:00:00 2001 From: Leon Scroggins III Date: Thu, 30 Jan 2020 15:10:11 -0500 Subject: Fix docs for bitmap.h Bug: 135133301 Test: Generate docs - Fix errors in bitmap documentation. - Add links where relevant. - Move comment in sensor.h so it will apply to the module, and not AHardwareBuffer. Change-Id: Iec00725522e74eccab9346665562081211a870eb --- include/android/bitmap.h | 26 +++++++++++++------------- include/android/sensor.h | 9 +++------ 2 files changed, 16 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/include/android/bitmap.h b/include/android/bitmap.h index 412fc6b53f..727a4af2f9 100644 --- a/include/android/bitmap.h +++ b/include/android/bitmap.h @@ -81,8 +81,8 @@ enum { enum { /** If this bit is set in AndroidBitmapInfo.flags, the Bitmap uses the - * HARDWARE Config, and its AHardwareBuffer can be retrieved via - * AndroidBitmap_getHardwareBuffer. + * HARDWARE Config, and its {@link AHardwareBuffer} can be retrieved via + * {@link AndroidBitmap_getHardwareBuffer}. */ ANDROID_BITMAP_FLAGS_IS_HARDWARE = 1 << 31, }; @@ -120,10 +120,10 @@ int AndroidBitmap_getInfo(JNIEnv* env, jobject jbitmap, #if __ANDROID_API__ >= 30 /** - * Given a java bitmap object, return its ADataSpace. + * Given a java bitmap object, return its {@link ADataSpace}. * - * Note that ADataSpace only exposes a few values. This may return - * ADATASPACE_UNKNOWN, even for Named ColorSpaces, if they have no + * Note that {@link ADataSpace} only exposes a few values. This may return + * {@link ADATASPACE_UNKNOWN}, even for Named ColorSpaces, if they have no * corresponding ADataSpace. */ int32_t AndroidBitmap_getDataSpace(JNIEnv* env, jobject jbitmap) __INTRODUCED_IN(30); @@ -206,17 +206,17 @@ typedef bool (*AndroidBitmap_CompressWriteFunc)(void* userContext, * @param dataspace {@link ADataSpace} describing the color space of the * pixels. * @param pixels Pointer to pixels to compress. - * @param format (@link AndroidBitmapCompressFormat} to compress to. + * @param format {@link AndroidBitmapCompressFormat} to compress to. * @param quality Hint to the compressor, 0-100. The value is interpreted * differently depending on the * {@link AndroidBitmapCompressFormat}. * @param userContext User-defined data which will be passed to the supplied * {@link AndroidBitmap_CompressWriteFunc} each time it is * called. May be null. - * @parm fn Function that writes the compressed data. Will be called each time - * the compressor has compressed more data that is ready to be - * written. May be called more than once for each call to this method. - * May not be null. + * @param fn Function that writes the compressed data. Will be called each time + * the compressor has compressed more data that is ready to be + * written. May be called more than once for each call to this method. + * May not be null. * @return AndroidBitmap functions result code. */ int AndroidBitmap_compress(const AndroidBitmapInfo* info, @@ -235,11 +235,11 @@ struct AHardwareBuffer; * * @param bitmap Handle to an android.graphics.Bitmap. * @param outBuffer On success, is set to a pointer to the - * AHardwareBuffer associated with bitmap. This acquires + * {@link AHardwareBuffer} associated with bitmap. This acquires * a reference on the buffer, and the client must call - * AHardwareBuffer_release when finished with it. + * {@link AHardwareBuffer_release} when finished with it. * @return AndroidBitmap functions result code. - * ANDROID_BITMAP_RESULT_BAD_PARAMETER if bitmap is not a + * {@link ANDROID_BITMAP_RESULT_BAD_PARAMETER} if bitmap is not a * HARDWARE Bitmap. */ int AndroidBitmap_getHardwareBuffer(JNIEnv* env, jobject bitmap, diff --git a/include/android/sensor.h b/include/android/sensor.h index 12c00ad8a2..e63ac4b407 100644 --- a/include/android/sensor.h +++ b/include/android/sensor.h @@ -15,6 +15,9 @@ */ /** + * Structures and functions to receive and process sensor events in + * native code. + * * @addtogroup Sensor * @{ */ @@ -42,12 +45,6 @@ * - DO NOT CHANGE THE LAYOUT OR SIZE OF STRUCTURES */ -/** - * Structures and functions to receive and process sensor events in - * native code. - * - */ - #include #include -- cgit v1.2.3-59-g8ed1b From 52c2cab46d033b6218b2fd031c33acd0755967b4 Mon Sep 17 00:00:00 2001 From: Tim Murray Date: Tue, 10 Dec 2019 17:23:52 -0800 Subject: libui: rewrite Region with FatVector Region is heavily used by SurfaceFlinger and the Android animation framework, but it uses the libutils Vector internally. libutils Vector allocates four elements via malloc() regardless of the contents of the Vector. However, Region is overwhelmingly a single-element Vector (occasionally two), which can be allocated on the stack easily enough. FatVector is an existing wrapper around std::vector that allows for a stack-allocated vector when the contents are small enough. Using FatVector in Region instead of libutils Vector avoids all of the malloc/free overhead, which improves SurfaceFlinger per-frame runtime by ~4%. This CL moves FatVector from libhwui to make it usable by Region. Test: boots, works, passes tests Bug: 149096186 Change-Id: I265c6c831bc82f31afe0d100aec2f98344f2390b --- include/ui/FatVector.h | 1 + libs/ui/Region.cpp | 94 +++++++++++++++++++------------------ libs/ui/include/ui/FatVector.h | 93 ++++++++++++++++++++++++++++++++++++ libs/ui/include/ui/Region.h | 7 ++- libs/ui/include_vndk/ui/FatVector.h | 1 + 5 files changed, 146 insertions(+), 50 deletions(-) create mode 120000 include/ui/FatVector.h create mode 100644 libs/ui/include/ui/FatVector.h create mode 120000 libs/ui/include_vndk/ui/FatVector.h (limited to 'include') diff --git a/include/ui/FatVector.h b/include/ui/FatVector.h new file mode 120000 index 0000000000..c2047c07e1 --- /dev/null +++ b/include/ui/FatVector.h @@ -0,0 +1 @@ +../../libs/ui/include/ui/FatVector.h \ No newline at end of file diff --git a/libs/ui/Region.cpp b/libs/ui/Region.cpp index bf487c4aec..cd2a448c3e 100644 --- a/libs/ui/Region.cpp +++ b/libs/ui/Region.cpp @@ -67,19 +67,20 @@ const Region Region::INVALID_REGION(Rect::INVALID_RECT); // ---------------------------------------------------------------------------- Region::Region() { - mStorage.add(Rect(0,0)); + mStorage.push_back(Rect(0, 0)); } Region::Region(const Region& rhs) - : mStorage(rhs.mStorage) { + mStorage.clear(); + mStorage.insert(mStorage.begin(), rhs.mStorage.begin(), rhs.mStorage.end()); #if defined(VALIDATE_REGIONS) validate(rhs, "rhs copy-ctor"); #endif } Region::Region(const Rect& rhs) { - mStorage.add(rhs); + mStorage.push_back(rhs); } Region::~Region() @@ -100,8 +101,8 @@ Region::~Region() * final, correctly ordered region buffer. Each rectangle will be compared with the span directly * above it, and subdivided to resolve any remaining T-junctions. */ -static void reverseRectsResolvingJunctions(const Rect* begin, const Rect* end, - Vector& dst, int spanDirection) { +static void reverseRectsResolvingJunctions(const Rect* begin, const Rect* end, FatVector& dst, + int spanDirection) { dst.clear(); const Rect* current = end - 1; @@ -109,7 +110,7 @@ static void reverseRectsResolvingJunctions(const Rect* begin, const Rect* end, // add first span immediately do { - dst.add(*current); + dst.push_back(*current); current--; } while (current->top == lastTop && current >= begin); @@ -147,12 +148,12 @@ static void reverseRectsResolvingJunctions(const Rect* begin, const Rect* end, if (prev.right <= left) break; if (prev.right > left && prev.right < right) { - dst.add(Rect(prev.right, top, right, bottom)); + dst.push_back(Rect(prev.right, top, right, bottom)); right = prev.right; } if (prev.left > left && prev.left < right) { - dst.add(Rect(prev.left, top, right, bottom)); + dst.push_back(Rect(prev.left, top, right, bottom)); right = prev.left; } @@ -166,12 +167,12 @@ static void reverseRectsResolvingJunctions(const Rect* begin, const Rect* end, if (prev.left >= right) break; if (prev.left > left && prev.left < right) { - dst.add(Rect(left, top, prev.left, bottom)); + dst.push_back(Rect(left, top, prev.left, bottom)); left = prev.left; } if (prev.right > left && prev.right < right) { - dst.add(Rect(left, top, prev.right, bottom)); + dst.push_back(Rect(left, top, prev.right, bottom)); left = prev.right; } // if an entry in the previous span is too far left, nothing further right in the @@ -183,7 +184,7 @@ static void reverseRectsResolvingJunctions(const Rect* begin, const Rect* end, } if (left < right) { - dst.add(Rect(left, top, right, bottom)); + dst.push_back(Rect(left, top, right, bottom)); } current--; @@ -201,13 +202,14 @@ Region Region::createTJunctionFreeRegion(const Region& r) { if (r.isEmpty()) return r; if (r.isRect()) return r; - Vector reversed; + FatVector reversed; reverseRectsResolvingJunctions(r.begin(), r.end(), reversed, direction_RTL); Region outputRegion; - reverseRectsResolvingJunctions(reversed.begin(), reversed.end(), - outputRegion.mStorage, direction_LTR); - outputRegion.mStorage.add(r.getBounds()); // to make region valid, mStorage must end with bounds + reverseRectsResolvingJunctions(reversed.data(), reversed.data() + reversed.size(), + outputRegion.mStorage, direction_LTR); + outputRegion.mStorage.push_back( + r.getBounds()); // to make region valid, mStorage must end with bounds #if defined(VALIDATE_REGIONS) validate(outputRegion, "T-Junction free region"); @@ -222,7 +224,8 @@ Region& Region::operator = (const Region& rhs) validate(*this, "this->operator="); validate(rhs, "rhs.operator="); #endif - mStorage = rhs.mStorage; + mStorage.clear(); + mStorage.insert(mStorage.begin(), rhs.mStorage.begin(), rhs.mStorage.end()); return *this; } @@ -231,7 +234,7 @@ Region& Region::makeBoundsSelf() if (mStorage.size() >= 2) { const Rect bounds(getBounds()); mStorage.clear(); - mStorage.add(bounds); + mStorage.push_back(bounds); } return *this; } @@ -255,25 +258,25 @@ bool Region::contains(int x, int y) const { void Region::clear() { mStorage.clear(); - mStorage.add(Rect(0,0)); + mStorage.push_back(Rect(0, 0)); } void Region::set(const Rect& r) { mStorage.clear(); - mStorage.add(r); + mStorage.push_back(r); } void Region::set(int32_t w, int32_t h) { mStorage.clear(); - mStorage.add(Rect(w, h)); + mStorage.push_back(Rect(w, h)); } void Region::set(uint32_t w, uint32_t h) { mStorage.clear(); - mStorage.add(Rect(w, h)); + mStorage.push_back(Rect(w, h)); } bool Region::isTriviallyEqual(const Region& region) const { @@ -299,8 +302,7 @@ bool Region::hasSameRects(const Region& other) const { void Region::addRectUnchecked(int l, int t, int r, int b) { Rect rect(l,t,r,b); - size_t where = mStorage.size() - 1; - mStorage.insertAt(rect, where, 1); + mStorage.insert(mStorage.end() - 1, rect); } // ---------------------------------------------------------------------------- @@ -350,7 +352,7 @@ Region& Region::translateSelf(int x, int y) { Region& Region::scaleSelf(float sx, float sy) { size_t count = mStorage.size(); - Rect* rects = mStorage.editArray(); + Rect* rects = mStorage.data(); while (count) { rects->left = static_cast(static_cast(rects->left) * sx + 0.5f); rects->right = static_cast(static_cast(rects->right) * sx + 0.5f); @@ -455,10 +457,10 @@ const Region Region::operation(const Region& rhs, int dx, int dy, uint32_t op) c class Region::rasterizer : public region_operator::region_rasterizer { Rect bounds; - Vector& storage; + FatVector& storage; Rect* head; Rect* tail; - Vector span; + FatVector span; Rect* cur; public: explicit rasterizer(Region& reg) @@ -485,8 +487,8 @@ Region::rasterizer::~rasterizer() flushSpan(); } if (storage.size()) { - bounds.top = storage.itemAt(0).top; - bounds.bottom = storage.top().bottom; + bounds.top = storage.front().top; + bounds.bottom = storage.back().bottom; if (storage.size() == 1) { storage.clear(); } @@ -494,7 +496,7 @@ Region::rasterizer::~rasterizer() bounds.left = 0; bounds.right = 0; } - storage.add(bounds); + storage.push_back(bounds); } void Region::rasterizer::operator()(const Rect& rect) @@ -509,15 +511,15 @@ void Region::rasterizer::operator()(const Rect& rect) return; } } - span.add(rect); - cur = span.editArray() + (span.size() - 1); + span.push_back(rect); + cur = span.data() + (span.size() - 1); } void Region::rasterizer::flushSpan() { bool merge = false; if (tail-head == ssize_t(span.size())) { - Rect const* p = span.editArray(); + Rect const* p = span.data(); Rect const* q = head; if (p->top == q->bottom) { merge = true; @@ -532,17 +534,17 @@ void Region::rasterizer::flushSpan() } } if (merge) { - const int bottom = span[0].bottom; + const int bottom = span.front().bottom; Rect* r = head; while (r != tail) { r->bottom = bottom; r++; } } else { - bounds.left = min(span.itemAt(0).left, bounds.left); - bounds.right = max(span.top().right, bounds.right); - storage.appendVector(span); - tail = storage.editArray() + storage.size(); + bounds.left = min(span.front().left, bounds.left); + bounds.right = max(span.back().right, bounds.right); + storage.insert(storage.end(), span.begin(), span.end()); + tail = storage.data() + storage.size(); head = tail - span.size(); } span.clear(); @@ -550,7 +552,7 @@ void Region::rasterizer::flushSpan() bool Region::validate(const Region& reg, const char* name, bool silent) { - if (reg.mStorage.isEmpty()) { + if (reg.mStorage.empty()) { ALOGE_IF(!silent, "%s: mStorage is empty, which is never valid", name); // return immediately as the code below assumes mStorage is non-empty return false; @@ -689,9 +691,8 @@ void Region::boolean_operation(uint32_t op, Region& dst, } sk_dst.op(sk_lhs, sk_rhs, sk_op); - if (sk_dst.isEmpty() && dst.isEmpty()) - return; - + if (sk_dst.empty() && dst.empty()) return; + bool same = true; Region::const_iterator head = dst.begin(); Region::const_iterator const tail = dst.end(); @@ -786,7 +787,7 @@ void Region::translate(Region& reg, int dx, int dy) validate(reg, "translate (before)"); #endif size_t count = reg.mStorage.size(); - Rect* rects = reg.mStorage.editArray(); + Rect* rects = reg.mStorage.data(); while (count) { rects->offsetBy(dx, dy); rects++; @@ -866,24 +867,25 @@ status_t Region::unflatten(void const* buffer, size_t size) { ALOGE("Region::unflatten() failed, invalid region"); return BAD_VALUE; } - mStorage = result.mStorage; + mStorage.clear(); + mStorage.insert(mStorage.begin(), result.mStorage.begin(), result.mStorage.end()); return NO_ERROR; } // ---------------------------------------------------------------------------- Region::const_iterator Region::begin() const { - return mStorage.array(); + return mStorage.data(); } Region::const_iterator Region::end() const { // Workaround for b/77643177 // mStorage should never be empty, but somehow it is and it's causing // an abort in ubsan - if (mStorage.isEmpty()) return mStorage.array(); + if (mStorage.empty()) return mStorage.data(); size_t numRects = isRect() ? 1 : mStorage.size() - 1; - return mStorage.array() + numRects; + return mStorage.data() + numRects; } Rect const* Region::getArray(size_t* count) const { diff --git a/libs/ui/include/ui/FatVector.h b/libs/ui/include/ui/FatVector.h new file mode 100644 index 0000000000..25fe3a0a2a --- /dev/null +++ b/libs/ui/include/ui/FatVector.h @@ -0,0 +1,93 @@ +/* + * Copyright 2019, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_REGION_FAT_VECTOR_H +#define ANDROID_REGION_FAT_VECTOR_H + +#include +#include +#include +#include + +#include + +namespace android { + +template +class InlineStdAllocator { +public: + struct Allocation { + private: + Allocation(const Allocation&) = delete; + void operator=(const Allocation&) = delete; + + public: + Allocation() {} + // char array instead of T array, so memory is uninitialized, with no destructors run + char array[sizeof(T) * SIZE]; + bool inUse = false; + }; + + typedef T value_type; // needed to implement std::allocator + typedef T* pointer; // needed to implement std::allocator + + explicit InlineStdAllocator(Allocation& allocation) : mAllocation(allocation) {} + InlineStdAllocator(const InlineStdAllocator& other) : mAllocation(other.mAllocation) {} + ~InlineStdAllocator() {} + + T* allocate(size_t num, const void* = 0) { + if (!mAllocation.inUse && num <= SIZE) { + mAllocation.inUse = true; + return static_cast(static_cast(mAllocation.array)); + } else { + return static_cast(static_cast(malloc(num * sizeof(T)))); + } + } + + void deallocate(pointer p, size_t) { + if (p == static_cast(static_cast(mAllocation.array))) { + mAllocation.inUse = false; + } else { + // 'free' instead of delete here - destruction handled separately + free(p); + } + } + Allocation& mAllocation; +}; + +/** + * std::vector with SIZE elements preallocated into an internal buffer. + * + * Useful for avoiding the cost of malloc in cases where only SIZE or + * fewer elements are needed in the common case. + */ +template +class FatVector : public std::vector> { +public: + FatVector() + : std::vector>(InlineStdAllocator(mAllocation)) { + this->reserve(SIZE); + } + + explicit FatVector(size_t capacity) : FatVector() { this->resize(capacity); } + +private: + typename InlineStdAllocator::Allocation mAllocation; +}; + +} // namespace android + +#endif // ANDROID_REGION_FAT_VECTOR_H diff --git a/libs/ui/include/ui/Region.h b/libs/ui/include/ui/Region.h index 2db3b10f13..6bb7b8d21c 100644 --- a/libs/ui/include/ui/Region.h +++ b/libs/ui/include/ui/Region.h @@ -21,13 +21,13 @@ #include #include -#include - #include #include #include +#include "FatVector.h" + #include namespace android { @@ -180,7 +180,7 @@ private: // with an extra Rect as the last element which is set to the // bounds of the region. However, if the region is // a simple Rect then mStorage contains only that rect. - Vector mStorage; + FatVector mStorage; }; @@ -235,4 +235,3 @@ static inline void PrintTo(const Region& region, ::std::ostream* os) { }; // namespace android #endif // ANDROID_UI_REGION_H - diff --git a/libs/ui/include_vndk/ui/FatVector.h b/libs/ui/include_vndk/ui/FatVector.h new file mode 120000 index 0000000000..bf30166784 --- /dev/null +++ b/libs/ui/include_vndk/ui/FatVector.h @@ -0,0 +1 @@ +../../include/ui/FatVector.h \ No newline at end of file -- cgit v1.2.3-59-g8ed1b From 54d3e189009e4138ec5074b0ac79f56ce322daed Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Wed, 15 Jan 2020 17:38:38 -0800 Subject: Add verifyInputEvent api to InputDispatcher Now InputDispatcher will be able to check whether a certain InputEvent is legitimate. Use the 'verifyInputEvent' api to determine if a given 'InputEvent' actually came from InputDispatcher. Bug: 134977432 Test: atest VerifiedKeyEventTest VerifiedMotionEventTest libinput_tests inputflinger_tests Change-Id: I8e7fa9bfa3c14b0b0d949fb5e28b43ff7583398f --- include/input/Input.h | 62 +++++++++++ libs/input/Input.cpp | 24 +++++ libs/input/tests/Android.bp | 2 +- libs/input/tests/InputEvent_test.cpp | 1 - .../input/tests/InputPublisherAndConsumer_test.cpp | 1 + libs/input/tests/StructLayout_test.cpp | 30 ++++++ libs/input/tests/VerifiedInputEvent_test.cpp | 116 +++++++++++++++++++++ .../inputflinger/dispatcher/InputDispatcher.cpp | 4 + services/inputflinger/dispatcher/InputDispatcher.h | 2 + .../dispatcher/include/InputDispatcherInterface.h | 7 ++ 10 files changed, 247 insertions(+), 2 deletions(-) create mode 100644 libs/input/tests/VerifiedInputEvent_test.cpp (limited to 'include') diff --git a/include/input/Input.h b/include/input/Input.h index cf0814cf2f..14a7288d19 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -73,6 +73,19 @@ enum { AMOTION_EVENT_FLAG_TAINTED = 0x80000000, }; +/** + * Allowed VerifiedKeyEvent flags. All other flags from KeyEvent do not get verified. + * These values must be kept in sync with VerifiedKeyEvent.java + */ +constexpr int32_t VERIFIED_KEY_EVENT_FLAGS = AKEY_EVENT_FLAG_CANCELED; + +/** + * Allowed VerifiedMotionEventFlags. All other flags from MotionEvent do not get verified. + * These values must be kept in sync with VerifiedMotionEvent.java + */ +constexpr int32_t VERIFIED_MOTION_EVENT_FLAGS = + AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED; + enum { /* Used when a motion event is not associated with any display. * Typically used for non-pointer events. */ @@ -718,6 +731,55 @@ protected: bool mInTouchMode; }; +/** + * Base class for verified events. + * Do not create a VerifiedInputEvent explicitly. + * Use helper functions to create them from InputEvents. + */ +struct __attribute__((__packed__)) VerifiedInputEvent { + enum class Type : int32_t { + KEY = AINPUT_EVENT_TYPE_KEY, + MOTION = AINPUT_EVENT_TYPE_MOTION, + }; + + Type type; + int32_t deviceId; + nsecs_t eventTimeNanos; + uint32_t source; + int32_t displayId; +}; + +/** + * Same as KeyEvent, but only contains the data that can be verified. + * If you update this class, you must also update VerifiedKeyEvent.java + */ +struct __attribute__((__packed__)) VerifiedKeyEvent : public VerifiedInputEvent { + int32_t action; + nsecs_t downTimeNanos; + int32_t flags; + int32_t keyCode; + int32_t scanCode; + int32_t metaState; + int32_t repeatCount; +}; + +/** + * Same as MotionEvent, but only contains the data that can be verified. + * If you update this class, you must also update VerifiedMotionEvent.java + */ +struct __attribute__((__packed__)) VerifiedMotionEvent : public VerifiedInputEvent { + float rawX; + float rawY; + int32_t actionMasked; + nsecs_t downTimeNanos; + int32_t flags; + int32_t metaState; + int32_t buttonState; +}; + +VerifiedKeyEvent verifiedKeyEventFromKeyEvent(const KeyEvent& event); +VerifiedMotionEvent verifiedMotionEventFromMotionEvent(const MotionEvent& event); + /* * Input event factory. */ diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index 85b0fd0ec7..2a73dc0149 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -57,6 +57,30 @@ const char* inputEventTypeToString(int32_t type) { return "UNKNOWN"; } +VerifiedKeyEvent verifiedKeyEventFromKeyEvent(const KeyEvent& event) { + return {{VerifiedInputEvent::Type::KEY, event.getDeviceId(), event.getEventTime(), + event.getSource(), event.getDisplayId()}, + event.getAction(), + event.getDownTime(), + event.getFlags() & VERIFIED_KEY_EVENT_FLAGS, + event.getKeyCode(), + event.getScanCode(), + event.getMetaState(), + event.getRepeatCount()}; +} + +VerifiedMotionEvent verifiedMotionEventFromMotionEvent(const MotionEvent& event) { + return {{VerifiedInputEvent::Type::MOTION, event.getDeviceId(), event.getEventTime(), + event.getSource(), event.getDisplayId()}, + event.getRawX(0), + event.getRawY(0), + event.getActionMasked(), + event.getDownTime(), + event.getFlags() & VERIFIED_MOTION_EVENT_FLAGS, + event.getMetaState(), + event.getButtonState()}; +} + void InputEvent::initialize(int32_t deviceId, uint32_t source, int32_t displayId, std::array hmac) { mDeviceId = deviceId; diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp index c1c35e1b89..fb21d5e3b1 100644 --- a/libs/input/tests/Android.bp +++ b/libs/input/tests/Android.bp @@ -10,12 +10,12 @@ cc_test { "LatencyStatistics_test.cpp", "TouchVideoFrame_test.cpp", "VelocityTracker_test.cpp", + "VerifiedInputEvent_test.cpp", ], cflags: [ "-Wall", "-Wextra", "-Werror", - "-Wno-unused-variable", ], shared_libs: [ "libinput", diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp index dce1f29124..d0f761887a 100644 --- a/libs/input/tests/InputEvent_test.cpp +++ b/libs/input/tests/InputEvent_test.cpp @@ -46,7 +46,6 @@ TEST_F(PointerCoordsTest, ClearSetsBitsToZero) { } TEST_F(PointerCoordsTest, AxisValues) { - float* valuePtr; PointerCoords coords; coords.clear(); diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp index d4bbf6c6ac..885196f3f3 100644 --- a/libs/input/tests/InputPublisherAndConsumer_test.cpp +++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp @@ -38,6 +38,7 @@ protected: virtual void SetUp() { status_t result = InputChannel::openInputChannelPair("channel name", serverChannel, clientChannel); + ASSERT_EQ(OK, result); mPublisher = new InputPublisher(serverChannel); mConsumer = new InputConsumer(clientChannel); diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp index aa8a2d488f..dd127fcabd 100644 --- a/libs/input/tests/StructLayout_test.cpp +++ b/libs/input/tests/StructLayout_test.cpp @@ -98,4 +98,34 @@ void TestBodySize() { static_assert(sizeof(InputMessage::Body::Focus) == 8); } +// --- VerifiedInputEvent --- +// Ensure that VerifiedInputEvent, VerifiedKeyEvent, VerifiedMotionEvent are packed. +// We will treat them as byte collections when signing them. There should not be any uninitialized +// data in-between fields. Otherwise, the padded data will affect the hmac value and verifications +// will fail. + +void TestVerifiedEventSize() { + // VerifiedInputEvent + constexpr size_t VERIFIED_INPUT_EVENT_SIZE = sizeof(VerifiedInputEvent::type) + + sizeof(VerifiedInputEvent::deviceId) + sizeof(VerifiedInputEvent::eventTimeNanos) + + sizeof(VerifiedInputEvent::source) + sizeof(VerifiedInputEvent::displayId); + static_assert(sizeof(VerifiedInputEvent) == VERIFIED_INPUT_EVENT_SIZE); + + // VerifiedKeyEvent + constexpr size_t VERIFIED_KEY_EVENT_SIZE = VERIFIED_INPUT_EVENT_SIZE + + sizeof(VerifiedKeyEvent::action) + sizeof(VerifiedKeyEvent::downTimeNanos) + + sizeof(VerifiedKeyEvent::flags) + sizeof(VerifiedKeyEvent::keyCode) + + sizeof(VerifiedKeyEvent::scanCode) + sizeof(VerifiedKeyEvent::metaState) + + sizeof(VerifiedKeyEvent::repeatCount); + static_assert(sizeof(VerifiedKeyEvent) == VERIFIED_KEY_EVENT_SIZE); + + // VerifiedMotionEvent + constexpr size_t VERIFIED_MOTION_EVENT_SIZE = VERIFIED_INPUT_EVENT_SIZE + + sizeof(VerifiedMotionEvent::rawX) + sizeof(VerifiedMotionEvent::rawY) + + sizeof(VerifiedMotionEvent::actionMasked) + sizeof(VerifiedMotionEvent::downTimeNanos) + + sizeof(VerifiedMotionEvent::flags) + sizeof(VerifiedMotionEvent::metaState) + + sizeof(VerifiedMotionEvent::buttonState); + static_assert(sizeof(VerifiedMotionEvent) == VERIFIED_MOTION_EVENT_SIZE); +} + } // namespace android diff --git a/libs/input/tests/VerifiedInputEvent_test.cpp b/libs/input/tests/VerifiedInputEvent_test.cpp new file mode 100644 index 0000000000..a59dbe5987 --- /dev/null +++ b/libs/input/tests/VerifiedInputEvent_test.cpp @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +namespace android { + +static KeyEvent getKeyEventWithFlags(int32_t flags) { + KeyEvent event; + event.initialize(2 /*deviceId*/, AINPUT_SOURCE_GAMEPAD, ADISPLAY_ID_DEFAULT, INVALID_HMAC, + AKEY_EVENT_ACTION_DOWN, flags, AKEYCODE_BUTTON_X, 121 /*scanCode*/, + AMETA_ALT_ON, 1 /*repeatCount*/, 1000 /*downTime*/, 2000 /*eventTime*/); + return event; +} + +static MotionEvent getMotionEventWithFlags(int32_t flags) { + MotionEvent event; + constexpr size_t pointerCount = 1; + PointerProperties pointerProperties[pointerCount]; + PointerCoords pointerCoords[pointerCount]; + for (size_t i = 0; i < pointerCount; i++) { + pointerProperties[i].clear(); + pointerProperties[i].id = i; + pointerCoords[i].clear(); + } + + event.initialize(0 /*deviceId*/, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_DEFAULT, INVALID_HMAC, + AMOTION_EVENT_ACTION_DOWN, 0 /*actionButton*/, flags, + AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/, + MotionClassification::NONE, 2 /*xScale*/, 3 /*yScale*/, 4 /*xOffset*/, + 5 /*yOffset*/, 0.1 /*xPrecision*/, 0.2 /*yPrecision*/, 280 /*xCursorPosition*/, + 540 /*yCursorPosition*/, 100 /*downTime*/, 200 /*eventTime*/, pointerCount, + pointerProperties, pointerCoords); + return event; +} + +TEST(VerifiedKeyEventTest, ConvertKeyEventToVerifiedKeyEvent) { + KeyEvent event = getKeyEventWithFlags(0); + VerifiedKeyEvent verified = verifiedKeyEventFromKeyEvent(event); + + ASSERT_EQ(VerifiedInputEvent::Type::KEY, verified.type); + + ASSERT_EQ(event.getDeviceId(), verified.deviceId); + ASSERT_EQ(event.getEventTime(), verified.eventTimeNanos); + ASSERT_EQ(event.getSource(), verified.source); + ASSERT_EQ(event.getDisplayId(), verified.displayId); + + ASSERT_EQ(event.getAction(), verified.action); + ASSERT_EQ(event.getDownTime(), verified.downTimeNanos); + ASSERT_EQ(event.getFlags() & VERIFIED_KEY_EVENT_FLAGS, verified.flags); + ASSERT_EQ(event.getKeyCode(), verified.keyCode); + ASSERT_EQ(event.getScanCode(), verified.scanCode); + ASSERT_EQ(event.getMetaState(), verified.metaState); + ASSERT_EQ(event.getRepeatCount(), verified.repeatCount); +} + +TEST(VerifiedKeyEventTest, VerifiedKeyEventContainsOnlyVerifiedFlags) { + KeyEvent event = getKeyEventWithFlags(AKEY_EVENT_FLAG_CANCELED | AKEY_EVENT_FLAG_FALLBACK); + VerifiedKeyEvent verified = verifiedKeyEventFromKeyEvent(event); + ASSERT_EQ(AKEY_EVENT_FLAG_CANCELED, verified.flags); +} + +TEST(VerifiedKeyEventTest, VerifiedKeyEventDoesNotContainUnverifiedFlags) { + KeyEvent event = getKeyEventWithFlags(AKEY_EVENT_FLAG_EDITOR_ACTION); + VerifiedKeyEvent verified = verifiedKeyEventFromKeyEvent(event); + ASSERT_EQ(0, verified.flags); +} + +TEST(VerifiedMotionEventTest, ConvertMotionEventToVerifiedMotionEvent) { + MotionEvent event = getMotionEventWithFlags(0); + VerifiedMotionEvent verified = verifiedMotionEventFromMotionEvent(event); + + ASSERT_EQ(VerifiedInputEvent::Type::MOTION, verified.type); + + ASSERT_EQ(event.getDeviceId(), verified.deviceId); + ASSERT_EQ(event.getEventTime(), verified.eventTimeNanos); + ASSERT_EQ(event.getSource(), verified.source); + ASSERT_EQ(event.getDisplayId(), verified.displayId); + + ASSERT_EQ(event.getRawX(0), verified.rawX); + ASSERT_EQ(event.getRawY(0), verified.rawY); + ASSERT_EQ(event.getAction(), verified.actionMasked); + ASSERT_EQ(event.getDownTime(), verified.downTimeNanos); + ASSERT_EQ(event.getFlags() & VERIFIED_MOTION_EVENT_FLAGS, verified.flags); + ASSERT_EQ(event.getMetaState(), verified.metaState); + ASSERT_EQ(event.getButtonState(), verified.buttonState); +} + +TEST(VerifiedMotionEventTest, VerifiedMotionEventContainsOnlyVerifiedFlags) { + MotionEvent event = getMotionEventWithFlags(AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | + AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE); + VerifiedMotionEvent verified = verifiedMotionEventFromMotionEvent(event); + ASSERT_EQ(AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, verified.flags); +} + +TEST(VerifiedMotionEventTest, VerifiedMotionEventDoesNotContainUnverifiedFlags) { + MotionEvent event = getMotionEventWithFlags(AMOTION_EVENT_FLAG_TAINTED); + VerifiedMotionEvent verified = verifiedMotionEventFromMotionEvent(event); + ASSERT_EQ(0, verified.flags); +} + +} // namespace android diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index f2b95e7f7a..a8158ba3cf 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -3340,6 +3340,10 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, int32_t injec return injectionResult; } +std::unique_ptr InputDispatcher::verifyInputEvent(const InputEvent& event) { + return nullptr; +} + bool InputDispatcher::hasInjectionPermission(int32_t injectorPid, int32_t injectorUid) { return injectorUid == 0 || mPolicy->checkInjectEventsPermissionNonReentrant(injectorPid, injectorUid); diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index d2aea80069..72511e9bf2 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -96,6 +96,8 @@ public: int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis, uint32_t policyFlags) override; + virtual std::unique_ptr verifyInputEvent(const InputEvent& event) override; + virtual void setInputWindows( const std::vector>& inputWindowHandles, int32_t displayId, const sp& setInputWindowsListener = nullptr) override; diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h index 3424f4ce84..6e986768fc 100644 --- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h +++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h @@ -92,6 +92,13 @@ public: int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis, uint32_t policyFlags) = 0; + /* + * Check whether InputEvent actually happened by checking the signature of the event. + * + * Return nullptr if the event cannot be verified. + */ + virtual std::unique_ptr verifyInputEvent(const InputEvent& event) = 0; + /* Sets the list of input windows. * * This method may be called on any thread (usually by the input manager). -- cgit v1.2.3-59-g8ed1b From d7a703c592bb8f57964f5b56b0a8111049458f81 Mon Sep 17 00:00:00 2001 From: Anthony Stange Date: Tue, 18 Feb 2020 12:02:22 -0500 Subject: Add hinge angle sensor enum docs Bug: 148954908 Test: N/A Change-Id: I1c75bdf8b0fcaad4128336dbe4515e10ff570235 --- include/android/sensor.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'include') diff --git a/include/android/sensor.h b/include/android/sensor.h index e63ac4b407..eb407794d1 100644 --- a/include/android/sensor.h +++ b/include/android/sensor.h @@ -244,6 +244,9 @@ enum { ASENSOR_TYPE_ACCELEROMETER_UNCALIBRATED = 35, /** * {@link ASENSOR_TYPE_HINGE_ANGLE} + * reporting-mode: on-change + * + * The hinge angle sensor value is returned in degrees. */ ASENSOR_TYPE_HINGE_ANGLE = 36, }; -- cgit v1.2.3-59-g8ed1b From 62a4cf8c48647de3442808264005e093ab7704f0 Mon Sep 17 00:00:00 2001 From: Steven Thomas Date: Fri, 31 Jan 2020 12:04:03 -0800 Subject: Add compatibility param to setFrameRate() api Add a compatiblity param to the setFrameRate() api, so the system has more info to decide the device frame rate when there are multiple competing preferences. I also changed the plumbing for setFrameRate() to go directly to surface flinger, instead of through buffer queue. We're trying to avoid changes to buffer queue code, to avoid disturbing the prebuilts. Bug: 137287430 Test: Added new cts tests to verify behavior of the compatibility param. cts-tradefed run commandAndExit cts-dev --module CtsGraphicsTestCases --test android.graphics.cts.SetFrameRateTest Test: /data/nativetest64/SurfaceFlinger_test/SurfaceFlinger_test --gtest_filter='SetFrameRateTest.*' Change-Id: Ibe75a778fb459d4138a1446c1b38b44798b56a99 --- include/android/surface_control.h | 9 ++- libs/gui/ISurfaceComposer.cpp | 66 ++++++++++++++++++++++ libs/gui/LayerState.cpp | 22 ++++++++ libs/gui/Surface.cpp | 16 ++++-- libs/gui/SurfaceComposerClient.cpp | 7 ++- libs/gui/include/gui/ISurfaceComposer.h | 7 +++ libs/gui/include/gui/LayerState.h | 15 ++++- libs/gui/include/gui/Surface.h | 2 +- libs/gui/include/gui/SurfaceComposerClient.h | 3 +- libs/gui/tests/Surface_test.cpp | 5 ++ libs/nativewindow/ANativeWindow.cpp | 6 +- libs/nativewindow/include/android/native_window.h | 29 +++++++++- libs/nativewindow/include/system/window.h | 6 +- libs/nativewindow/libnativewindow.map.txt | 2 +- services/surfaceflinger/BufferQueueLayer.cpp | 13 ----- services/surfaceflinger/BufferQueueLayer.h | 5 -- services/surfaceflinger/Layer.cpp | 13 +++++ services/surfaceflinger/Layer.h | 6 +- services/surfaceflinger/Scheduler/LayerHistory.cpp | 1 - services/surfaceflinger/SurfaceFlinger.cpp | 35 +++++++++++- services/surfaceflinger/SurfaceFlinger.h | 2 + .../surfaceflinger/tests/SetFrameRate_test.cpp | 14 +++-- 22 files changed, 236 insertions(+), 48 deletions(-) (limited to 'include') diff --git a/include/android/surface_control.h b/include/android/surface_control.h index eeb8330efd..c30dcfee09 100644 --- a/include/android/surface_control.h +++ b/include/android/surface_control.h @@ -425,12 +425,15 @@ void ASurfaceTransaction_setHdrMetadata_cta861_3(ASurfaceTransaction* transactio * valid refresh rate for this device's display - e.g., it's fine to pass 30fps to a device that can * only run the display at 60fps. * + * |compatibility| The frame rate compatibility of this surface. The compatibility value may + * influence the system's choice of display frame rate. To specify a compatibility use the + * ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_* enum. + * * Available since API level 30. */ void ASurfaceTransaction_setFrameRate(ASurfaceTransaction* transaction, - ASurfaceControl* surface_control, - float frameRate) - __INTRODUCED_IN(30); + ASurfaceControl* surface_control, float frameRate, + int8_t compatibility) __INTRODUCED_IN(30); #endif // __ANDROID_API__ >= 30 diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp index 2f27fd20fd..ce41eaba1d 100644 --- a/libs/gui/ISurfaceComposer.cpp +++ b/libs/gui/ISurfaceComposer.cpp @@ -1112,6 +1112,42 @@ public: } return NO_ERROR; } + + virtual status_t setFrameRate(const sp& surface, float frameRate, + int8_t compatibility) { + Parcel data, reply; + status_t err = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + if (err != NO_ERROR) { + ALOGE("setFrameRate: failed writing interface token: %s (%d)", strerror(-err), -err); + return err; + } + + err = data.writeStrongBinder(IInterface::asBinder(surface)); + if (err != NO_ERROR) { + ALOGE("setFrameRate: failed writing strong binder: %s (%d)", strerror(-err), -err); + return err; + } + + err = data.writeFloat(frameRate); + if (err != NO_ERROR) { + ALOGE("setFrameRate: failed writing float: %s (%d)", strerror(-err), -err); + return err; + } + + err = data.writeByte(compatibility); + if (err != NO_ERROR) { + ALOGE("setFrameRate: failed writing byte: %s (%d)", strerror(-err), -err); + return err; + } + + err = remote()->transact(BnSurfaceComposer::SET_FRAME_RATE, data, &reply, + IBinder::FLAG_ONEWAY); + if (err != NO_ERROR) { + ALOGE("setFrameRate: failed to transact: %s (%d)", strerror(-err), err); + return err; + } + return NO_ERROR; + } }; // Out-of-line virtual method definition to trigger vtable emission in this @@ -1877,6 +1913,36 @@ status_t BnSurfaceComposer::onTransact( return setGlobalShadowSettings(ambientColor, spotColor, lightPosY, lightPosZ, lightRadius); } + case SET_FRAME_RATE: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + sp binder; + status_t err = data.readStrongBinder(&binder); + if (err != NO_ERROR) { + ALOGE("setFrameRate: failed to read strong binder: %s (%d)", strerror(-err), -err); + return err; + } + sp surface = interface_cast(binder); + if (!surface) { + ALOGE("setFrameRate: failed to cast to IGraphicBufferProducer: %s (%d)", + strerror(-err), -err); + return err; + } + float frameRate; + err = data.readFloat(&frameRate); + if (err != NO_ERROR) { + ALOGE("setFrameRate: failed to read float: %s (%d)", strerror(-err), -err); + return err; + } + int8_t compatibility; + err = data.readByte(&compatibility); + if (err != NO_ERROR) { + ALOGE("setFrameRate: failed to read byte: %s (%d)", strerror(-err), -err); + return err; + } + status_t result = setFrameRate(surface, frameRate, compatibility); + reply->writeInt32(result); + return NO_ERROR; + } default: { return BBinder::onTransact(code, data, reply, flags); } diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp index 5547efc3ad..a9c9b7460b 100644 --- a/libs/gui/LayerState.cpp +++ b/libs/gui/LayerState.cpp @@ -24,6 +24,8 @@ #include #include +#include + namespace android { status_t layer_state_t::write(Parcel& output) const @@ -113,6 +115,7 @@ status_t layer_state_t::write(Parcel& output) const output.writeFloat(shadowRadius); output.writeInt32(frameRateSelectionPriority); output.writeFloat(frameRate); + output.writeByte(frameRateCompatibility); return NO_ERROR; } @@ -194,6 +197,7 @@ status_t layer_state_t::read(const Parcel& input) shadowRadius = input.readFloat(); frameRateSelectionPriority = input.readInt32(); frameRate = input.readFloat(); + frameRateCompatibility = input.readByte(); return NO_ERROR; } @@ -427,6 +431,7 @@ void layer_state_t::merge(const layer_state_t& other) { if (other.what & eFrameRateChanged) { what |= eFrameRateChanged; frameRate = other.frameRate; + frameRateCompatibility = other.frameRateCompatibility; } if ((other.what & what) != other.what) { ALOGE("Unmerged SurfaceComposer Transaction properties. LayerState::merge needs updating? " @@ -474,4 +479,21 @@ void InputWindowCommands::read(const Parcel& input) { syncInputWindows = input.readBool(); } +bool ValidateFrameRate(float frameRate, int8_t compatibility, const char* inFunctionName) { + const char* functionName = inFunctionName != nullptr ? inFunctionName : "call"; + int floatClassification = std::fpclassify(frameRate); + if (frameRate < 0 || floatClassification == FP_INFINITE || floatClassification == FP_NAN) { + ALOGE("%s failed - invalid frame rate %f", functionName, frameRate); + return false; + } + + if (compatibility != ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT && + compatibility != ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE) { + ALOGE("%s failed - invalid compatibility value %d", functionName, compatibility); + return false; + } + + return true; +} + }; // namespace android diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp index 278cc593b7..f911e70ebf 100644 --- a/libs/gui/Surface.cpp +++ b/libs/gui/Surface.cpp @@ -43,6 +43,7 @@ #include #include +#include #include namespace android { @@ -1413,7 +1414,8 @@ int Surface::dispatchGetLastQueueDuration(va_list args) { int Surface::dispatchSetFrameRate(va_list args) { float frameRate = static_cast(va_arg(args, double)); - return setFrameRate(frameRate); + int8_t compatibility = static_cast(va_arg(args, int)); + return setFrameRate(frameRate, compatibility); } int Surface::dispatchAddCancelInterceptor(va_list args) { @@ -2222,11 +2224,15 @@ void Surface::ProducerListenerProxy::onBuffersDiscarded(const std::vectoronBuffersDiscarded(discardedBufs); } -status_t Surface::setFrameRate(float frameRate) { +status_t Surface::setFrameRate(float frameRate, int8_t compatibility) { ATRACE_CALL(); - ALOGV("Surface::setTargetFrameRate"); - Mutex::Autolock lock(mMutex); - return mGraphicBufferProducer->setFrameRate(frameRate); + ALOGV("Surface::setFrameRate"); + + if (!ValidateFrameRate(frameRate, compatibility, "Surface::setFrameRate")) { + return BAD_VALUE; + } + + return composerService()->setFrameRate(mGraphicBufferProducer, frameRate, compatibility); } }; // namespace android diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index 7017b7c8f3..7f28c6c230 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -1402,14 +1402,19 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setShado } SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFrameRate( - const sp& sc, float frameRate) { + const sp& sc, float frameRate, int8_t compatibility) { layer_state_t* s = getLayerState(sc); if (!s) { mStatus = BAD_INDEX; return *this; } + if (!ValidateFrameRate(frameRate, compatibility, "Transaction::setFrameRate")) { + mStatus = BAD_VALUE; + return *this; + } s->what |= layer_state_t::eFrameRateChanged; s->frameRate = frameRate; + s->frameRateCompatibility = compatibility; return *this; } diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h index e860f61c22..0659f0de06 100644 --- a/libs/gui/include/gui/ISurfaceComposer.h +++ b/libs/gui/include/gui/ISurfaceComposer.h @@ -500,6 +500,12 @@ public: virtual status_t setGlobalShadowSettings(const half4& ambientColor, const half4& spotColor, float lightPosY, float lightPosZ, float lightRadius) = 0; + + /* + * Sets the intended frame rate for a surface. See ANativeWindow_setFrameRate() for more info. + */ + virtual status_t setFrameRate(const sp& surface, float frameRate, + int8_t compatibility) = 0; }; // ---------------------------------------------------------------------------- @@ -557,6 +563,7 @@ public: SET_AUTO_LOW_LATENCY_MODE, GET_GAME_CONTENT_TYPE_SUPPORT, SET_GAME_CONTENT_TYPE, + SET_FRAME_RATE, // Always append new enum to the end. }; diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h index 2d53b48475..7e3d5d50d3 100644 --- a/libs/gui/include/gui/LayerState.h +++ b/libs/gui/include/gui/LayerState.h @@ -20,8 +20,7 @@ #include #include -#include - +#include #include #include #include @@ -36,6 +35,7 @@ #include #include #include +#include namespace android { @@ -135,7 +135,8 @@ struct layer_state_t { colorSpaceAgnostic(false), shadowRadius(0.0f), frameRateSelectionPriority(-1), - frameRate(0.0f) { + frameRate(0.0f), + frameRateCompatibility(ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT) { matrix.dsdx = matrix.dtdy = 1.0f; matrix.dsdy = matrix.dtdx = 0.0f; hdrMetadata.validTypes = 0; @@ -221,7 +222,9 @@ struct layer_state_t { // Priority of the layer assigned by Window Manager. int32_t frameRateSelectionPriority; + // Layer frame rate and compatibility. See ANativeWindow_setFrameRate(). float frameRate; + int8_t frameRateCompatibility; }; struct ComposerState { @@ -292,6 +295,12 @@ static inline int compare_type(const DisplayState& lhs, const DisplayState& rhs) return compare_type(lhs.token, rhs.token); } +// Returns true if the frameRate and compatibility are valid values, false +// othwerise. If either of the params are invalid, an error log is printed, and +// functionName is added to the log to indicate which function call failed. +// functionName can be null. +bool ValidateFrameRate(float frameRate, int8_t compatibility, const char* functionName); + }; // namespace android #endif // ANDROID_SF_LAYER_STATE_H diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h index 4a353fc659..ad7cbfe914 100644 --- a/libs/gui/include/gui/Surface.h +++ b/libs/gui/include/gui/Surface.h @@ -179,7 +179,7 @@ public: status_t getConsumerUsage(uint64_t* outUsage) const; // See IGraphicBufferProducer::setFrameRate - status_t setFrameRate(float frameRate); + status_t setFrameRate(float frameRate, int8_t compatibility); protected: virtual ~Surface(); diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h index d0bb6a39ec..fe3dec5ee5 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -525,7 +525,8 @@ public: const Rect& source, const Rect& dst, int transform); Transaction& setShadowRadius(const sp& sc, float cornerRadius); - Transaction& setFrameRate(const sp& sc, float frameRate); + Transaction& setFrameRate(const sp& sc, float frameRate, + int8_t compatibility); status_t setDisplaySurface(const sp& token, const sp& bufferProducer); diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index 70fd888aaf..8c0f8f8de9 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -854,6 +854,11 @@ public: return NO_ERROR; } + status_t setFrameRate(const sp& /*surface*/, float /*frameRate*/, + int8_t /*compatibility*/) override { + return NO_ERROR; + } + protected: IBinder* onAsBinder() override { return nullptr; } diff --git a/libs/nativewindow/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp index 98b76fd667..f09decf21a 100644 --- a/libs/nativewindow/ANativeWindow.cpp +++ b/libs/nativewindow/ANativeWindow.cpp @@ -158,11 +158,11 @@ int32_t ANativeWindow_getBuffersDataSpace(ANativeWindow* window) { return query(window, NATIVE_WINDOW_DATASPACE); } -int32_t ANativeWindow_setFrameRate(ANativeWindow* window, float frameRate) { - if (!window || !query(window, NATIVE_WINDOW_IS_VALID) || frameRate < 0) { +int32_t ANativeWindow_setFrameRate(ANativeWindow* window, float frameRate, int8_t compatibility) { + if (!window || !query(window, NATIVE_WINDOW_IS_VALID)) { return -EINVAL; } - return native_window_set_frame_rate(window, frameRate); + return native_window_set_frame_rate(window, frameRate, compatibility); } void ANativeWindow_tryAllocateBuffers(ANativeWindow* window) { diff --git a/libs/nativewindow/include/android/native_window.h b/libs/nativewindow/include/android/native_window.h index 4b426c518a..59aa6655b8 100644 --- a/libs/nativewindow/include/android/native_window.h +++ b/libs/nativewindow/include/android/native_window.h @@ -33,6 +33,7 @@ #ifndef ANDROID_NATIVE_WINDOW_H #define ANDROID_NATIVE_WINDOW_H +#include #include #include @@ -232,6 +233,24 @@ int32_t ANativeWindow_getBuffersDataSpace(ANativeWindow* window) __INTRODUCED_IN #if __ANDROID_API__ >= 30 +/* Parameter for ANativeWindow_setFrameRate */ +enum { + /** + * There are no inherent restrictions on the frame rate of this window. + */ + ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT = 0, + /** + * This window is being used to display content with an inherently fixed + * frame rate, e.g. a video that has a specific frame rate. When the system + * selects a frame rate other than what the app requested, the app will need + * to do pull down or use some other technique to adapt to the system's + * frame rate. The user experience is likely to be worse (e.g. more frame + * stuttering) than it would be if the system had chosen the app's requested + * frame rate. + */ + ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE = 1 +}; + /** * Sets the intended frame rate for this window. * @@ -257,9 +276,15 @@ int32_t ANativeWindow_getBuffersDataSpace(ANativeWindow* window) __INTRODUCED_IN * refresh rate for this device's display - e.g., it's fine to pass 30fps to a * device that can only run the display at 60fps. * - * \return 0 for success, -EINVAL if the window or frame rate are invalid. + * \param compatibility The frame rate compatibility of this window. The + * compatibility value may influence the system's choice of display refresh + * rate. See the ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_* values for more info. + * + * \return 0 for success, -EINVAL if the window, frame rate, or compatibility + * value are invalid. */ -int32_t ANativeWindow_setFrameRate(ANativeWindow* window, float frameRate) __INTRODUCED_IN(30); +int32_t ANativeWindow_setFrameRate(ANativeWindow* window, float frameRate, int8_t compatibility) + __INTRODUCED_IN(30); /** * Provides a hint to the window that buffers should be preallocated ahead of diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h index f686147a5f..0e28fb84ca 100644 --- a/libs/nativewindow/include/system/window.h +++ b/libs/nativewindow/include/system/window.h @@ -1015,8 +1015,10 @@ static inline int native_window_set_auto_prerotation(struct ANativeWindow* windo return window->perform(window, NATIVE_WINDOW_SET_AUTO_PREROTATION, autoPrerotation); } -static inline int native_window_set_frame_rate(struct ANativeWindow* window, float frameRate) { - return window->perform(window, NATIVE_WINDOW_SET_FRAME_RATE, (double)frameRate); +static inline int native_window_set_frame_rate(struct ANativeWindow* window, float frameRate, + int8_t compatibility) { + return window->perform(window, NATIVE_WINDOW_SET_FRAME_RATE, (double)frameRate, + (int)compatibility); } // ------------------------------------------------------------------------------------------------ diff --git a/libs/nativewindow/libnativewindow.map.txt b/libs/nativewindow/libnativewindow.map.txt index 154eb8eb52..e072e1145d 100644 --- a/libs/nativewindow/libnativewindow.map.txt +++ b/libs/nativewindow/libnativewindow.map.txt @@ -46,9 +46,9 @@ LIBNATIVEWINDOW { ANativeWindow_setBuffersTimestamp; # llndk ANativeWindow_setBuffersTransform; ANativeWindow_setDequeueTimeout; # apex # introduced=30 + ANativeWindow_setFrameRate; # introduced=30 ANativeWindow_setSharedBufferMode; # llndk ANativeWindow_setSwapInterval; # llndk - ANativeWindow_setFrameRate; # introduced=30 ANativeWindow_setUsage; # llndk ANativeWindow_tryAllocateBuffers; # introduced=30 ANativeWindow_unlockAndPost; diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp index e65064bebf..f5a99cadd7 100644 --- a/services/surfaceflinger/BufferQueueLayer.cpp +++ b/services/surfaceflinger/BufferQueueLayer.cpp @@ -124,18 +124,6 @@ bool BufferQueueLayer::shouldPresentNow(nsecs_t expectedPresentTime) const { return isDue || !isPlausible; } -bool BufferQueueLayer::setFrameRate(FrameRate frameRate) { - float oldFrameRate = 0.f; - status_t result = mConsumer->getFrameRate(&oldFrameRate); - bool frameRateChanged = result < 0 || frameRate.rate != oldFrameRate; - mConsumer->setFrameRate(frameRate.rate); - return frameRateChanged; -} - -Layer::FrameRate BufferQueueLayer::getFrameRate() const { - return FrameRate(mLatchedFrameRate, Layer::FrameRateCompatibility::Default); -} - // ----------------------------------------------------------------------- // Interface implementation for BufferLayer // ----------------------------------------------------------------------- @@ -578,7 +566,6 @@ void BufferQueueLayer::gatherBufferInfo() { mBufferInfo.mTransformToDisplayInverse = mConsumer->getTransformToDisplayInverse(); float latchedFrameRate; mConsumer->getFrameRate(&latchedFrameRate); - mLatchedFrameRate = latchedFrameRate; } sp BufferQueueLayer::createClone() { diff --git a/services/surfaceflinger/BufferQueueLayer.h b/services/surfaceflinger/BufferQueueLayer.h index 626af4b6f1..5f7587c547 100644 --- a/services/surfaceflinger/BufferQueueLayer.h +++ b/services/surfaceflinger/BufferQueueLayer.h @@ -56,9 +56,6 @@ public: bool shouldPresentNow(nsecs_t expectedPresentTime) const override; - bool setFrameRate(FrameRate frameRate) override; - FrameRate getFrameRate() const override; - // ----------------------------------------------------------------------- // ----------------------------------------------------------------------- @@ -155,8 +152,6 @@ private: std::atomic mSidebandStreamChanged{false}; sp mContentsChangedListener; - - std::atomic mLatchedFrameRate = 0.f; }; } // namespace android diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index da26a374db..d7647d76d3 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -26,6 +26,7 @@ #include "Layer.h" #include +#include #include #include #include @@ -2446,6 +2447,18 @@ void Layer::addChildToDrawing(const sp& layer) { layer->mDrawingParent = this; } +Layer::FrameRateCompatibility Layer::FrameRate::convertCompatibility(int8_t compatibility) { + switch (compatibility) { + case ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT: + return FrameRateCompatibility::Default; + case ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE: + return FrameRateCompatibility::ExactOrMultiple; + default: + LOG_ALWAYS_FATAL("Invalid frame rate compatibility value %d", compatibility); + return FrameRateCompatibility::Default; + } +} + // --------------------------------------------------------------------------- }; // namespace android diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index 37ae340653..5d2144aed4 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -161,6 +161,10 @@ public: } bool operator!=(const FrameRate& other) const { return !(*this == other); } + + // Convert an ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_* value to a + // Layer::FrameRateCompatibility. Logs fatal if the compatibility value is invalid. + static FrameRateCompatibility convertCompatibility(int8_t compatibility); }; struct State { @@ -795,7 +799,7 @@ public: */ Rect getCroppedBufferSize(const Layer::State& s) const; - virtual bool setFrameRate(FrameRate frameRate); + bool setFrameRate(FrameRate frameRate); virtual FrameRate getFrameRate() const; protected: diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp index b313777253..b851fc6ebb 100644 --- a/services/surfaceflinger/Scheduler/LayerHistory.cpp +++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp @@ -179,4 +179,3 @@ void LayerHistory::clear() { mActiveLayersEnd = 0; } } // namespace android::scheduler::impl - diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index a98ff4fe7c..2701c3e6c9 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -34,6 +34,8 @@ #include #include +#include + #include #include @@ -58,6 +60,7 @@ #include #include #include +#include #include #include

To use:

    + *
  • Create a new thermal manager instance by calling the + * {@link AThermal_acquireManager} function.
  • + *
  • Get current thermal status with + * {@link AThermal_getCurrentThermalStatus}.
  • + *
  • Register a thermal status listener with + * {@link AThermal_registerThermalStatusListener}.
  • + *
  • Unregister a thermal status listener with + * {@link AThermal_unregisterThermalStatusListener}.
  • + *
  • Release the thermal manager instance with + * {@link AThermal_releaseManager}.

+ * + */ +typedef struct AThermalManager AThermalManager; + +/** + * Prototype of the function that is called when thermal status changes. + * It's passed the updated thermal status as parameter, as well as the + * pointer provided by the client that registered a callback. + */ +typedef int (*AThermal_StatusCallback)(void *data, AThermalStatus status); + +/** + * Acquire an instance of the thermal manager. This must be freed using + * {@link AThermal_releaseManager}. + * + * @return manager instance on success, nullptr on failure. + */ +AThermalManager* AThermal_acquireManager(); + +/** + * Release the thermal manager pointer acquired via + * {@link AThermal_acquireManager}. + * + * @param manager The manager to be released. + * + */ +void AThermal_releaseManager(AThermalManager *manager); + +/** + * Gets the current thermal status. + * + * @param manager The manager instance to use to query the thermal status. + * Acquired via {@link AThermal_acquireManager}. + * + * @return current thermal status, ATHERMAL_STATUS_ERROR on failure. +*/ +AThermalStatus AThermal_getCurrentThermalStatus(AThermalManager *manager); + +/** + * Register the thermal status listener for thermal status change. + * + * @param manager The manager instance to use to register. + * Acquired via {@link AThermal_acquireManager}. + * @param callback The callback function to be called when thermal status updated. + * @param data The data pointer to be passed when callback is called. + * + * @return 0 on success + * EINVAL if the listener and data pointer were previously added and not removed. + * EPERM if the required permission is not held. + * EPIPE if communication with the system service has failed. + */ +int AThermal_registerThermalStatusListener(AThermalManager *manager, + AThermal_StatusCallback callback, void *data); + +/** + * Unregister the thermal status listener previously resgistered. + * + * @param manager The manager instance to use to unregister. + * Acquired via {@link AThermal_acquireManager}. + * @param callback The callback function to be called when thermal status updated. + * @param data The data pointer to be passed when callback is called. + * + * @return 0 on success + * EINVAL if the listener and data pointer were not previously added. + * EPERM if the required permission is not held. + * EPIPE if communication with the system service has failed. + */ +int AThermal_unregisterThermalStatusListener(AThermalManager *manager, + AThermal_StatusCallback callback, void *data); + + +#endif // __ANDROID_API__ >= 30 + +#ifdef __cplusplus +} +#endif + +#endif // _ANDROID_THERMAL_H + +/** @} */ diff --git a/include/powermanager/PowerManager.h b/include/powermanager/PowerManager.h index 3268b45716..9bac242a12 100644 --- a/include/powermanager/PowerManager.h +++ b/include/powermanager/PowerManager.h @@ -33,6 +33,17 @@ enum { USER_ACTIVITY_EVENT_LAST = USER_ACTIVITY_EVENT_ACCESSIBILITY, // Last valid event code. }; +/** Keep in sync with android.os.temprature and hardware/interfaces/thermal/2.0/types.hal */ +enum class ThermalStatus : uint32_t { + THERMAL_STATUS_NONE = 0, + THERMAL_STATUS_LIGHT = 1, + THERMAL_STATUS_MODERATE = 2, + THERMAL_STATUS_SEVERE = 3, + THERMAL_STATUS_CRITICAL = 4, + THERMAL_STATUS_EMERGENCY = 5, + THERMAL_STATUS_SHUTDOWN = 6, +}; + }; // namespace android #endif // ANDROID_POWERMANAGER_H diff --git a/services/powermanager/Android.bp b/services/powermanager/Android.bp index 7b3af70927..3e0f136dfb 100644 --- a/services/powermanager/Android.bp +++ b/services/powermanager/Android.bp @@ -1,11 +1,25 @@ cc_library_shared { name: "libpowermanager", - srcs: ["IPowerManager.cpp"], + srcs: [ + "IPowerManager.cpp", + "Temperature.cpp", + "CoolingDevice.cpp", + ":libpowermanager_aidl", + ], + + aidl: { + local_include_dirs: ["."], + include_dirs: [ + "frameworks/base/core/java/android/os", + ], + export_aidl_headers: true + }, shared_libs: [ "libutils", "libbinder", + "liblog" ], cflags: [ @@ -15,3 +29,22 @@ cc_library_shared { "-Wunreachable-code", ], } + +cc_test { + name: "thermalmanager-test", + srcs: ["IThermalManagerTest.cpp", + ], + cflags: [ + "-Wall", + "-Werror", + "-Wextra", + ], + shared_libs: [ + "libbase", + "libhidlbase", + "liblog", + "libpowermanager", + "libbinder", + "libutils", + ], +} diff --git a/services/powermanager/CoolingDevice.cpp b/services/powermanager/CoolingDevice.cpp new file mode 100644 index 0000000000..ebbc1b4197 --- /dev/null +++ b/services/powermanager/CoolingDevice.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "CoolingDevice" + +#include +#include +#include + +namespace android { +namespace os { + +status_t CoolingDevice::readFromParcel(const android::Parcel *parcel) { + if (parcel == nullptr) { + ALOGE("%s: Null parcel", __FUNCTION__); + return BAD_VALUE; + } + + parcel->readFloat(&mValue); + parcel->readUint32(&mType); + parcel->readString16(&mName); + + return OK; +} + +status_t CoolingDevice::writeToParcel(android::Parcel *parcel) const { + if (parcel == nullptr) { + ALOGE("%s: Null parcel", __FUNCTION__); + return BAD_VALUE; + } + + parcel->writeFloat(mValue); + parcel->writeUint32(mType); + parcel->writeString16(mName); + + return OK; +} + +} // namespace os +} // namespace android \ No newline at end of file diff --git a/services/powermanager/IThermalManagerTest.cpp b/services/powermanager/IThermalManagerTest.cpp new file mode 100644 index 0000000000..575b9ee1c4 --- /dev/null +++ b/services/powermanager/IThermalManagerTest.cpp @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "ThermalManagerTest" +//#define LOG_NDEBUG 0 + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace android; +using namespace android::os; +using namespace std::chrono_literals; + +class IThermalServiceTest : public testing::Test, + public BnThermalStatusListener{ + public: + IThermalServiceTest(); + void setThermalOverride(int level); + virtual binder::Status onStatusChange(int status) override; + int getStatusFromService(); + void SetUp() override; + void TearDown() override; + protected: + sp mThermalSvc; + std::condition_variable mCondition; + int mListenerStatus; + int mServiceStatus; + std::mutex mMutex; +}; + +IThermalServiceTest::IThermalServiceTest() + : mListenerStatus(0), + mServiceStatus(0) { +} + +void IThermalServiceTest::setThermalOverride(int level) { + std::string cmdStr = "cmd thermalservice override-status " + std::to_string(level); + system(cmdStr.c_str()); +} + +binder::Status IThermalServiceTest::onStatusChange(int status) { + std::unique_lock lock(mMutex); + mListenerStatus = status; + ALOGI("IThermalServiceTest::notifyListener %d", mListenerStatus); + mCondition.notify_all(); + return binder::Status::ok(); +} + +int IThermalServiceTest::getStatusFromService() { + int status; + binder::Status ret = mThermalSvc->getCurrentThermalStatus(&status); + if (ret.isOk()) { + return status; + } else { + return BAD_VALUE; + } +} + +void IThermalServiceTest::SetUp() { + setThermalOverride(0); + // use checkService() to avoid blocking if thermal service is not up yet + sp binder = + defaultServiceManager()->checkService(String16("thermalservice")); + EXPECT_NE(binder, nullptr); + mThermalSvc = interface_cast(binder); + EXPECT_NE(mThermalSvc, nullptr); + bool success = false; + binder::Status ret = mThermalSvc->registerThermalStatusListener(this, &success); + ASSERT_TRUE(success); + ASSERT_TRUE(ret.isOk()); + // Wait for listener called after registration, shouldn't timeout + std::unique_lock lock(mMutex); + EXPECT_NE(mCondition.wait_for(lock, 1s), std::cv_status::timeout); +} + +void IThermalServiceTest::TearDown() { + bool success = false; + binder::Status ret = mThermalSvc->unregisterThermalStatusListener(this, &success); + ASSERT_TRUE(success); + ASSERT_TRUE(ret.isOk()); +} + +class IThermalListenerTest : public IThermalServiceTest, public testing::WithParamInterface { + public: + static auto PrintParam(const testing::TestParamInfo &info) { + return std::to_string(info.param); + } +}; + +TEST_P(IThermalListenerTest, TestListener) { + int level = GetParam(); + std::unique_lock lock(mMutex); + // Set the override thermal status + setThermalOverride(level); + // Wait for listener called, shouldn't timeout + EXPECT_NE(mCondition.wait_for(lock, 1s), std::cv_status::timeout); + // Check the result + EXPECT_EQ(level, mListenerStatus); + ALOGI("Thermal listener status %d, expecting %d", mListenerStatus, level); +} + +INSTANTIATE_TEST_SUITE_P(TestListenerLevels, IThermalListenerTest, testing::Range( + static_cast(ThermalStatus::THERMAL_STATUS_LIGHT), + static_cast(ThermalStatus::THERMAL_STATUS_SHUTDOWN)), + IThermalListenerTest::PrintParam); + +class IThermalLevelTest : public IThermalServiceTest, public testing::WithParamInterface { + public: + static auto PrintParam(const testing::TestParamInfo &info) { + return std::to_string(info.param); + } +}; + +TEST_P(IThermalLevelTest, TestGetStatusLevel) { + int level = GetParam(); + setThermalOverride(level); + mServiceStatus = getStatusFromService(); + EXPECT_EQ(level, mServiceStatus); +} + +INSTANTIATE_TEST_SUITE_P(TestStatusLevels, IThermalLevelTest, testing::Range( + static_cast(ThermalStatus::THERMAL_STATUS_NONE), + static_cast(ThermalStatus::THERMAL_STATUS_SHUTDOWN)), + IThermalLevelTest::PrintParam); + +int main(int argc, char **argv) { + std::unique_ptr binderLoop; + binderLoop = std::make_unique( + [&] { IPCThreadState::self()->joinThreadPool(true); }); + + ::testing::InitGoogleTest(&argc, argv); + int status = RUN_ALL_TESTS(); + ALOGV("Test result = %d\n", status); + + return status; +} \ No newline at end of file diff --git a/services/powermanager/Temperature.cpp b/services/powermanager/Temperature.cpp new file mode 100644 index 0000000000..8ec0a87146 --- /dev/null +++ b/services/powermanager/Temperature.cpp @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "Temperature" + +#include +#include +#include + +namespace android { +namespace os { + +status_t Temperature::readFromParcel(const android::Parcel *parcel) { + if (parcel == nullptr) { + ALOGE("%s: Null parcel", __FUNCTION__); + return BAD_VALUE; + } + + parcel->readFloat(&mValue); + parcel->readInt32(&mType); + parcel->readString16(&mName); + parcel->readInt32(&mStatus); + + return OK; +} + +status_t Temperature::writeToParcel(android::Parcel *parcel) const { + if (parcel == nullptr) { + ALOGE("%s: Null parcel", __FUNCTION__); + return BAD_VALUE; + } + + parcel->writeFloat(mValue); + parcel->writeInt32(mType); + parcel->writeString16(mName); + parcel->writeInt32(mStatus); + + return OK; +} + +} // namespace os +} // namespace android \ No newline at end of file -- cgit v1.2.3-59-g8ed1b From a7b82e1d297a3a5aa1150ac9556361856e681351 Mon Sep 17 00:00:00 2001 From: "Nathaniel R. Lewis" Date: Wed, 12 Feb 2020 15:40:45 -0800 Subject: Support multiple EventHub devices per InputDevice Some physical devices contain more functions than can be addressed by a single Linux evdev device. Examples of such devices are the Sony DualShock 4 and Wacom Tablets, which appear as multiple evdev devices sharing the same value for the EVIOCGUNIQ ioctl. As more instances of such devices hit the market, apps need a way of figuring out which InputDevices are part of the same physical device. Per conversation with the android input team, a solution proposed to this problem is to merge multiple EventHub devices (which have a 1:1 relation with evdev) into a single android InputDevice. Changes: Decouple the EventHub device id ("eventHubId") from the InputDevice device id ("deviceId"). This requires InputDeviceContext to track the the EventHub devices it represents. Most logic changes are inside InputDeviceContext, so there are minimal changes to InputMappers. Added enum value END_RESERVED_ID to represent the first available id that can be assigned to a normal InputDevice. The android framework assumes specific values for the virtual keyboard and built-in hardware keyboard, so for these two cases, the "deviceId" must match the "eventHubId." Added "EVENTHUB_ID" constants to tests and changed where applicable. Cherry-picked from pa/1475694. Bug: 38511270 Test: atest inputflinger_tests libinput_tests Change-Id: I89df085fadc1c09bc999599ac5db35c9277c4a2a --- include/input/InputDevice.h | 2 + services/inputflinger/reader/InputDevice.cpp | 133 +++-- services/inputflinger/reader/InputReader.cpp | 132 ++--- services/inputflinger/reader/include/InputDevice.h | 94 +++- services/inputflinger/reader/include/InputReader.h | 22 +- services/inputflinger/tests/InputReader_test.cpp | 538 +++++++++++---------- 6 files changed, 547 insertions(+), 374 deletions(-) (limited to 'include') diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h index b6efc82fd4..20a17e3347 100644 --- a/include/input/InputDevice.h +++ b/include/input/InputDevice.h @@ -180,6 +180,8 @@ enum ReservedInputDeviceId : int32_t { VIRTUAL_KEYBOARD_ID = -1, // Device id of the "built-in" keyboard if there is one. BUILT_IN_KEYBOARD_ID = 0, + // First device id available for dynamic devices + END_RESERVED_ID = 1, }; } // namespace android diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp index ae82cd4aa4..d0eee64b0c 100644 --- a/services/inputflinger/reader/InputDevice.cpp +++ b/services/inputflinger/reader/InputDevice.cpp @@ -18,6 +18,8 @@ #include "InputDevice.h" +#include + #include "CursorInputMapper.h" #include "ExternalStylusInputMapper.h" #include "InputReaderContext.h" @@ -32,25 +34,28 @@ namespace android { InputDevice::InputDevice(InputReaderContext* context, int32_t id, int32_t generation, - int32_t controllerNumber, const InputDeviceIdentifier& identifier, - uint32_t classes) + const InputDeviceIdentifier& identifier) : mContext(context), mId(id), mGeneration(generation), - mControllerNumber(controllerNumber), + mControllerNumber(0), mIdentifier(identifier), - mClasses(classes), + mClasses(0), mSources(0), mIsExternal(false), mHasMic(false), - mDropUntilNextSync(false) { - mDeviceContext = std::make_unique(*this); -} + mDropUntilNextSync(false) {} InputDevice::~InputDevice() {} bool InputDevice::isEnabled() { - return mDeviceContext->isDeviceEnabled(); + if (!hasEventHubDevices()) { + return false; + } + // devices are either all enabled or all disabled, so we only need to check the first + auto& devicePair = mDevices.begin()->second; + auto& contextPtr = devicePair.first; + return contextPtr->isDeviceEnabled(); } void InputDevice::setEnabled(bool enabled, nsecs_t when) { @@ -65,12 +70,15 @@ void InputDevice::setEnabled(bool enabled, nsecs_t when) { return; } + // When resetting some devices, the driver needs to be queried to ensure that a proper reset is + // performed. The querying must happen when the device is enabled, so we reset after enabling + // but before disabling the device. See MultiTouchMotionAccumulator::reset for more information. if (enabled) { - mDeviceContext->enableDevice(); + for_each_subdevice([](auto& context) { context.enableDevice(); }); reset(when); } else { reset(when); - mDeviceContext->disableDevice(); + for_each_subdevice([](auto& context) { context.disableDevice(); }); } // Must change generation to flag this device as changed bumpGeneration(); @@ -118,19 +126,18 @@ void InputDevice::dump(std::string& dump) { for_each_mapper([&dump](InputMapper& mapper) { mapper.dump(dump); }); } -void InputDevice::populateMappers() { - uint32_t classes = mClasses; - std::vector>& mappers = mMappers; - std::unique_ptr& contextPtr = mDeviceContext; - - // External devices. - if (classes & INPUT_DEVICE_CLASS_EXTERNAL) { - setExternal(true); +void InputDevice::addEventHubDevice(int32_t eventHubId, bool populateMappers) { + if (mDevices.find(eventHubId) != mDevices.end()) { + return; } + std::unique_ptr contextPtr(new InputDeviceContext(*this, eventHubId)); + uint32_t classes = contextPtr->getDeviceClasses(); + std::vector> mappers; - // Devices with mics. - if (classes & INPUT_DEVICE_CLASS_MIC) { - setMic(true); + // Check if we should skip population + if (!populateMappers) { + mDevices.insert({eventHubId, std::make_pair(std::move(contextPtr), std::move(mappers))}); + return; } // Switch-like devices. @@ -190,22 +197,58 @@ void InputDevice::populateMappers() { if (classes & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) { mappers.push_back(std::make_unique(*contextPtr)); } + + // insert the context into the devices set + mDevices.insert({eventHubId, std::make_pair(std::move(contextPtr), std::move(mappers))}); +} + +void InputDevice::removeEventHubDevice(int32_t eventHubId) { + mDevices.erase(eventHubId); } void InputDevice::configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes) { mSources = 0; + mClasses = 0; + mControllerNumber = 0; + + for_each_subdevice([this](InputDeviceContext& context) { + mClasses |= context.getDeviceClasses(); + int32_t controllerNumber = context.getDeviceControllerNumber(); + if (controllerNumber > 0) { + if (mControllerNumber && mControllerNumber != controllerNumber) { + ALOGW("InputDevice::configure(): composite device contains multiple unique " + "controller numbers"); + } + mControllerNumber = controllerNumber; + } + }); + + mIsExternal = !!(mClasses & INPUT_DEVICE_CLASS_EXTERNAL); + mHasMic = !!(mClasses & INPUT_DEVICE_CLASS_MIC); if (!isIgnored()) { if (!changes) { // first time only - mDeviceContext->getConfiguration(&mConfiguration); + mConfiguration.clear(); + for_each_subdevice([this](InputDeviceContext& context) { + PropertyMap configuration; + context.getConfiguration(&configuration); + mConfiguration.addAll(&configuration); + }); } if (!changes || (changes & InputReaderConfiguration::CHANGE_KEYBOARD_LAYOUTS)) { if (!(mClasses & INPUT_DEVICE_CLASS_VIRTUAL)) { sp keyboardLayout = mContext->getPolicy()->getKeyboardLayoutOverlay(mIdentifier); - if (mDeviceContext->setKeyboardLayoutOverlay(keyboardLayout)) { + bool shouldBumpGeneration = false; + for_each_subdevice( + [&keyboardLayout, &shouldBumpGeneration](InputDeviceContext& context) { + if (context.setKeyboardLayoutOverlay(keyboardLayout)) { + shouldBumpGeneration = true; + } + }); + if (shouldBumpGeneration) { bumpGeneration(); } } @@ -313,7 +356,9 @@ void InputDevice::process(const RawEvent* rawEvents, size_t count) { mDropUntilNextSync = true; reset(rawEvent->when); } else { - for_each_mapper([rawEvent](InputMapper& mapper) { mapper.process(rawEvent); }); + for_each_mapper_in_subdevice(rawEvent->deviceId, [rawEvent](InputMapper& mapper) { + mapper.process(rawEvent); + }); } --count; } @@ -348,16 +393,20 @@ int32_t InputDevice::getSwitchState(uint32_t sourceMask, int32_t switchCode) { int32_t InputDevice::getState(uint32_t sourceMask, int32_t code, GetStateFunc getStateFunc) { int32_t result = AKEY_STATE_UNKNOWN; - for (auto& mapperPtr : mMappers) { - InputMapper& mapper = *mapperPtr; - if (sourcesMatchMask(mapper.getSources(), sourceMask)) { - // If any mapper reports AKEY_STATE_DOWN or AKEY_STATE_VIRTUAL, return that - // value. Otherwise, return AKEY_STATE_UP as long as one mapper reports it. - int32_t currentResult = (mapper.*getStateFunc)(sourceMask, code); - if (currentResult >= AKEY_STATE_DOWN) { - return currentResult; - } else if (currentResult == AKEY_STATE_UP) { - result = currentResult; + for (auto& deviceEntry : mDevices) { + auto& devicePair = deviceEntry.second; + auto& mappers = devicePair.second; + for (auto& mapperPtr : mappers) { + InputMapper& mapper = *mapperPtr; + if (sourcesMatchMask(mapper.getSources(), sourceMask)) { + // If any mapper reports AKEY_STATE_DOWN or AKEY_STATE_VIRTUAL, return that + // value. Otherwise, return AKEY_STATE_UP as long as one mapper reports it. + int32_t currentResult = (mapper.*getStateFunc)(sourceMask, code); + if (currentResult >= AKEY_STATE_DOWN) { + return currentResult; + } else if (currentResult == AKEY_STATE_UP) { + result = currentResult; + } } } } @@ -424,11 +473,23 @@ std::optional InputDevice::getAssociatedDisplayId() { [](InputMapper& mapper) { return mapper.getAssociatedDisplayId(); }); } -InputDeviceContext::InputDeviceContext(InputDevice& device) +// returns the number of mappers associated with the device +size_t InputDevice::getMapperCount() { + size_t count = 0; + for (auto& deviceEntry : mDevices) { + auto& devicePair = deviceEntry.second; + auto& mappers = devicePair.second; + count += mappers.size(); + } + return count; +} + +InputDeviceContext::InputDeviceContext(InputDevice& device, int32_t eventHubId) : mDevice(device), mContext(device.getContext()), mEventHub(device.getContext()->getEventHub()), - mId(device.getId()) {} + mId(eventHubId), + mDeviceId(device.getId()) {} InputDeviceContext::~InputDeviceContext() {} diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp index 8327ed8e30..cbfa702015 100644 --- a/services/inputflinger/reader/InputReader.cpp +++ b/services/inputflinger/reader/InputReader.cpp @@ -49,6 +49,7 @@ InputReader::InputReader(std::shared_ptr eventHub, mNextSequenceNum(1), mGlobalMetaState(0), mGeneration(1), + mNextInputDeviceId(END_RESERVED_ID), mDisableVirtualKeysTimeout(LLONG_MIN), mNextTimeout(LLONG_MAX), mConfigurationChangesToRefresh(0) { @@ -184,30 +185,28 @@ void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) { } } -void InputReader::addDeviceLocked(nsecs_t when, int32_t deviceId) { - if (mDevices.find(deviceId) != mDevices.end()) { - ALOGW("Ignoring spurious device added event for deviceId %d.", deviceId); +void InputReader::addDeviceLocked(nsecs_t when, int32_t eventHubId) { + if (mDevices.find(eventHubId) != mDevices.end()) { + ALOGW("Ignoring spurious device added event for eventHubId %d.", eventHubId); return; } - InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(deviceId); - uint32_t classes = mEventHub->getDeviceClasses(deviceId); - int32_t controllerNumber = mEventHub->getDeviceControllerNumber(deviceId); - - std::shared_ptr device = - createDeviceLocked(deviceId, controllerNumber, identifier, classes); + InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(eventHubId); + std::shared_ptr device = createDeviceLocked(eventHubId, identifier); device->configure(when, &mConfig, 0); device->reset(when); if (device->isIgnored()) { - ALOGI("Device added: id=%d, name='%s' (ignored non-input device)", deviceId, - identifier.name.c_str()); + ALOGI("Device added: id=%d, eventHubId=%d, name='%s', descriptor='%s' " + "(ignored non-input device)", + device->getId(), eventHubId, identifier.name.c_str(), identifier.descriptor.c_str()); } else { - ALOGI("Device added: id=%d, name='%s', sources=0x%08x", deviceId, identifier.name.c_str(), + ALOGI("Device added: id=%d, eventHubId=%d, name='%s', descriptor='%s',sources=0x%08x", + device->getId(), eventHubId, identifier.name.c_str(), identifier.descriptor.c_str(), device->getSources()); } - mDevices.emplace(deviceId, device); + mDevices.emplace(eventHubId, device); bumpGenerationLocked(); if (device->getClasses() & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) { @@ -215,10 +214,10 @@ void InputReader::addDeviceLocked(nsecs_t when, int32_t deviceId) { } } -void InputReader::removeDeviceLocked(nsecs_t when, int32_t deviceId) { - auto deviceIt = mDevices.find(deviceId); +void InputReader::removeDeviceLocked(nsecs_t when, int32_t eventHubId) { + auto deviceIt = mDevices.find(eventHubId); if (deviceIt == mDevices.end()) { - ALOGW("Ignoring spurious device removed event for deviceId %d.", deviceId); + ALOGW("Ignoring spurious device removed event for eventHubId %d.", eventHubId); return; } @@ -227,35 +226,52 @@ void InputReader::removeDeviceLocked(nsecs_t when, int32_t deviceId) { bumpGenerationLocked(); if (device->isIgnored()) { - ALOGI("Device removed: id=%d, name='%s' (ignored non-input device)", device->getId(), - device->getName().c_str()); + ALOGI("Device removed: id=%d, eventHubId=%d, name='%s', descriptor='%s' " + "(ignored non-input device)", + device->getId(), eventHubId, device->getName().c_str(), + device->getDescriptor().c_str()); } else { - ALOGI("Device removed: id=%d, name='%s', sources=0x%08x", device->getId(), - device->getName().c_str(), device->getSources()); + ALOGI("Device removed: id=%d, eventHubId=%d, name='%s', descriptor='%s', sources=0x%08x", + device->getId(), eventHubId, device->getName().c_str(), + device->getDescriptor().c_str(), device->getSources()); } + device->removeEventHubDevice(eventHubId); + if (device->getClasses() & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) { notifyExternalStylusPresenceChanged(); } + if (device->hasEventHubDevices()) { + device->configure(when, &mConfig, 0); + } device->reset(when); } std::shared_ptr InputReader::createDeviceLocked( - int32_t deviceId, int32_t controllerNumber, const InputDeviceIdentifier& identifier, - uint32_t classes) { - std::shared_ptr device = - std::make_shared(&mContext, deviceId, bumpGenerationLocked(), - controllerNumber, identifier, classes); - device->populateMappers(); + int32_t eventHubId, const InputDeviceIdentifier& identifier) { + auto deviceIt = std::find_if(mDevices.begin(), mDevices.end(), [identifier](auto& devicePair) { + return devicePair.second->getDescriptor().size() && identifier.descriptor.size() && + devicePair.second->getDescriptor() == identifier.descriptor; + }); + + std::shared_ptr device; + if (deviceIt != mDevices.end()) { + device = deviceIt->second; + } else { + int32_t deviceId = (eventHubId < END_RESERVED_ID) ? eventHubId : nextInputDeviceIdLocked(); + device = std::make_shared(&mContext, deviceId, bumpGenerationLocked(), + identifier); + } + device->addEventHubDevice(eventHubId); return device; } -void InputReader::processEventsForDeviceLocked(int32_t deviceId, const RawEvent* rawEvents, +void InputReader::processEventsForDeviceLocked(int32_t eventHubId, const RawEvent* rawEvents, size_t count) { - auto deviceIt = mDevices.find(deviceId); + auto deviceIt = mDevices.find(eventHubId); if (deviceIt == mDevices.end()) { - ALOGW("Discarding event for unknown deviceId %d.", deviceId); + ALOGW("Discarding event for unknown eventHubId %d.", eventHubId); return; } @@ -268,6 +284,17 @@ void InputReader::processEventsForDeviceLocked(int32_t deviceId, const RawEvent* device->process(rawEvents, count); } +InputDevice* InputReader::findInputDevice(int32_t deviceId) { + auto deviceIt = + std::find_if(mDevices.begin(), mDevices.end(), [deviceId](const auto& devicePair) { + return devicePair.second->getId() == deviceId; + }); + if (deviceIt != mDevices.end()) { + return deviceIt->second.get(); + } + return nullptr; +} + void InputReader::timeoutExpiredLocked(nsecs_t when) { for (auto& devicePair : mDevices) { std::shared_ptr& device = devicePair.second; @@ -277,6 +304,10 @@ void InputReader::timeoutExpiredLocked(nsecs_t when) { } } +int32_t InputReader::nextInputDeviceIdLocked() { + return ++mNextInputDeviceId; +} + void InputReader::handleConfigurationChangedLocked(nsecs_t when) { // Reset global meta state because it depends on the list of all configured devices. updateGlobalMetaStateLocked(); @@ -414,12 +445,9 @@ int32_t InputReader::getStateLocked(int32_t deviceId, uint32_t sourceMask, int32 GetStateFunc getStateFunc) { int32_t result = AKEY_STATE_UNKNOWN; if (deviceId >= 0) { - auto deviceIt = mDevices.find(deviceId); - if (deviceIt != mDevices.end()) { - std::shared_ptr& device = deviceIt->second; - if (!device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) { - result = (device.get()->*getStateFunc)(sourceMask, code); - } + InputDevice* device = findInputDevice(deviceId); + if (device && !device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) { + result = (device->*getStateFunc)(sourceMask, code); } } else { for (auto& devicePair : mDevices) { @@ -440,13 +468,12 @@ int32_t InputReader::getStateLocked(int32_t deviceId, uint32_t sourceMask, int32 } void InputReader::toggleCapsLockState(int32_t deviceId) { - auto deviceIt = mDevices.find(deviceId); - if (deviceIt == mDevices.end()) { + InputDevice* device = findInputDevice(deviceId); + if (!device) { ALOGW("Ignoring toggleCapsLock for unknown deviceId %" PRId32 ".", deviceId); return; } - std::shared_ptr& device = deviceIt->second; if (device->isIgnored()) { return; } @@ -467,12 +494,9 @@ bool InputReader::markSupportedKeyCodesLocked(int32_t deviceId, uint32_t sourceM uint8_t* outFlags) { bool result = false; if (deviceId >= 0) { - auto deviceIt = mDevices.find(deviceId); - if (deviceIt != mDevices.end()) { - std::shared_ptr& device = deviceIt->second; - if (!device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) { - result = device->markSupportedKeyCodes(sourceMask, numCodes, keyCodes, outFlags); - } + InputDevice* device = findInputDevice(deviceId); + if (device && !device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) { + result = device->markSupportedKeyCodes(sourceMask, numCodes, keyCodes, outFlags); } } else { for (auto& devicePair : mDevices) { @@ -501,9 +525,8 @@ void InputReader::requestRefreshConfiguration(uint32_t changes) { void InputReader::vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patternSize, ssize_t repeat, int32_t token) { AutoMutex _l(mLock); - auto deviceIt = mDevices.find(deviceId); - if (deviceIt != mDevices.end()) { - std::shared_ptr& device = deviceIt->second; + InputDevice* device = findInputDevice(deviceId); + if (device) { device->vibrate(pattern, patternSize, repeat, token); } } @@ -511,9 +534,8 @@ void InputReader::vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patte void InputReader::cancelVibrate(int32_t deviceId, int32_t token) { AutoMutex _l(mLock); - auto deviceIt = mDevices.find(deviceId); - if (deviceIt != mDevices.end()) { - std::shared_ptr& device = deviceIt->second; + InputDevice* device = findInputDevice(deviceId); + if (device) { device->cancelVibrate(token); } } @@ -521,9 +543,8 @@ void InputReader::cancelVibrate(int32_t deviceId, int32_t token) { bool InputReader::isInputDeviceEnabled(int32_t deviceId) { AutoMutex _l(mLock); - auto deviceIt = mDevices.find(deviceId); - if (deviceIt != mDevices.end()) { - std::shared_ptr& device = deviceIt->second; + InputDevice* device = findInputDevice(deviceId); + if (device) { return device->isEnabled(); } ALOGW("Ignoring invalid device id %" PRId32 ".", deviceId); @@ -533,13 +554,12 @@ bool InputReader::isInputDeviceEnabled(int32_t deviceId) { bool InputReader::canDispatchToDisplay(int32_t deviceId, int32_t displayId) { AutoMutex _l(mLock); - auto deviceIt = mDevices.find(deviceId); - if (deviceIt == mDevices.end()) { + InputDevice* device = findInputDevice(deviceId); + if (!device) { ALOGW("Ignoring invalid device id %" PRId32 ".", deviceId); return false; } - std::shared_ptr& device = deviceIt->second; if (!device->isEnabled()) { ALOGW("Ignoring disabled device %s", device->getName().c_str()); return false; diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h index 0814d1f16e..aaa0d268b3 100644 --- a/services/inputflinger/reader/include/InputDevice.h +++ b/services/inputflinger/reader/include/InputDevice.h @@ -17,18 +17,19 @@ #ifndef _UI_INPUTREADER_INPUT_DEVICE_H #define _UI_INPUTREADER_INPUT_DEVICE_H -#include "EventHub.h" -#include "InputReaderBase.h" -#include "InputReaderContext.h" - #include #include +#include #include -#include #include +#include #include +#include "EventHub.h" +#include "InputReaderBase.h" +#include "InputReaderContext.h" + namespace android { class InputDeviceContext; @@ -38,8 +39,7 @@ class InputMapper; class InputDevice { public: InputDevice(InputReaderContext* context, int32_t id, int32_t generation, - int32_t controllerNumber, const InputDeviceIdentifier& identifier, - uint32_t classes); + const InputDeviceIdentifier& identifier); ~InputDevice(); inline InputReaderContext* getContext() { return mContext; } @@ -50,25 +50,25 @@ public: inline const std::string getDescriptor() { return mIdentifier.descriptor; } inline uint32_t getClasses() const { return mClasses; } inline uint32_t getSources() const { return mSources; } + inline bool hasEventHubDevices() const { return !mDevices.empty(); } inline bool isExternal() { return mIsExternal; } - inline void setExternal(bool external) { mIsExternal = external; } inline std::optional getAssociatedDisplayPort() const { return mAssociatedDisplayPort; } inline std::optional getAssociatedViewport() const { return mAssociatedViewport; } - inline void setMic(bool hasMic) { mHasMic = hasMic; } inline bool hasMic() const { return mHasMic; } - inline bool isIgnored() { return mMappers.empty(); } + inline bool isIgnored() { return !getMapperCount(); } bool isEnabled(); void setEnabled(bool enabled, nsecs_t when); void dump(std::string& dump); - void populateMappers(); + void addEventHubDevice(int32_t eventHubId, bool populateMappers = true); + void removeEventHubDevice(int32_t eventHubId); void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); void reset(nsecs_t when); void process(const RawEvent* rawEvents, size_t count); @@ -99,11 +99,20 @@ public: std::optional getAssociatedDisplayId(); + size_t getMapperCount(); + // construct and add a mapper to the input device template - T& addMapper(Args... args) { - T* mapper = new T(*mDeviceContext, args...); - mMappers.emplace_back(mapper); + T& addMapper(int32_t eventHubId, Args... args) { + // ensure a device entry exists for this eventHubId + addEventHubDevice(eventHubId, false); + + // create mapper + auto& devicePair = mDevices[eventHubId]; + auto& deviceContext = devicePair.first; + auto& mappers = devicePair.second; + T* mapper = new T(*deviceContext, args...); + mappers.emplace_back(mapper); return *mapper; } @@ -116,8 +125,10 @@ private: std::string mAlias; uint32_t mClasses; - std::unique_ptr mDeviceContext; - std::vector> mMappers; + // map from eventHubId to device context and mappers + using MapperVector = std::vector>; + using DevicePair = std::pair, MapperVector>; + std::unordered_map mDevices; uint32_t mSources; bool mIsExternal; @@ -131,10 +142,37 @@ private: PropertyMap mConfiguration; - // run a function against every mapper + // helpers to interate over the devices collection + // run a function against every mapper on every subdevice inline void for_each_mapper(std::function f) { - for (auto& mapperPtr : mMappers) { - f(*mapperPtr); + for (auto& deviceEntry : mDevices) { + auto& devicePair = deviceEntry.second; + auto& mappers = devicePair.second; + for (auto& mapperPtr : mappers) { + f(*mapperPtr); + } + } + } + + // run a function against every mapper on a specific subdevice + inline void for_each_mapper_in_subdevice(int32_t eventHubDevice, + std::function f) { + auto deviceIt = mDevices.find(eventHubDevice); + if (deviceIt != mDevices.end()) { + auto& devicePair = deviceIt->second; + auto& mappers = devicePair.second; + for (auto& mapperPtr : mappers) { + f(*mapperPtr); + } + } + } + + // run a function against every subdevice + inline void for_each_subdevice(std::function f) { + for (auto& deviceEntry : mDevices) { + auto& devicePair = deviceEntry.second; + auto& contextPtr = devicePair.first; + f(*contextPtr); } } @@ -142,10 +180,14 @@ private: // if all mappers return nullopt, return nullopt. template inline std::optional first_in_mappers(std::function(InputMapper&)> f) { - for (auto& mapperPtr : mMappers) { - std::optional ret = f(*mapperPtr); - if (ret) { - return ret; + for (auto& deviceEntry : mDevices) { + auto& devicePair = deviceEntry.second; + auto& mappers = devicePair.second; + for (auto& mapperPtr : mappers) { + std::optional ret = f(*mapperPtr); + if (ret) { + return ret; + } } } return std::nullopt; @@ -159,11 +201,12 @@ private: */ class InputDeviceContext { public: - InputDeviceContext(InputDevice& device); + InputDeviceContext(InputDevice& device, int32_t eventHubId); ~InputDeviceContext(); inline InputReaderContext* getContext() { return mContext; } - inline int32_t getId() { return mId; } + inline int32_t getId() { return mDeviceId; } + inline int32_t getEventHubId() { return mId; } inline uint32_t getDeviceClasses() const { return mEventHub->getDeviceClasses(mId); } inline InputDeviceIdentifier getDeviceIdentifier() const { @@ -259,6 +302,7 @@ private: InputReaderContext* mContext; EventHubInterface* mEventHub; int32_t mId; + int32_t mDeviceId; }; } // namespace android diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h index 4f5d2eabf3..31d82f1abe 100644 --- a/services/inputflinger/reader/include/InputReader.h +++ b/services/inputflinger/reader/include/InputReader.h @@ -84,10 +84,8 @@ public: protected: // These members are protected so they can be instrumented by test cases. - virtual std::shared_ptr createDeviceLocked(int32_t deviceId, - int32_t controllerNumber, - const InputDeviceIdentifier& identifier, - uint32_t classes); + virtual std::shared_ptr createDeviceLocked( + int32_t deviceId, const InputDeviceIdentifier& identifier); // With each iteration of the loop, InputReader reads and processes one incoming message from // the EventHub. @@ -139,14 +137,16 @@ private: static const int EVENT_BUFFER_SIZE = 256; RawEvent mEventBuffer[EVENT_BUFFER_SIZE]; - std::unordered_map> mDevices; + // An input device can represent a collection of EventHub devices. This map provides a way + // to lookup the input device instance from the EventHub device id. + std::unordered_map> mDevices; // low-level input event decoding and device management void processEventsLocked(const RawEvent* rawEvents, size_t count); - void addDeviceLocked(nsecs_t when, int32_t deviceId); - void removeDeviceLocked(nsecs_t when, int32_t deviceId); - void processEventsForDeviceLocked(int32_t deviceId, const RawEvent* rawEvents, size_t count); + void addDeviceLocked(nsecs_t when, int32_t eventHubId); + void removeDeviceLocked(nsecs_t when, int32_t eventHubId); + void processEventsForDeviceLocked(int32_t eventHubId, const RawEvent* rawEvents, size_t count); void timeoutExpiredLocked(nsecs_t when); void handleConfigurationChangedLocked(nsecs_t when); @@ -164,6 +164,9 @@ private: int32_t mGeneration; int32_t bumpGenerationLocked(); + int32_t mNextInputDeviceId; + int32_t nextInputDeviceIdLocked(); + void getInputDevicesLocked(std::vector& outInputDevices); nsecs_t mDisableVirtualKeysTimeout; @@ -182,6 +185,9 @@ private: GetStateFunc getStateFunc); bool markSupportedKeyCodesLocked(int32_t deviceId, uint32_t sourceMask, size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags); + + // find an InputDevice from an InputDevice id + InputDevice* findInputDevice(int32_t deviceId); }; } // namespace android diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index d870a01bfa..578605fa40 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -1103,31 +1103,27 @@ public: void setNextDevice(std::shared_ptr device) { mNextDevice = device; } - std::shared_ptr newDevice(int32_t deviceId, int32_t controllerNumber, - const std::string& name, uint32_t classes, + std::shared_ptr newDevice(int32_t deviceId, const std::string& name, const std::string& location = "") { InputDeviceIdentifier identifier; identifier.name = name; identifier.location = location; int32_t generation = deviceId + 1; - return std::make_shared(&mContext, deviceId, generation, controllerNumber, - identifier, classes); + return std::make_shared(&mContext, deviceId, generation, identifier); } // Make the protected loopOnce method accessible to tests. using InputReader::loopOnce; protected: - virtual std::shared_ptr createDeviceLocked(int32_t deviceId, - int32_t controllerNumber, - const InputDeviceIdentifier& identifier, - uint32_t classes) { + virtual std::shared_ptr createDeviceLocked( + int32_t eventHubId, const InputDeviceIdentifier& identifier) { if (mNextDevice) { std::shared_ptr device(mNextDevice); mNextDevice = nullptr; return device; } - return InputReader::createDeviceLocked(deviceId, controllerNumber, identifier, classes); + return InputReader::createDeviceLocked(eventHubId, identifier); } friend class InputReaderTest; @@ -1338,12 +1334,12 @@ protected: mFakePolicy.clear(); } - void addDevice(int32_t deviceId, const std::string& name, uint32_t classes, - const PropertyMap* configuration) { - mFakeEventHub->addDevice(deviceId, name, classes); + void addDevice(int32_t eventHubId, const std::string& name, uint32_t classes, + const PropertyMap* configuration) { + mFakeEventHub->addDevice(eventHubId, name, classes); if (configuration) { - mFakeEventHub->addConfigurationMap(deviceId, configuration); + mFakeEventHub->addConfigurationMap(eventHubId, configuration); } mFakeEventHub->finishDeviceScan(); mReader->loopOnce(); @@ -1362,15 +1358,14 @@ protected: mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_ENABLED_STATE); } - FakeInputMapper& addDeviceWithFakeInputMapper(int32_t deviceId, int32_t controllerNumber, + FakeInputMapper& addDeviceWithFakeInputMapper(int32_t deviceId, int32_t eventHubId, const std::string& name, uint32_t classes, uint32_t sources, const PropertyMap* configuration) { - std::shared_ptr device = - mReader->newDevice(deviceId, controllerNumber, name, classes); - FakeInputMapper& mapper = device->addMapper(sources); + std::shared_ptr device = mReader->newDevice(deviceId, name); + FakeInputMapper& mapper = device->addMapper(eventHubId, sources); mReader->setNextDevice(device); - addDevice(deviceId, name, classes, configuration); + addDevice(eventHubId, name, classes, configuration); return mapper; } }; @@ -1384,7 +1379,7 @@ TEST_F(InputReaderTest, GetInputDevices) { std::vector inputDevices; mReader->getInputDevices(inputDevices); ASSERT_EQ(1U, inputDevices.size()); - ASSERT_EQ(1, inputDevices[0].getId()); + ASSERT_EQ(END_RESERVED_ID + 1, inputDevices[0].getId()); ASSERT_STREQ("keyboard", inputDevices[0].getIdentifier().name.c_str()); ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC, inputDevices[0].getKeyboardType()); ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, inputDevices[0].getSources()); @@ -1393,7 +1388,7 @@ TEST_F(InputReaderTest, GetInputDevices) { // Should also have received a notification describing the new input devices. inputDevices = mFakePolicy->getInputDevices(); ASSERT_EQ(1U, inputDevices.size()); - ASSERT_EQ(1, inputDevices[0].getId()); + ASSERT_EQ(END_RESERVED_ID + 1, inputDevices[0].getId()); ASSERT_STREQ("keyboard", inputDevices[0].getIdentifier().name.c_str()); ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC, inputDevices[0].getKeyboardType()); ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, inputDevices[0].getSources()); @@ -1401,14 +1396,14 @@ TEST_F(InputReaderTest, GetInputDevices) { } TEST_F(InputReaderTest, WhenEnabledChanges_SendsDeviceResetNotification) { - constexpr int32_t deviceId = 1; + constexpr int32_t deviceId = END_RESERVED_ID + 1000; constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD; - std::shared_ptr device = - mReader->newDevice(deviceId, 0 /*controllerNumber*/, "fake", deviceClass); + constexpr int32_t eventHubId = 1; + std::shared_ptr device = mReader->newDevice(deviceId, "fake"); // Must add at least one mapper or the device will be ignored! - device->addMapper(AINPUT_SOURCE_KEYBOARD); + device->addMapper(eventHubId, AINPUT_SOURCE_KEYBOARD); mReader->setNextDevice(device); - ASSERT_NO_FATAL_FAILURE(addDevice(deviceId, "fake", deviceClass, nullptr)); + ASSERT_NO_FATAL_FAILURE(addDevice(eventHubId, "fake", deviceClass, nullptr)); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasCalled(nullptr)); @@ -1438,8 +1433,11 @@ TEST_F(InputReaderTest, WhenEnabledChanges_SendsDeviceResetNotification) { } TEST_F(InputReaderTest, GetKeyCodeState_ForwardsRequestsToMappers) { + constexpr int32_t deviceId = END_RESERVED_ID + 1000; + constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD; + constexpr int32_t eventHubId = 1; FakeInputMapper& mapper = - addDeviceWithFakeInputMapper(1, 0, "fake", INPUT_DEVICE_CLASS_KEYBOARD, + addDeviceWithFakeInputMapper(deviceId, eventHubId, "fake", deviceClass, AINPUT_SOURCE_KEYBOARD, nullptr); mapper.setKeyCodeState(AKEYCODE_A, AKEY_STATE_DOWN); @@ -1447,13 +1445,16 @@ TEST_F(InputReaderTest, GetKeyCodeState_ForwardsRequestsToMappers) { AINPUT_SOURCE_ANY, AKEYCODE_A)) << "Should return unknown when the device id is >= 0 but unknown."; - ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getKeyCodeState(1, - AINPUT_SOURCE_TRACKBALL, AKEYCODE_A)) - << "Should return unknown when the device id is valid but the sources are not supported by the device."; + ASSERT_EQ(AKEY_STATE_UNKNOWN, + mReader->getKeyCodeState(deviceId, AINPUT_SOURCE_TRACKBALL, AKEYCODE_A)) + << "Should return unknown when the device id is valid but the sources are not " + "supported by the device."; - ASSERT_EQ(AKEY_STATE_DOWN, mReader->getKeyCodeState(1, - AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TRACKBALL, AKEYCODE_A)) - << "Should return value provided by mapper when device id is valid and the device supports some of the sources."; + ASSERT_EQ(AKEY_STATE_DOWN, + mReader->getKeyCodeState(deviceId, AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TRACKBALL, + AKEYCODE_A)) + << "Should return value provided by mapper when device id is valid and the device " + "supports some of the sources."; ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getKeyCodeState(-1, AINPUT_SOURCE_TRACKBALL, AKEYCODE_A)) @@ -1465,8 +1466,11 @@ TEST_F(InputReaderTest, GetKeyCodeState_ForwardsRequestsToMappers) { } TEST_F(InputReaderTest, GetScanCodeState_ForwardsRequestsToMappers) { + constexpr int32_t deviceId = END_RESERVED_ID + 1000; + constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD; + constexpr int32_t eventHubId = 1; FakeInputMapper& mapper = - addDeviceWithFakeInputMapper(1, 0, "fake", INPUT_DEVICE_CLASS_KEYBOARD, + addDeviceWithFakeInputMapper(deviceId, eventHubId, "fake", deviceClass, AINPUT_SOURCE_KEYBOARD, nullptr); mapper.setScanCodeState(KEY_A, AKEY_STATE_DOWN); @@ -1474,13 +1478,16 @@ TEST_F(InputReaderTest, GetScanCodeState_ForwardsRequestsToMappers) { AINPUT_SOURCE_ANY, KEY_A)) << "Should return unknown when the device id is >= 0 but unknown."; - ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getScanCodeState(1, - AINPUT_SOURCE_TRACKBALL, KEY_A)) - << "Should return unknown when the device id is valid but the sources are not supported by the device."; + ASSERT_EQ(AKEY_STATE_UNKNOWN, + mReader->getScanCodeState(deviceId, AINPUT_SOURCE_TRACKBALL, KEY_A)) + << "Should return unknown when the device id is valid but the sources are not " + "supported by the device."; - ASSERT_EQ(AKEY_STATE_DOWN, mReader->getScanCodeState(1, - AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TRACKBALL, KEY_A)) - << "Should return value provided by mapper when device id is valid and the device supports some of the sources."; + ASSERT_EQ(AKEY_STATE_DOWN, + mReader->getScanCodeState(deviceId, AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TRACKBALL, + KEY_A)) + << "Should return value provided by mapper when device id is valid and the device " + "supports some of the sources."; ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getScanCodeState(-1, AINPUT_SOURCE_TRACKBALL, KEY_A)) @@ -1492,8 +1499,11 @@ TEST_F(InputReaderTest, GetScanCodeState_ForwardsRequestsToMappers) { } TEST_F(InputReaderTest, GetSwitchState_ForwardsRequestsToMappers) { + constexpr int32_t deviceId = END_RESERVED_ID + 1000; + constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD; + constexpr int32_t eventHubId = 1; FakeInputMapper& mapper = - addDeviceWithFakeInputMapper(1, 0, "fake", INPUT_DEVICE_CLASS_KEYBOARD, + addDeviceWithFakeInputMapper(deviceId, eventHubId, "fake", deviceClass, AINPUT_SOURCE_KEYBOARD, nullptr); mapper.setSwitchState(SW_LID, AKEY_STATE_DOWN); @@ -1501,13 +1511,16 @@ TEST_F(InputReaderTest, GetSwitchState_ForwardsRequestsToMappers) { AINPUT_SOURCE_ANY, SW_LID)) << "Should return unknown when the device id is >= 0 but unknown."; - ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getSwitchState(1, - AINPUT_SOURCE_TRACKBALL, SW_LID)) - << "Should return unknown when the device id is valid but the sources are not supported by the device."; + ASSERT_EQ(AKEY_STATE_UNKNOWN, + mReader->getSwitchState(deviceId, AINPUT_SOURCE_TRACKBALL, SW_LID)) + << "Should return unknown when the device id is valid but the sources are not " + "supported by the device."; - ASSERT_EQ(AKEY_STATE_DOWN, mReader->getSwitchState(1, - AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TRACKBALL, SW_LID)) - << "Should return value provided by mapper when device id is valid and the device supports some of the sources."; + ASSERT_EQ(AKEY_STATE_DOWN, + mReader->getSwitchState(deviceId, AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TRACKBALL, + SW_LID)) + << "Should return value provided by mapper when device id is valid and the device " + "supports some of the sources."; ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getSwitchState(-1, AINPUT_SOURCE_TRACKBALL, SW_LID)) @@ -1519,8 +1532,11 @@ TEST_F(InputReaderTest, GetSwitchState_ForwardsRequestsToMappers) { } TEST_F(InputReaderTest, MarkSupportedKeyCodes_ForwardsRequestsToMappers) { + constexpr int32_t deviceId = END_RESERVED_ID + 1000; + constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD; + constexpr int32_t eventHubId = 1; FakeInputMapper& mapper = - addDeviceWithFakeInputMapper(1, 0, "fake", INPUT_DEVICE_CLASS_KEYBOARD, + addDeviceWithFakeInputMapper(deviceId, eventHubId, "fake", deviceClass, AINPUT_SOURCE_KEYBOARD, nullptr); mapper.addSupportedKeyCode(AKEYCODE_A); @@ -1534,13 +1550,16 @@ TEST_F(InputReaderTest, MarkSupportedKeyCodes_ForwardsRequestsToMappers) { ASSERT_TRUE(!flags[0] && !flags[1] && !flags[2] && !flags[3]); flags[3] = 1; - ASSERT_FALSE(mReader->hasKeys(1, AINPUT_SOURCE_TRACKBALL, 4, keyCodes, flags)) - << "Should return false when device id is valid but the sources are not supported by the device."; + ASSERT_FALSE(mReader->hasKeys(deviceId, AINPUT_SOURCE_TRACKBALL, 4, keyCodes, flags)) + << "Should return false when device id is valid but the sources are not supported by " + "the device."; ASSERT_TRUE(!flags[0] && !flags[1] && !flags[2] && !flags[3]); flags[3] = 1; - ASSERT_TRUE(mReader->hasKeys(1, AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TRACKBALL, 4, keyCodes, flags)) - << "Should return value provided by mapper when device id is valid and the device supports some of the sources."; + ASSERT_TRUE(mReader->hasKeys(deviceId, AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TRACKBALL, 4, + keyCodes, flags)) + << "Should return value provided by mapper when device id is valid and the device " + "supports some of the sources."; ASSERT_TRUE(flags[0] && flags[1] && !flags[2] && !flags[3]); flags[3] = 1; @@ -1555,7 +1574,8 @@ TEST_F(InputReaderTest, MarkSupportedKeyCodes_ForwardsRequestsToMappers) { } TEST_F(InputReaderTest, LoopOnce_WhenDeviceScanFinished_SendsConfigurationChanged) { - addDevice(1, "ignored", INPUT_DEVICE_CLASS_KEYBOARD, nullptr); + constexpr int32_t eventHubId = 1; + addDevice(eventHubId, "ignored", INPUT_DEVICE_CLASS_KEYBOARD, nullptr); NotifyConfigurationChangedArgs args; @@ -1564,32 +1584,35 @@ TEST_F(InputReaderTest, LoopOnce_WhenDeviceScanFinished_SendsConfigurationChange } TEST_F(InputReaderTest, LoopOnce_ForwardsRawEventsToMappers) { + constexpr int32_t deviceId = END_RESERVED_ID + 1000; + constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD; + constexpr int32_t eventHubId = 1; FakeInputMapper& mapper = - addDeviceWithFakeInputMapper(1, 0, "fake", INPUT_DEVICE_CLASS_KEYBOARD, + addDeviceWithFakeInputMapper(deviceId, eventHubId, "fake", deviceClass, AINPUT_SOURCE_KEYBOARD, nullptr); - mFakeEventHub->enqueueEvent(0, 1, EV_KEY, KEY_A, 1); + mFakeEventHub->enqueueEvent(0, eventHubId, EV_KEY, KEY_A, 1); mReader->loopOnce(); ASSERT_NO_FATAL_FAILURE(mFakeEventHub->assertQueueIsEmpty()); RawEvent event; ASSERT_NO_FATAL_FAILURE(mapper.assertProcessWasCalled(&event)); ASSERT_EQ(0, event.when); - ASSERT_EQ(1, event.deviceId); + ASSERT_EQ(eventHubId, event.deviceId); ASSERT_EQ(EV_KEY, event.type); ASSERT_EQ(KEY_A, event.code); ASSERT_EQ(1, event.value); } TEST_F(InputReaderTest, DeviceReset_IncrementsSequenceNumber) { - constexpr int32_t deviceId = 1; + constexpr int32_t deviceId = END_RESERVED_ID + 1000; constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD; - std::shared_ptr device = - mReader->newDevice(deviceId, 0 /*controllerNumber*/, "fake", deviceClass); + constexpr int32_t eventHubId = 1; + std::shared_ptr device = mReader->newDevice(deviceId, "fake"); // Must add at least one mapper or the device will be ignored! - device->addMapper(AINPUT_SOURCE_KEYBOARD); + device->addMapper(eventHubId, AINPUT_SOURCE_KEYBOARD); mReader->setNextDevice(device); - ASSERT_NO_FATAL_FAILURE(addDevice(deviceId, "fake", deviceClass, nullptr)); + ASSERT_NO_FATAL_FAILURE(addDevice(eventHubId, "fake", deviceClass, nullptr)); NotifyDeviceResetArgs resetArgs; ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs)); @@ -1615,12 +1638,13 @@ TEST_F(InputReaderTest, DeviceReset_IncrementsSequenceNumber) { } TEST_F(InputReaderTest, Device_CanDispatchToDisplay) { - constexpr int32_t deviceId = 1; + constexpr int32_t deviceId = END_RESERVED_ID + 1000; constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD; + constexpr int32_t eventHubId = 1; const char* DEVICE_LOCATION = "USB1"; - std::shared_ptr device = mReader->newDevice(deviceId, 0 /*controllerNumber*/, - "fake", deviceClass, DEVICE_LOCATION); - FakeInputMapper& mapper = device->addMapper(AINPUT_SOURCE_TOUCHSCREEN); + std::shared_ptr device = mReader->newDevice(deviceId, "fake", DEVICE_LOCATION); + FakeInputMapper& mapper = + device->addMapper(eventHubId, AINPUT_SOURCE_TOUCHSCREEN); mReader->setNextDevice(device); const uint8_t hdmi1 = 1; @@ -1640,7 +1664,7 @@ TEST_F(InputReaderTest, Device_CanDispatchToDisplay) { // Add the device, and make sure all of the callbacks are triggered. // The device is added after the input port associations are processed since // we do not yet support dynamic device-to-display associations. - ASSERT_NO_FATAL_FAILURE(addDevice(deviceId, "fake", deviceClass, nullptr)); + ASSERT_NO_FATAL_FAILURE(addDevice(eventHubId, "fake", deviceClass, nullptr)); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasCalled()); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled()); ASSERT_NO_FATAL_FAILURE(mapper.assertConfigureWasCalled()); @@ -1666,6 +1690,7 @@ protected: static const int32_t DEVICE_GENERATION; static const int32_t DEVICE_CONTROLLER_NUMBER; static const uint32_t DEVICE_CLASSES; + static const int32_t EVENTHUB_ID; std::shared_ptr mFakeEventHub; sp mFakePolicy; @@ -1680,13 +1705,12 @@ protected: mFakeListener = new TestInputListener(); mFakeContext = new FakeInputReaderContext(mFakeEventHub, mFakePolicy, mFakeListener); - mFakeEventHub->addDevice(DEVICE_ID, DEVICE_NAME, 0); + mFakeEventHub->addDevice(EVENTHUB_ID, DEVICE_NAME, 0); InputDeviceIdentifier identifier; identifier.name = DEVICE_NAME; identifier.location = DEVICE_LOCATION; - mDevice = - std::make_shared(mFakeContext, DEVICE_ID, DEVICE_GENERATION, - DEVICE_CONTROLLER_NUMBER, identifier, DEVICE_CLASSES); + mDevice = std::make_shared(mFakeContext, DEVICE_ID, DEVICE_GENERATION, + identifier); } virtual void TearDown() override { @@ -1699,20 +1723,21 @@ protected: const char* InputDeviceTest::DEVICE_NAME = "device"; const char* InputDeviceTest::DEVICE_LOCATION = "USB1"; -const int32_t InputDeviceTest::DEVICE_ID = 1; +const int32_t InputDeviceTest::DEVICE_ID = END_RESERVED_ID + 1000; const int32_t InputDeviceTest::DEVICE_GENERATION = 2; const int32_t InputDeviceTest::DEVICE_CONTROLLER_NUMBER = 0; const uint32_t InputDeviceTest::DEVICE_CLASSES = INPUT_DEVICE_CLASS_KEYBOARD | INPUT_DEVICE_CLASS_TOUCH | INPUT_DEVICE_CLASS_JOYSTICK; +const int32_t InputDeviceTest::EVENTHUB_ID = 1; TEST_F(InputDeviceTest, ImmutableProperties) { ASSERT_EQ(DEVICE_ID, mDevice->getId()); ASSERT_STREQ(DEVICE_NAME, mDevice->getName().c_str()); - ASSERT_EQ(DEVICE_CLASSES, mDevice->getClasses()); + ASSERT_EQ(0U, mDevice->getClasses()); } -TEST_F(InputDeviceTest, WhenDeviceCreated_EnabledIsTrue) { - ASSERT_EQ(mDevice->isEnabled(), true); +TEST_F(InputDeviceTest, WhenDeviceCreated_EnabledIsFalse) { + ASSERT_EQ(mDevice->isEnabled(), false); } TEST_F(InputDeviceTest, WhenNoMappersAreRegistered_DeviceIsIgnored) { @@ -1759,9 +1784,10 @@ TEST_F(InputDeviceTest, WhenNoMappersAreRegistered_DeviceIsIgnored) { TEST_F(InputDeviceTest, WhenMappersAreRegistered_DeviceIsNotIgnoredAndForwardsRequestsToMappers) { // Configuration. - mFakeEventHub->addConfigurationProperty(DEVICE_ID, String8("key"), String8("value")); + mFakeEventHub->addConfigurationProperty(EVENTHUB_ID, String8("key"), String8("value")); - FakeInputMapper& mapper1 = mDevice->addMapper(AINPUT_SOURCE_KEYBOARD); + FakeInputMapper& mapper1 = + mDevice->addMapper(EVENTHUB_ID, AINPUT_SOURCE_KEYBOARD); mapper1.setKeyboardType(AINPUT_KEYBOARD_TYPE_ALPHABETIC); mapper1.setMetaState(AMETA_ALT_ON); mapper1.addSupportedKeyCode(AKEYCODE_A); @@ -1772,7 +1798,8 @@ TEST_F(InputDeviceTest, WhenMappersAreRegistered_DeviceIsNotIgnoredAndForwardsRe mapper1.setScanCodeState(3, AKEY_STATE_UP); mapper1.setSwitchState(4, AKEY_STATE_DOWN); - FakeInputMapper& mapper2 = mDevice->addMapper(AINPUT_SOURCE_TOUCHSCREEN); + FakeInputMapper& mapper2 = + mDevice->addMapper(EVENTHUB_ID, AINPUT_SOURCE_TOUCHSCREEN); mapper2.setMetaState(AMETA_SHIFT_ON); InputReaderConfiguration config; @@ -1843,6 +1870,7 @@ TEST_F(InputDeviceTest, WhenMappersAreRegistered_DeviceIsNotIgnoredAndForwardsRe // Event handling. RawEvent event; + event.deviceId = EVENTHUB_ID; mDevice->process(&event, 1); ASSERT_NO_FATAL_FAILURE(mapper1.assertProcessWasCalled()); @@ -1853,7 +1881,7 @@ TEST_F(InputDeviceTest, WhenMappersAreRegistered_DeviceIsNotIgnoredAndForwardsRe // 1. Device is disabled if the viewport corresponding to the associated display is not found // 2. Device is disabled when setEnabled API is called TEST_F(InputDeviceTest, Configure_AssignsDisplayPort) { - mDevice->addMapper(AINPUT_SOURCE_TOUCHSCREEN); + mDevice->addMapper(EVENTHUB_ID, AINPUT_SOURCE_TOUCHSCREEN); // First Configuration. mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0); @@ -1902,6 +1930,7 @@ protected: static const int32_t DEVICE_GENERATION; static const int32_t DEVICE_CONTROLLER_NUMBER; static const uint32_t DEVICE_CLASSES; + static const int32_t EVENTHUB_ID; std::shared_ptr mFakeEventHub; sp mFakePolicy; @@ -1909,7 +1938,7 @@ protected: FakeInputReaderContext* mFakeContext; InputDevice* mDevice; - virtual void SetUp() override { + virtual void SetUp(uint32_t classes) { mFakeEventHub = std::make_unique(); mFakePolicy = new FakeInputReaderPolicy(); mFakeListener = new TestInputListener(); @@ -1917,12 +1946,13 @@ protected: InputDeviceIdentifier identifier; identifier.name = DEVICE_NAME; identifier.location = DEVICE_LOCATION; - mDevice = new InputDevice(mFakeContext, DEVICE_ID, DEVICE_GENERATION, - DEVICE_CONTROLLER_NUMBER, identifier, DEVICE_CLASSES); + mDevice = new InputDevice(mFakeContext, DEVICE_ID, DEVICE_GENERATION, identifier); - mFakeEventHub->addDevice(mDevice->getId(), DEVICE_NAME, 0); + mFakeEventHub->addDevice(EVENTHUB_ID, DEVICE_NAME, classes); } + virtual void SetUp() override { SetUp(DEVICE_CLASSES); } + virtual void TearDown() override { delete mDevice; delete mFakeContext; @@ -1931,7 +1961,7 @@ protected: } void addConfigurationProperty(const char* key, const char* value) { - mFakeEventHub->addConfigurationProperty(mDevice->getId(), String8(key), String8(value)); + mFakeEventHub->addConfigurationProperty(EVENTHUB_ID, String8(key), String8(value)); } void configureDevice(uint32_t changes) { @@ -1940,7 +1970,7 @@ protected: template T& addMapperAndConfigure(Args... args) { - T& mapper = mDevice->addMapper(args...); + T& mapper = mDevice->addMapper(EVENTHUB_ID, args...); configureDevice(0); mDevice->reset(ARBITRARY_TIME); return mapper; @@ -1962,7 +1992,7 @@ protected: int32_t value) { RawEvent event; event.when = when; - event.deviceId = mapper.getDeviceId(); + event.deviceId = mapper.getDeviceContext().getEventHubId(); event.type = type; event.code = code; event.value = value; @@ -2007,11 +2037,11 @@ protected: const char* InputMapperTest::DEVICE_NAME = "device"; const char* InputMapperTest::DEVICE_LOCATION = "USB1"; -const int32_t InputMapperTest::DEVICE_ID = 1; +const int32_t InputMapperTest::DEVICE_ID = END_RESERVED_ID + 1000; const int32_t InputMapperTest::DEVICE_GENERATION = 2; const int32_t InputMapperTest::DEVICE_CONTROLLER_NUMBER = 0; const uint32_t InputMapperTest::DEVICE_CLASSES = 0; // not needed for current tests - +const int32_t InputMapperTest::EVENTHUB_ID = 1; // --- SwitchInputMapperTest --- @@ -2028,10 +2058,10 @@ TEST_F(SwitchInputMapperTest, GetSources) { TEST_F(SwitchInputMapperTest, GetSwitchState) { SwitchInputMapper& mapper = addMapperAndConfigure(); - mFakeEventHub->setSwitchState(DEVICE_ID, SW_LID, 1); + mFakeEventHub->setSwitchState(EVENTHUB_ID, SW_LID, 1); ASSERT_EQ(1, mapper.getSwitchState(AINPUT_SOURCE_ANY, SW_LID)); - mFakeEventHub->setSwitchState(DEVICE_ID, SW_LID, 0); + mFakeEventHub->setSwitchState(EVENTHUB_ID, SW_LID, 0); ASSERT_EQ(0, mapper.getSwitchState(AINPUT_SOURCE_ANY, SW_LID)); } @@ -2105,8 +2135,8 @@ TEST_F(KeyboardInputMapperTest, GetSources) { TEST_F(KeyboardInputMapperTest, Process_SimpleKeyPress) { const int32_t USAGE_A = 0x070004; const int32_t USAGE_UNKNOWN = 0x07ffff; - mFakeEventHub->addKey(DEVICE_ID, KEY_HOME, 0, AKEYCODE_HOME, POLICY_FLAG_WAKE); - mFakeEventHub->addKey(DEVICE_ID, 0, USAGE_A, AKEYCODE_A, POLICY_FLAG_WAKE); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_HOME, 0, AKEYCODE_HOME, POLICY_FLAG_WAKE); + mFakeEventHub->addKey(EVENTHUB_ID, 0, USAGE_A, AKEYCODE_A, POLICY_FLAG_WAKE); KeyboardInputMapper& mapper = addMapperAndConfigure(AINPUT_SOURCE_KEYBOARD, @@ -2203,8 +2233,8 @@ TEST_F(KeyboardInputMapperTest, Process_SimpleKeyPress) { } TEST_F(KeyboardInputMapperTest, Process_ShouldUpdateMetaState) { - mFakeEventHub->addKey(DEVICE_ID, KEY_LEFTSHIFT, 0, AKEYCODE_SHIFT_LEFT, 0); - mFakeEventHub->addKey(DEVICE_ID, KEY_A, 0, AKEYCODE_A, 0); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_LEFTSHIFT, 0, AKEYCODE_SHIFT_LEFT, 0); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_A, 0, AKEYCODE_A, 0); KeyboardInputMapper& mapper = addMapperAndConfigure(AINPUT_SOURCE_KEYBOARD, @@ -2242,10 +2272,10 @@ TEST_F(KeyboardInputMapperTest, Process_ShouldUpdateMetaState) { } TEST_F(KeyboardInputMapperTest, Process_WhenNotOrientationAware_ShouldNotRotateDPad) { - mFakeEventHub->addKey(DEVICE_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0); - mFakeEventHub->addKey(DEVICE_ID, KEY_RIGHT, 0, AKEYCODE_DPAD_RIGHT, 0); - mFakeEventHub->addKey(DEVICE_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0); - mFakeEventHub->addKey(DEVICE_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_RIGHT, 0, AKEYCODE_DPAD_RIGHT, 0); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0); KeyboardInputMapper& mapper = addMapperAndConfigure(AINPUT_SOURCE_KEYBOARD, @@ -2263,10 +2293,10 @@ TEST_F(KeyboardInputMapperTest, Process_WhenNotOrientationAware_ShouldNotRotateD } TEST_F(KeyboardInputMapperTest, Process_WhenOrientationAware_ShouldRotateDPad) { - mFakeEventHub->addKey(DEVICE_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0); - mFakeEventHub->addKey(DEVICE_ID, KEY_RIGHT, 0, AKEYCODE_DPAD_RIGHT, 0); - mFakeEventHub->addKey(DEVICE_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0); - mFakeEventHub->addKey(DEVICE_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_RIGHT, 0, AKEYCODE_DPAD_RIGHT, 0); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0); addConfigurationProperty("keyboard.orientationAware", "1"); KeyboardInputMapper& mapper = @@ -2339,7 +2369,7 @@ TEST_F(KeyboardInputMapperTest, Process_WhenOrientationAware_ShouldRotateDPad) { TEST_F(KeyboardInputMapperTest, DisplayIdConfigurationChange_NotOrientationAware) { // If the keyboard is not orientation aware, // key events should not be associated with a specific display id - mFakeEventHub->addKey(DEVICE_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0); KeyboardInputMapper& mapper = addMapperAndConfigure(AINPUT_SOURCE_KEYBOARD, @@ -2364,7 +2394,7 @@ TEST_F(KeyboardInputMapperTest, DisplayIdConfigurationChange_NotOrientationAware TEST_F(KeyboardInputMapperTest, DisplayIdConfigurationChange_OrientationAware) { // If the keyboard is orientation aware, // key events should be associated with the internal viewport - mFakeEventHub->addKey(DEVICE_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0); addConfigurationProperty("keyboard.orientationAware", "1"); KeyboardInputMapper& mapper = @@ -2399,10 +2429,10 @@ TEST_F(KeyboardInputMapperTest, GetKeyCodeState) { addMapperAndConfigure(AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC); - mFakeEventHub->setKeyCodeState(DEVICE_ID, AKEYCODE_A, 1); + mFakeEventHub->setKeyCodeState(EVENTHUB_ID, AKEYCODE_A, 1); ASSERT_EQ(1, mapper.getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_A)); - mFakeEventHub->setKeyCodeState(DEVICE_ID, AKEYCODE_A, 0); + mFakeEventHub->setKeyCodeState(EVENTHUB_ID, AKEYCODE_A, 0); ASSERT_EQ(0, mapper.getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_A)); } @@ -2411,10 +2441,10 @@ TEST_F(KeyboardInputMapperTest, GetScanCodeState) { addMapperAndConfigure(AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC); - mFakeEventHub->setScanCodeState(DEVICE_ID, KEY_A, 1); + mFakeEventHub->setScanCodeState(EVENTHUB_ID, KEY_A, 1); ASSERT_EQ(1, mapper.getScanCodeState(AINPUT_SOURCE_ANY, KEY_A)); - mFakeEventHub->setScanCodeState(DEVICE_ID, KEY_A, 0); + mFakeEventHub->setScanCodeState(EVENTHUB_ID, KEY_A, 0); ASSERT_EQ(0, mapper.getScanCodeState(AINPUT_SOURCE_ANY, KEY_A)); } @@ -2423,7 +2453,7 @@ TEST_F(KeyboardInputMapperTest, MarkSupportedKeyCodes) { addMapperAndConfigure(AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC); - mFakeEventHub->addKey(DEVICE_ID, KEY_A, 0, AKEYCODE_A, 0); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_A, 0, AKEYCODE_A, 0); const int32_t keyCodes[2] = { AKEYCODE_A, AKEYCODE_B }; uint8_t flags[2] = { 0, 0 }; @@ -2433,99 +2463,100 @@ TEST_F(KeyboardInputMapperTest, MarkSupportedKeyCodes) { } TEST_F(KeyboardInputMapperTest, Process_LockedKeysShouldToggleMetaStateAndLeds) { - mFakeEventHub->addLed(DEVICE_ID, LED_CAPSL, true /*initially on*/); - mFakeEventHub->addLed(DEVICE_ID, LED_NUML, false /*initially off*/); - mFakeEventHub->addLed(DEVICE_ID, LED_SCROLLL, false /*initially off*/); - mFakeEventHub->addKey(DEVICE_ID, KEY_CAPSLOCK, 0, AKEYCODE_CAPS_LOCK, 0); - mFakeEventHub->addKey(DEVICE_ID, KEY_NUMLOCK, 0, AKEYCODE_NUM_LOCK, 0); - mFakeEventHub->addKey(DEVICE_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0); + mFakeEventHub->addLed(EVENTHUB_ID, LED_CAPSL, true /*initially on*/); + mFakeEventHub->addLed(EVENTHUB_ID, LED_NUML, false /*initially off*/); + mFakeEventHub->addLed(EVENTHUB_ID, LED_SCROLLL, false /*initially off*/); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_CAPSLOCK, 0, AKEYCODE_CAPS_LOCK, 0); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_NUMLOCK, 0, AKEYCODE_NUM_LOCK, 0); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0); KeyboardInputMapper& mapper = addMapperAndConfigure(AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC); // Initialization should have turned all of the lights off. - ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_CAPSL)); - ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_NUML)); - ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_SCROLLL)); + ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL)); + ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML)); + ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL)); // Toggle caps lock on. process(mapper, ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 1); process(mapper, ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 0); - ASSERT_TRUE(mFakeEventHub->getLedState(DEVICE_ID, LED_CAPSL)); - ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_NUML)); - ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_SCROLLL)); + ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL)); + ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML)); + ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL)); ASSERT_EQ(AMETA_CAPS_LOCK_ON, mapper.getMetaState()); // Toggle num lock on. process(mapper, ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 1); process(mapper, ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 0); - ASSERT_TRUE(mFakeEventHub->getLedState(DEVICE_ID, LED_CAPSL)); - ASSERT_TRUE(mFakeEventHub->getLedState(DEVICE_ID, LED_NUML)); - ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_SCROLLL)); + ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL)); + ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML)); + ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL)); ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON, mapper.getMetaState()); // Toggle caps lock off. process(mapper, ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 1); process(mapper, ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 0); - ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_CAPSL)); - ASSERT_TRUE(mFakeEventHub->getLedState(DEVICE_ID, LED_NUML)); - ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_SCROLLL)); + ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL)); + ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML)); + ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL)); ASSERT_EQ(AMETA_NUM_LOCK_ON, mapper.getMetaState()); // Toggle scroll lock on. process(mapper, ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 1); process(mapper, ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 0); - ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_CAPSL)); - ASSERT_TRUE(mFakeEventHub->getLedState(DEVICE_ID, LED_NUML)); - ASSERT_TRUE(mFakeEventHub->getLedState(DEVICE_ID, LED_SCROLLL)); + ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL)); + ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML)); + ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL)); ASSERT_EQ(AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON, mapper.getMetaState()); // Toggle num lock off. process(mapper, ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 1); process(mapper, ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 0); - ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_CAPSL)); - ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_NUML)); - ASSERT_TRUE(mFakeEventHub->getLedState(DEVICE_ID, LED_SCROLLL)); + ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL)); + ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML)); + ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL)); ASSERT_EQ(AMETA_SCROLL_LOCK_ON, mapper.getMetaState()); // Toggle scroll lock off. process(mapper, ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 1); process(mapper, ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 0); - ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_CAPSL)); - ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_NUML)); - ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_SCROLLL)); + ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL)); + ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML)); + ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL)); ASSERT_EQ(AMETA_NONE, mapper.getMetaState()); } TEST_F(KeyboardInputMapperTest, Configure_AssignsDisplayPort) { // keyboard 1. - mFakeEventHub->addKey(DEVICE_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0); - mFakeEventHub->addKey(DEVICE_ID, KEY_RIGHT, 0, AKEYCODE_DPAD_RIGHT, 0); - mFakeEventHub->addKey(DEVICE_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0); - mFakeEventHub->addKey(DEVICE_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_RIGHT, 0, AKEYCODE_DPAD_RIGHT, 0); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0); // keyboard 2. const std::string USB2 = "USB2"; constexpr int32_t SECOND_DEVICE_ID = DEVICE_ID + 1; + constexpr int32_t SECOND_EVENTHUB_ID = EVENTHUB_ID + 1; InputDeviceIdentifier identifier; identifier.name = "KEYBOARD2"; identifier.location = USB2; std::unique_ptr device2 = std::make_unique(mFakeContext, SECOND_DEVICE_ID, DEVICE_GENERATION, - DEVICE_CONTROLLER_NUMBER, identifier, DEVICE_CLASSES); - mFakeEventHub->addDevice(SECOND_DEVICE_ID, DEVICE_NAME, 0 /*classes*/); - mFakeEventHub->addKey(SECOND_DEVICE_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0); - mFakeEventHub->addKey(SECOND_DEVICE_ID, KEY_RIGHT, 0, AKEYCODE_DPAD_RIGHT, 0); - mFakeEventHub->addKey(SECOND_DEVICE_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0); - mFakeEventHub->addKey(SECOND_DEVICE_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0); + identifier); + mFakeEventHub->addDevice(SECOND_EVENTHUB_ID, DEVICE_NAME, 0 /*classes*/); + mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0); + mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_RIGHT, 0, AKEYCODE_DPAD_RIGHT, 0); + mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0); + mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0); KeyboardInputMapper& mapper = addMapperAndConfigure(AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC); KeyboardInputMapper& mapper2 = - device2->addMapper(AINPUT_SOURCE_KEYBOARD, + device2->addMapper(SECOND_EVENTHUB_ID, AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC); device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0 /*changes*/); device2->reset(ARBITRARY_TIME); @@ -2577,14 +2608,23 @@ TEST_F(KeyboardInputMapperTest, Configure_AssignsDisplayPort) { AKEYCODE_DPAD_LEFT, newDisplayId)); } -TEST_F(KeyboardInputMapperTest, ExternalDevice_WakeBehavior) { +// --- KeyboardInputMapperTest_ExternalDevice --- + +class KeyboardInputMapperTest_ExternalDevice : public InputMapperTest { +protected: + virtual void SetUp() override { + InputMapperTest::SetUp(DEVICE_CLASSES | INPUT_DEVICE_CLASS_EXTERNAL); + } +}; + +TEST_F(KeyboardInputMapperTest_ExternalDevice, WakeBehavior) { // For external devices, non-media keys will trigger wake on key down. Media keys need to be // marked as WAKE in the keylayout file to trigger wake. - mDevice->setExternal(true); - mFakeEventHub->addKey(DEVICE_ID, KEY_HOME, 0, AKEYCODE_HOME, 0); - mFakeEventHub->addKey(DEVICE_ID, KEY_PLAY, 0, AKEYCODE_MEDIA_PLAY, 0); - mFakeEventHub->addKey(DEVICE_ID, KEY_PLAYPAUSE, 0, AKEYCODE_MEDIA_PLAY_PAUSE, POLICY_FLAG_WAKE); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_HOME, 0, AKEYCODE_HOME, 0); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_PLAY, 0, AKEYCODE_MEDIA_PLAY, 0); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_PLAYPAUSE, 0, AKEYCODE_MEDIA_PLAY_PAUSE, + POLICY_FLAG_WAKE); KeyboardInputMapper& mapper = addMapperAndConfigure(AINPUT_SOURCE_KEYBOARD, @@ -2616,13 +2656,12 @@ TEST_F(KeyboardInputMapperTest, ExternalDevice_WakeBehavior) { ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags); } -TEST_F(KeyboardInputMapperTest, ExternalDevice_DoNotWakeByDefaultBehavior) { +TEST_F(KeyboardInputMapperTest_ExternalDevice, DoNotWakeByDefaultBehavior) { // Tv Remote key's wake behavior is prescribed by the keylayout file. - mDevice->setExternal(true); - mFakeEventHub->addKey(DEVICE_ID, KEY_HOME, 0, AKEYCODE_HOME, POLICY_FLAG_WAKE); - mFakeEventHub->addKey(DEVICE_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0); - mFakeEventHub->addKey(DEVICE_ID, KEY_PLAY, 0, AKEYCODE_MEDIA_PLAY, POLICY_FLAG_WAKE); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_HOME, 0, AKEYCODE_HOME, POLICY_FLAG_WAKE); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_PLAY, 0, AKEYCODE_MEDIA_PLAY, POLICY_FLAG_WAKE); addConfigurationProperty("keyboard.doNotWakeByDefault", "1"); KeyboardInputMapper& mapper = @@ -3564,10 +3603,10 @@ void TouchInputMapperTest::prepareVirtualDisplay(int32_t orientation) { } void TouchInputMapperTest::prepareVirtualKeys() { - mFakeEventHub->addVirtualKeyDefinition(DEVICE_ID, VIRTUAL_KEYS[0]); - mFakeEventHub->addVirtualKeyDefinition(DEVICE_ID, VIRTUAL_KEYS[1]); - mFakeEventHub->addKey(DEVICE_ID, KEY_HOME, 0, AKEYCODE_HOME, POLICY_FLAG_WAKE); - mFakeEventHub->addKey(DEVICE_ID, KEY_MENU, 0, AKEYCODE_MENU, POLICY_FLAG_WAKE); + mFakeEventHub->addVirtualKeyDefinition(EVENTHUB_ID, VIRTUAL_KEYS[0]); + mFakeEventHub->addVirtualKeyDefinition(EVENTHUB_ID, VIRTUAL_KEYS[1]); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_HOME, 0, AKEYCODE_HOME, POLICY_FLAG_WAKE); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_MENU, 0, AKEYCODE_MENU, POLICY_FLAG_WAKE); } void TouchInputMapperTest::prepareLocationCalibration() { @@ -3628,33 +3667,29 @@ protected: }; void SingleTouchInputMapperTest::prepareButtons() { - mFakeEventHub->addKey(DEVICE_ID, BTN_TOUCH, 0, AKEYCODE_UNKNOWN, 0); + mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOUCH, 0, AKEYCODE_UNKNOWN, 0); } void SingleTouchInputMapperTest::prepareAxes(int axes) { if (axes & POSITION) { - mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_X, - RAW_X_MIN, RAW_X_MAX, 0, 0); - mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_Y, - RAW_Y_MIN, RAW_Y_MAX, 0, 0); + mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_X, RAW_X_MIN, RAW_X_MAX, 0, 0); + mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_Y, RAW_Y_MIN, RAW_Y_MAX, 0, 0); } if (axes & PRESSURE) { - mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_PRESSURE, - RAW_PRESSURE_MIN, RAW_PRESSURE_MAX, 0, 0); + mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_PRESSURE, RAW_PRESSURE_MIN, + RAW_PRESSURE_MAX, 0, 0); } if (axes & TOOL) { - mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_TOOL_WIDTH, - RAW_TOOL_MIN, RAW_TOOL_MAX, 0, 0); + mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_TOOL_WIDTH, RAW_TOOL_MIN, RAW_TOOL_MAX, 0, + 0); } if (axes & DISTANCE) { - mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_DISTANCE, - RAW_DISTANCE_MIN, RAW_DISTANCE_MAX, 0, 0); + mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_DISTANCE, RAW_DISTANCE_MIN, + RAW_DISTANCE_MAX, 0, 0); } if (axes & TILT) { - mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_TILT_X, - RAW_TILT_MIN, RAW_TILT_MAX, 0, 0); - mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_TILT_Y, - RAW_TILT_MIN, RAW_TILT_MAX, 0, 0); + mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_TILT_X, RAW_TILT_MIN, RAW_TILT_MAX, 0, 0); + mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_TILT_Y, RAW_TILT_MIN, RAW_TILT_MAX, 0, 0); } } @@ -3710,8 +3745,8 @@ TEST_F(SingleTouchInputMapperTest, GetSources_WhenDeviceTypeIsNotSpecifiedAndNot } TEST_F(SingleTouchInputMapperTest, GetSources_WhenDeviceTypeIsNotSpecifiedAndIsACursor_ReturnsTouchPad) { - mFakeEventHub->addRelativeAxis(DEVICE_ID, REL_X); - mFakeEventHub->addRelativeAxis(DEVICE_ID, REL_Y); + mFakeEventHub->addRelativeAxis(EVENTHUB_ID, REL_X); + mFakeEventHub->addRelativeAxis(EVENTHUB_ID, REL_Y); prepareButtons(); prepareAxes(POSITION); SingleTouchInputMapper& mapper = addMapperAndConfigure(); @@ -4778,7 +4813,7 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenBtnTouchPresent_HoversIfItsValueI prepareDisplay(DISPLAY_ORIENTATION_0); prepareButtons(); prepareAxes(POSITION); - mFakeEventHub->addKey(DEVICE_ID, BTN_TOOL_FINGER, 0, AKEYCODE_UNKNOWN, 0); + mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOOL_FINGER, 0, AKEYCODE_UNKNOWN, 0); SingleTouchInputMapper& mapper = addMapperAndConfigure(); NotifyMotionArgs motionArgs; @@ -4940,51 +4975,47 @@ protected: void MultiTouchInputMapperTest::prepareAxes(int axes) { if (axes & POSITION) { - mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_POSITION_X, - RAW_X_MIN, RAW_X_MAX, 0, 0); - mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_POSITION_Y, - RAW_Y_MIN, RAW_Y_MAX, 0, 0); + mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_X, RAW_X_MIN, RAW_X_MAX, 0, 0); + mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_Y, RAW_Y_MIN, RAW_Y_MAX, 0, 0); } if (axes & TOUCH) { - mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_TOUCH_MAJOR, - RAW_TOUCH_MIN, RAW_TOUCH_MAX, 0, 0); + mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_TOUCH_MAJOR, RAW_TOUCH_MIN, + RAW_TOUCH_MAX, 0, 0); if (axes & MINOR) { - mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_TOUCH_MINOR, - RAW_TOUCH_MIN, RAW_TOUCH_MAX, 0, 0); + mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_TOUCH_MINOR, RAW_TOUCH_MIN, + RAW_TOUCH_MAX, 0, 0); } } if (axes & TOOL) { - mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_WIDTH_MAJOR, - RAW_TOOL_MIN, RAW_TOOL_MAX, 0, 0); + mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_WIDTH_MAJOR, RAW_TOOL_MIN, RAW_TOOL_MAX, + 0, 0); if (axes & MINOR) { - mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_WIDTH_MINOR, - RAW_TOOL_MAX, RAW_TOOL_MAX, 0, 0); + mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_WIDTH_MINOR, RAW_TOOL_MAX, + RAW_TOOL_MAX, 0, 0); } } if (axes & ORIENTATION) { - mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_ORIENTATION, - RAW_ORIENTATION_MIN, RAW_ORIENTATION_MAX, 0, 0); + mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_ORIENTATION, RAW_ORIENTATION_MIN, + RAW_ORIENTATION_MAX, 0, 0); } if (axes & PRESSURE) { - mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_PRESSURE, - RAW_PRESSURE_MIN, RAW_PRESSURE_MAX, 0, 0); + mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_PRESSURE, RAW_PRESSURE_MIN, + RAW_PRESSURE_MAX, 0, 0); } if (axes & DISTANCE) { - mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_DISTANCE, - RAW_DISTANCE_MIN, RAW_DISTANCE_MAX, 0, 0); + mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_DISTANCE, RAW_DISTANCE_MIN, + RAW_DISTANCE_MAX, 0, 0); } if (axes & ID) { - mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_TRACKING_ID, - RAW_ID_MIN, RAW_ID_MAX, 0, 0); + mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_TRACKING_ID, RAW_ID_MIN, RAW_ID_MAX, 0, + 0); } if (axes & SLOT) { - mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_SLOT, - RAW_SLOT_MIN, RAW_SLOT_MAX, 0, 0); - mFakeEventHub->setAbsoluteAxisValue(DEVICE_ID, ABS_MT_SLOT, 0); + mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_SLOT, RAW_SLOT_MIN, RAW_SLOT_MAX, 0, 0); + mFakeEventHub->setAbsoluteAxisValue(EVENTHUB_ID, ABS_MT_SLOT, 0); } if (axes & TOOL_TYPE) { - mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_TOOL_TYPE, - 0, MT_TOOL_MAX, 0, 0); + mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_TOOL_TYPE, 0, MT_TOOL_MAX, 0, 0); } } @@ -6273,7 +6304,7 @@ TEST_F(MultiTouchInputMapperTest, Process_WhenBtnTouchPresent_HoversIfItsValueIs addConfigurationProperty("touch.deviceType", "touchScreen"); prepareDisplay(DISPLAY_ORIENTATION_0); prepareAxes(POSITION | ID | SLOT); - mFakeEventHub->addKey(DEVICE_ID, BTN_TOUCH, 0, AKEYCODE_UNKNOWN, 0); + mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOUCH, 0, AKEYCODE_UNKNOWN, 0); MultiTouchInputMapper& mapper = addMapperAndConfigure(); NotifyMotionArgs motionArgs; @@ -6452,35 +6483,6 @@ TEST_F(MultiTouchInputMapperTest, Configure_AssignsDisplayPort) { ASSERT_EQ(DISPLAY_ID, args.displayId); } -/** - * Expect fallback to internal viewport if device is external and external viewport is not present. - */ -TEST_F(MultiTouchInputMapperTest, Viewports_Fallback) { - prepareAxes(POSITION); - addConfigurationProperty("touch.deviceType", "touchScreen"); - prepareDisplay(DISPLAY_ORIENTATION_0); - mDevice->setExternal(true); - MultiTouchInputMapper& mapper = addMapperAndConfigure(); - - ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, mapper.getSources()); - - NotifyMotionArgs motionArgs; - - // Expect the event to be sent to the internal viewport, - // because an external viewport is not present. - processPosition(mapper, 100, 100); - processSync(mapper); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(ADISPLAY_ID_DEFAULT, motionArgs.displayId); - - // Expect the event to be sent to the external viewport if it is present. - prepareSecondaryDisplay(ViewportType::VIEWPORT_EXTERNAL); - processPosition(mapper, 100, 100); - processSync(mapper); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(SECONDARY_DISPLAY_ID, motionArgs.displayId); -} - TEST_F(MultiTouchInputMapperTest, Process_Pointer_ShouldHandleDisplayId) { // Setup for second display. sp fakePointerController = new FakePointerController(); @@ -6517,27 +6519,28 @@ TEST_F(MultiTouchInputMapperTest, Process_Pointer_ShowTouches) { // Create the second touch screen device, and enable multi fingers. const std::string USB2 = "USB2"; constexpr int32_t SECOND_DEVICE_ID = DEVICE_ID + 1; + constexpr int32_t SECOND_EVENTHUB_ID = EVENTHUB_ID + 1; InputDeviceIdentifier identifier; identifier.name = "TOUCHSCREEN2"; identifier.location = USB2; std::unique_ptr device2 = std::make_unique(mFakeContext, SECOND_DEVICE_ID, DEVICE_GENERATION, - DEVICE_CONTROLLER_NUMBER, identifier, DEVICE_CLASSES); - mFakeEventHub->addDevice(SECOND_DEVICE_ID, DEVICE_NAME, 0 /*classes*/); - mFakeEventHub->addAbsoluteAxis(SECOND_DEVICE_ID, ABS_MT_POSITION_X, RAW_X_MIN, RAW_X_MAX, - 0 /*flat*/, 0 /*fuzz*/); - mFakeEventHub->addAbsoluteAxis(SECOND_DEVICE_ID, ABS_MT_POSITION_Y, RAW_Y_MIN, RAW_Y_MAX, - 0 /*flat*/, 0 /*fuzz*/); - mFakeEventHub->addAbsoluteAxis(SECOND_DEVICE_ID, ABS_MT_TRACKING_ID, RAW_ID_MIN, RAW_ID_MAX, - 0 /*flat*/, 0 /*fuzz*/); - mFakeEventHub->addAbsoluteAxis(SECOND_DEVICE_ID, ABS_MT_SLOT, RAW_SLOT_MIN, RAW_SLOT_MAX, - 0 /*flat*/, 0 /*fuzz*/); - mFakeEventHub->setAbsoluteAxisValue(SECOND_DEVICE_ID, ABS_MT_SLOT, 0 /*value*/); - mFakeEventHub->addConfigurationProperty(SECOND_DEVICE_ID, String8("touch.deviceType"), - String8("touchScreen")); + identifier); + mFakeEventHub->addDevice(SECOND_EVENTHUB_ID, DEVICE_NAME, 0 /*classes*/); + mFakeEventHub->addAbsoluteAxis(SECOND_EVENTHUB_ID, ABS_MT_POSITION_X, RAW_X_MIN, RAW_X_MAX, + 0 /*flat*/, 0 /*fuzz*/); + mFakeEventHub->addAbsoluteAxis(SECOND_EVENTHUB_ID, ABS_MT_POSITION_Y, RAW_Y_MIN, RAW_Y_MAX, + 0 /*flat*/, 0 /*fuzz*/); + mFakeEventHub->addAbsoluteAxis(SECOND_EVENTHUB_ID, ABS_MT_TRACKING_ID, RAW_ID_MIN, RAW_ID_MAX, + 0 /*flat*/, 0 /*fuzz*/); + mFakeEventHub->addAbsoluteAxis(SECOND_EVENTHUB_ID, ABS_MT_SLOT, RAW_SLOT_MIN, RAW_SLOT_MAX, + 0 /*flat*/, 0 /*fuzz*/); + mFakeEventHub->setAbsoluteAxisValue(SECOND_EVENTHUB_ID, ABS_MT_SLOT, 0 /*value*/); + mFakeEventHub->addConfigurationProperty(SECOND_EVENTHUB_ID, String8("touch.deviceType"), + String8("touchScreen")); // Setup the second touch screen device. - MultiTouchInputMapper& mapper2 = device2->addMapper(); + MultiTouchInputMapper& mapper2 = device2->addMapper(SECOND_EVENTHUB_ID); device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0 /*changes*/); device2->reset(ARBITRARY_TIME); @@ -6598,7 +6601,7 @@ TEST_F(MultiTouchInputMapperTest, VideoFrames_ReceivedByListener) { // Unrotated video frame TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, {1, 2}); std::vector frames{frame}; - mFakeEventHub->setVideoFrames({{mDevice->getId(), frames}}); + mFakeEventHub->setVideoFrames({{EVENTHUB_ID, frames}}); processPosition(mapper, 100, 200); processSync(mapper); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); @@ -6628,7 +6631,7 @@ TEST_F(MultiTouchInputMapperTest, VideoFrames_AreRotated) { clearViewports(); prepareDisplay(orientation); std::vector frames{frame}; - mFakeEventHub->setVideoFrames({{mDevice->getId(), frames}}); + mFakeEventHub->setVideoFrames({{EVENTHUB_ID, frames}}); processPosition(mapper, 100, 200); processSync(mapper); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); @@ -6650,7 +6653,7 @@ TEST_F(MultiTouchInputMapperTest, VideoFrames_MultipleFramesAreRotated) { NotifyMotionArgs motionArgs; prepareDisplay(DISPLAY_ORIENTATION_90); - mFakeEventHub->setVideoFrames({{mDevice->getId(), frames}}); + mFakeEventHub->setVideoFrames({{EVENTHUB_ID, frames}}); processPosition(mapper, 100, 200); processSync(mapper); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); @@ -6824,4 +6827,41 @@ TEST_F(MultiTouchInputMapperTest, Process_ShouldHandlePalmToolType) { ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType); } +// --- MultiTouchInputMapperTest_ExternalDevice --- + +class MultiTouchInputMapperTest_ExternalDevice : public MultiTouchInputMapperTest { +protected: + virtual void SetUp() override { + InputMapperTest::SetUp(DEVICE_CLASSES | INPUT_DEVICE_CLASS_EXTERNAL); + } +}; + +/** + * Expect fallback to internal viewport if device is external and external viewport is not present. + */ +TEST_F(MultiTouchInputMapperTest_ExternalDevice, Viewports_Fallback) { + prepareAxes(POSITION); + addConfigurationProperty("touch.deviceType", "touchScreen"); + prepareDisplay(DISPLAY_ORIENTATION_0); + MultiTouchInputMapper& mapper = addMapperAndConfigure(); + + ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, mapper.getSources()); + + NotifyMotionArgs motionArgs; + + // Expect the event to be sent to the internal viewport, + // because an external viewport is not present. + processPosition(mapper, 100, 100); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(ADISPLAY_ID_DEFAULT, motionArgs.displayId); + + // Expect the event to be sent to the external viewport if it is present. + prepareSecondaryDisplay(ViewportType::VIEWPORT_EXTERNAL); + processPosition(mapper, 100, 100); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(SECONDARY_DISPLAY_ID, motionArgs.displayId); +} + } // namespace android -- cgit v1.2.3-59-g8ed1b From bd25f1cad839bbe84f1baa0eb1aa3ddd84fcbde5 Mon Sep 17 00:00:00 2001 From: Garfield Tan Date: Thu, 23 Jan 2020 10:49:05 -0800 Subject: Add event ID generator. Bug: 144889238 Test: atest libinput_test Change-Id: I0fd192b3c5a08326aa6a6598025c7b8cdc64cce4 Merged-In: I0fd192b3c5a08326aa6a6598025c7b8cdc64cce4 (cherry picked from commit 84b087ec591626e8dd7d3d2c877afb74f0de1b09) --- include/input/Input.h | 32 +++++++++++++++++++++ libs/input/Input.cpp | 29 +++++++++++++++++++ libs/input/tests/Android.bp | 1 + libs/input/tests/IdGenerator_test.cpp | 52 +++++++++++++++++++++++++++++++++++ 4 files changed, 114 insertions(+) create mode 100644 libs/input/tests/IdGenerator_test.cpp (limited to 'include') diff --git a/include/input/Input.h b/include/input/Input.h index 14a7288d19..b86d761e48 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -265,6 +265,38 @@ enum class MotionClassification : uint8_t { */ const char* motionClassificationToString(MotionClassification classification); +/** + * Generator of unique numbers used to identify input events. + * + * Layout of ID: + * |--------------------------|---------------------------| + * | 2 bits for source | 30 bits for random number | + * |--------------------------|---------------------------| + */ +class IdGenerator { +private: + static constexpr uint32_t SOURCE_SHIFT = 30; + +public: + // Used to divide integer space to ensure no conflict among these sources./ + enum class Source : int32_t { + INPUT_READER = 0x0 << SOURCE_SHIFT, + INPUT_DISPATCHER = 0x1 << SOURCE_SHIFT, + OTHER = 0x3 << SOURCE_SHIFT, // E.g. app injected events + }; + IdGenerator(Source source); + + int32_t nextId() const; + + // Extract source from given id. + static inline Source getSource(int32_t id) { return static_cast(SOURCE_MASK & id); } + +private: + const Source mSource; + + static constexpr int32_t SOURCE_MASK = 0x3 << SOURCE_SHIFT; +}; + /** * Invalid value for cursor position. Used for non-mouse events, tests and injected events. Don't * use it for direct comparison with any other value, because NaN isn't equal to itself according to diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index 2a73dc0149..abd031ae26 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -17,7 +17,9 @@ #define LOG_TAG "Input" //#define LOG_NDEBUG 0 +#include #include +#include #include #include @@ -25,6 +27,7 @@ #ifdef __ANDROID__ #include +#include #endif namespace android { @@ -40,6 +43,32 @@ const char* motionClassificationToString(MotionClassification classification) { } } +// --- IdGenerator --- +IdGenerator::IdGenerator(Source source) : mSource(source) {} + +int32_t IdGenerator::nextId() const { + constexpr uint32_t SEQUENCE_NUMBER_MASK = ~SOURCE_MASK; + int32_t id = 0; + +// Avoid building against syscall getrandom(2) on host, which will fail build on Mac. Host doesn't +// use sequence number so just always return mSource. +#ifdef __ANDROID__ + constexpr size_t BUF_LEN = sizeof(id); + size_t totalBytes = 0; + while (totalBytes < BUF_LEN) { + ssize_t bytes = TEMP_FAILURE_RETRY(getrandom(&id, BUF_LEN, GRND_NONBLOCK)); + if (CC_UNLIKELY(bytes < 0)) { + ALOGW("Failed to fill in random number for sequence number: %s.", strerror(errno)); + id = 0; + break; + } + totalBytes += bytes; + } +#endif // __ANDROID__ + + return (id & SEQUENCE_NUMBER_MASK) | static_cast(mSource); +} + // --- InputEvent --- const char* inputEventTypeToString(int32_t type) { diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp index fb21d5e3b1..3b57146461 100644 --- a/libs/input/tests/Android.bp +++ b/libs/input/tests/Android.bp @@ -2,6 +2,7 @@ cc_test { name: "libinput_tests", srcs: [ + "IdGenerator_test.cpp", "InputChannel_test.cpp", "InputDevice_test.cpp", "InputEvent_test.cpp", diff --git a/libs/input/tests/IdGenerator_test.cpp b/libs/input/tests/IdGenerator_test.cpp new file mode 100644 index 0000000000..f7fc3c0ef2 --- /dev/null +++ b/libs/input/tests/IdGenerator_test.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include + +namespace android::test { + +class IdGeneratorTest : public testing::TestWithParam { +protected: + void SetUp() override { mGenerator.reset(new IdGenerator(GetParam())); } + + std::unique_ptr mGenerator; +}; + +TEST_P(IdGeneratorTest, GenerateRandomNumber) { + for (int i = 0; i < 500; ++i) { + mGenerator->nextId(); + } +} + +TEST_P(IdGeneratorTest, GenerateRandomNumberWithProperFlag) { + for (int i = 0; i < 500; ++i) { + int32_t id = mGenerator->nextId(); + IdGenerator::Source source = IdGenerator::getSource(id); + EXPECT_EQ(source, GetParam()) + << std::hex << "Generator generated a value with wrong source. Value: 0x" << id + << " Source: 0x" << static_cast(source); + } +} + +INSTANTIATE_TEST_SUITE_P(SourceInstantiation, IdGeneratorTest, + testing::Values(IdGenerator::Source::INPUT_READER, + IdGenerator::Source::INPUT_DISPATCHER, + IdGenerator::Source::OTHER)); +} // namespace android::test -- cgit v1.2.3-59-g8ed1b From fbe732ebbb25870086610e9a32acdb37df402612 Mon Sep 17 00:00:00 2001 From: Garfield Tan Date: Fri, 24 Jan 2020 11:26:14 -0800 Subject: Add ID to native events. To separate this big work into pieces I introduced a placeholder in InputTransport used to initialize native input events received from InputFlinger until InputFlinger can generate random sequence numbers. The work in InputDispatcher wires up ID between events and notify args as well. Bug: 144889238 Test: Builds and some smoke tests. Test: atest libinput_tests Change-Id: I1ef7f243cc89a8b6e07ba9ba30a43c21eedb16ce Merged-In: I1ef7f243cc89a8b6e07ba9ba30a43c21eedb16ce (cherry picked from commit 4cc839fe01cd5bbb16650e85913070386f637e75) --- include/input/Input.h | 14 ++-- libs/input/Input.cpp | 26 +++++--- libs/input/InputTransport.cpp | 37 ++++++----- libs/input/KeyCharacterMap.cpp | 6 +- libs/input/tests/InputEvent_test.cpp | 37 ++++++----- libs/input/tests/VelocityTracker_test.cpp | 11 ++-- libs/input/tests/VerifiedInputEvent_test.cpp | 11 ++-- .../benchmarks/InputDispatcher_benchmarks.cpp | 5 +- .../inputflinger/dispatcher/InputDispatcher.cpp | 55 ++++++++-------- .../inputflinger/tests/InputDispatcher_test.cpp | 76 ++++++++++++---------- 10 files changed, 160 insertions(+), 118 deletions(-) (limited to 'include') diff --git a/include/input/Input.h b/include/input/Input.h index b86d761e48..9e47318203 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -397,6 +397,8 @@ public: virtual int32_t getType() const = 0; + inline int32_t getId() const { return mId; } + inline int32_t getDeviceId() const { return mDeviceId; } inline uint32_t getSource() const { return mSource; } @@ -409,11 +411,15 @@ public: inline std::array getHmac() const { return mHmac; } + static int32_t nextId(); + protected: - void initialize(int32_t deviceId, uint32_t source, int32_t displayId, + void initialize(int32_t id, int32_t deviceId, uint32_t source, int32_t displayId, std::array hmac); + void initialize(const InputEvent& from); + int32_t mId; int32_t mDeviceId; uint32_t mSource; int32_t mDisplayId; @@ -450,7 +456,7 @@ public: static const char* getLabel(int32_t keyCode); static int32_t getKeyCodeFromLabel(const char* label); - void initialize(int32_t deviceId, uint32_t source, int32_t displayId, + void initialize(int32_t id, int32_t deviceId, uint32_t source, int32_t displayId, std::array hmac, int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState, int32_t repeatCount, nsecs_t downTime, nsecs_t eventTime); @@ -674,7 +680,7 @@ public: ssize_t findPointerIndex(int32_t pointerId) const; - void initialize(int32_t deviceId, uint32_t source, int32_t displayId, + void initialize(int32_t id, int32_t deviceId, uint32_t source, int32_t displayId, std::array hmac, int32_t action, int32_t actionButton, int32_t flags, int32_t edgeFlags, int32_t metaState, int32_t buttonState, MotionClassification classification, float xScale, float yScale, float xOffset, @@ -754,7 +760,7 @@ public: inline bool getInTouchMode() const { return mInTouchMode; } - void initialize(bool hasFocus, bool inTouchMode); + void initialize(int32_t id, bool hasFocus, bool inTouchMode); void initialize(const FocusEvent& from); diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index abd031ae26..c2437673df 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -110,8 +110,9 @@ VerifiedMotionEvent verifiedMotionEventFromMotionEvent(const MotionEvent& event) event.getButtonState()}; } -void InputEvent::initialize(int32_t deviceId, uint32_t source, int32_t displayId, +void InputEvent::initialize(int32_t id, int32_t deviceId, uint32_t source, int32_t displayId, std::array hmac) { + mId = id; mDeviceId = deviceId; mSource = source; mDisplayId = displayId; @@ -119,12 +120,18 @@ void InputEvent::initialize(int32_t deviceId, uint32_t source, int32_t displayId } void InputEvent::initialize(const InputEvent& from) { + mId = from.mId; mDeviceId = from.mDeviceId; mSource = from.mSource; mDisplayId = from.mDisplayId; mHmac = from.mHmac; } +int32_t InputEvent::nextId() { + static IdGenerator idGen(IdGenerator::Source::OTHER); + return idGen.nextId(); +} + // --- KeyEvent --- const char* KeyEvent::getLabel(int32_t keyCode) { @@ -135,11 +142,11 @@ int32_t KeyEvent::getKeyCodeFromLabel(const char* label) { return getKeyCodeByLabel(label); } -void KeyEvent::initialize(int32_t deviceId, uint32_t source, int32_t displayId, +void KeyEvent::initialize(int32_t id, int32_t deviceId, uint32_t source, int32_t displayId, std::array hmac, int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState, int32_t repeatCount, nsecs_t downTime, nsecs_t eventTime) { - InputEvent::initialize(deviceId, source, displayId, hmac); + InputEvent::initialize(id, deviceId, source, displayId, hmac); mAction = action; mFlags = flags; mKeyCode = keyCode; @@ -298,7 +305,7 @@ void PointerProperties::copyFrom(const PointerProperties& other) { // --- MotionEvent --- -void MotionEvent::initialize(int32_t deviceId, uint32_t source, int32_t displayId, +void MotionEvent::initialize(int32_t id, int32_t deviceId, uint32_t source, int32_t displayId, std::array hmac, int32_t action, int32_t actionButton, int32_t flags, int32_t edgeFlags, int32_t metaState, int32_t buttonState, MotionClassification classification, float xScale, @@ -307,7 +314,7 @@ void MotionEvent::initialize(int32_t deviceId, uint32_t source, int32_t displayI nsecs_t downTime, nsecs_t eventTime, size_t pointerCount, const PointerProperties* pointerProperties, const PointerCoords* pointerCoords) { - InputEvent::initialize(deviceId, source, displayId, hmac); + InputEvent::initialize(id, deviceId, source, displayId, hmac); mAction = action; mActionButton = actionButton; mFlags = flags; @@ -332,7 +339,8 @@ void MotionEvent::initialize(int32_t deviceId, uint32_t source, int32_t displayI } void MotionEvent::copyFrom(const MotionEvent* other, bool keepHistory) { - InputEvent::initialize(other->mDeviceId, other->mSource, other->mDisplayId, other->mHmac); + InputEvent::initialize(other->mId, other->mDeviceId, other->mSource, other->mDisplayId, + other->mHmac); mAction = other->mAction; mActionButton = other->mActionButton; mFlags = other->mFlags; @@ -540,6 +548,7 @@ status_t MotionEvent::readFromParcel(Parcel* parcel) { return BAD_VALUE; } + mId = parcel->readInt32(); mDeviceId = parcel->readInt32(); mSource = parcel->readUint32(); mDisplayId = parcel->readInt32(); @@ -601,6 +610,7 @@ status_t MotionEvent::writeToParcel(Parcel* parcel) const { parcel->writeInt32(pointerCount); parcel->writeInt32(sampleCount); + parcel->writeInt32(mId); parcel->writeInt32(mDeviceId); parcel->writeUint32(mSource); parcel->writeInt32(mDisplayId); @@ -670,8 +680,8 @@ int32_t MotionEvent::getAxisFromLabel(const char* label) { // --- FocusEvent --- -void FocusEvent::initialize(bool hasFocus, bool inTouchMode) { - InputEvent::initialize(ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID, AINPUT_SOURCE_UNKNOWN, +void FocusEvent::initialize(int32_t id, bool hasFocus, bool inTouchMode) { + InputEvent::initialize(id, ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID, AINPUT_SOURCE_UNKNOWN, ADISPLAY_ID_NONE, INVALID_HMAC); mHasFocus = hasFocus; mInTouchMode = inTouchMode; diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp index d25a5cc0ef..eda01c5032 100644 --- a/libs/input/InputTransport.cpp +++ b/libs/input/InputTransport.cpp @@ -63,6 +63,10 @@ static const nsecs_t RESAMPLE_MAX_DELTA = 20 * NANOS_PER_MS; // far into the future. This time is further bounded by 50% of the last time delta. static const nsecs_t RESAMPLE_MAX_PREDICTION = 8 * NANOS_PER_MS; +// A placeholder sequence number used to initialize native input events before InputFlinger is +// migrated to new sequence number system. +static constexpr int32_t INPUT_FLINGER_SEQUENCE_NUM = 0; + /** * System property for enabling / disabling touch resampling. * Resampling extrapolates / interpolates the reported touch event coordinates to better @@ -1142,14 +1146,16 @@ ssize_t InputConsumer::findTouchState(int32_t deviceId, int32_t source) const { } void InputConsumer::initializeKeyEvent(KeyEvent* event, const InputMessage* msg) { - event->initialize(msg->body.key.deviceId, msg->body.key.source, msg->body.key.displayId, - msg->body.key.hmac, msg->body.key.action, msg->body.key.flags, - msg->body.key.keyCode, msg->body.key.scanCode, msg->body.key.metaState, - msg->body.key.repeatCount, msg->body.key.downTime, msg->body.key.eventTime); + event->initialize(INPUT_FLINGER_SEQUENCE_NUM, msg->body.key.deviceId, msg->body.key.source, + msg->body.key.displayId, msg->body.key.hmac, msg->body.key.action, + msg->body.key.flags, msg->body.key.keyCode, msg->body.key.scanCode, + msg->body.key.metaState, msg->body.key.repeatCount, msg->body.key.downTime, + msg->body.key.eventTime); } void InputConsumer::initializeFocusEvent(FocusEvent* event, const InputMessage* msg) { - event->initialize(msg->body.focus.hasFocus == 1, msg->body.focus.inTouchMode == 1); + event->initialize(INPUT_FLINGER_SEQUENCE_NUM, msg->body.focus.hasFocus == 1, + msg->body.focus.inTouchMode == 1); } void InputConsumer::initializeMotionEvent(MotionEvent* event, const InputMessage* msg) { @@ -1161,16 +1167,17 @@ void InputConsumer::initializeMotionEvent(MotionEvent* event, const InputMessage pointerCoords[i].copyFrom(msg->body.motion.pointers[i].coords); } - event->initialize(msg->body.motion.deviceId, msg->body.motion.source, - msg->body.motion.displayId, msg->body.motion.hmac, msg->body.motion.action, - msg->body.motion.actionButton, msg->body.motion.flags, - msg->body.motion.edgeFlags, msg->body.motion.metaState, - msg->body.motion.buttonState, msg->body.motion.classification, - msg->body.motion.xScale, msg->body.motion.yScale, msg->body.motion.xOffset, - msg->body.motion.yOffset, msg->body.motion.xPrecision, - msg->body.motion.yPrecision, msg->body.motion.xCursorPosition, - msg->body.motion.yCursorPosition, msg->body.motion.downTime, - msg->body.motion.eventTime, pointerCount, pointerProperties, pointerCoords); + event->initialize(INPUT_FLINGER_SEQUENCE_NUM, msg->body.motion.deviceId, + msg->body.motion.source, msg->body.motion.displayId, msg->body.motion.hmac, + msg->body.motion.action, msg->body.motion.actionButton, + msg->body.motion.flags, msg->body.motion.edgeFlags, + msg->body.motion.metaState, msg->body.motion.buttonState, + msg->body.motion.classification, msg->body.motion.xScale, + msg->body.motion.yScale, msg->body.motion.xOffset, msg->body.motion.yOffset, + msg->body.motion.xPrecision, msg->body.motion.yPrecision, + msg->body.motion.xCursorPosition, msg->body.motion.yCursorPosition, + msg->body.motion.downTime, msg->body.motion.eventTime, pointerCount, + pointerProperties, pointerCoords); } void InputConsumer::addSample(MotionEvent* event, const InputMessage* msg) { diff --git a/libs/input/KeyCharacterMap.cpp b/libs/input/KeyCharacterMap.cpp index 6f9b162986..cb68165433 100644 --- a/libs/input/KeyCharacterMap.cpp +++ b/libs/input/KeyCharacterMap.cpp @@ -487,9 +487,9 @@ void KeyCharacterMap::addKey(Vector& outEvents, int32_t deviceId, int32_t keyCode, int32_t metaState, bool down, nsecs_t time) { outEvents.push(); KeyEvent& event = outEvents.editTop(); - event.initialize(deviceId, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE, INVALID_HMAC, - down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP, 0, keyCode, 0, metaState, - 0, time, time); + event.initialize(InputEvent::nextId(), deviceId, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE, + INVALID_HMAC, down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP, 0, keyCode, + 0, metaState, 0, time, time); } void KeyCharacterMap::addMetaKeys(Vector& outEvents, diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp index d0f761887a..553dc4c068 100644 --- a/libs/input/tests/InputEvent_test.cpp +++ b/libs/input/tests/InputEvent_test.cpp @@ -182,10 +182,12 @@ TEST_F(KeyEventTest, Properties) { // Initialize and get properties. constexpr nsecs_t ARBITRARY_DOWN_TIME = 1; constexpr nsecs_t ARBITRARY_EVENT_TIME = 2; - event.initialize(2, AINPUT_SOURCE_GAMEPAD, DISPLAY_ID, HMAC, AKEY_EVENT_ACTION_DOWN, + const int32_t id = InputEvent::nextId(); + event.initialize(id, 2, AINPUT_SOURCE_GAMEPAD, DISPLAY_ID, HMAC, AKEY_EVENT_ACTION_DOWN, AKEY_EVENT_FLAG_FROM_SYSTEM, AKEYCODE_BUTTON_X, 121, AMETA_ALT_ON, 1, ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME); + ASSERT_EQ(id, event.getId()); ASSERT_EQ(AINPUT_EVENT_TYPE_KEY, event.getType()); ASSERT_EQ(2, event.getDeviceId()); ASSERT_EQ(AINPUT_SOURCE_GAMEPAD, event.getSource()); @@ -222,12 +224,16 @@ protected: static constexpr float X_OFFSET = 1; static constexpr float Y_OFFSET = 1.1; + int32_t mId; + void initializeEventWithHistory(MotionEvent* event); void assertEqualsEventWithHistory(const MotionEvent* event); }; void MotionEventTest::initializeEventWithHistory(MotionEvent* event) { + mId = InputEvent::nextId(); + PointerProperties pointerProperties[2]; pointerProperties[0].clear(); pointerProperties[0].id = 1; @@ -257,10 +263,10 @@ void MotionEventTest::initializeEventWithHistory(MotionEvent* event) { pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 26); pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 27); pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 28); - event->initialize(2, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, HMAC, AMOTION_EVENT_ACTION_MOVE, 0, - AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, AMOTION_EVENT_EDGE_FLAG_TOP, - AMETA_ALT_ON, AMOTION_EVENT_BUTTON_PRIMARY, MotionClassification::NONE, - X_SCALE, Y_SCALE, X_OFFSET, Y_OFFSET, 2.0f, 2.1f, + event->initialize(mId, 2, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, HMAC, + AMOTION_EVENT_ACTION_MOVE, 0, AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, + AMOTION_EVENT_EDGE_FLAG_TOP, AMETA_ALT_ON, AMOTION_EVENT_BUTTON_PRIMARY, + MotionClassification::NONE, X_SCALE, Y_SCALE, X_OFFSET, Y_OFFSET, 2.0f, 2.1f, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME, 2, pointerProperties, pointerCoords); @@ -308,6 +314,7 @@ void MotionEventTest::initializeEventWithHistory(MotionEvent* event) { void MotionEventTest::assertEqualsEventWithHistory(const MotionEvent* event) { // Check properties. + ASSERT_EQ(mId, event->getId()); ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, event->getType()); ASSERT_EQ(2, event->getDeviceId()); ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, event->getSource()); @@ -577,8 +584,8 @@ TEST_F(MotionEventTest, Transform) { pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, angle); } MotionEvent event; - event.initialize(0 /*deviceId*/, AINPUT_SOURCE_UNKNOWN, DISPLAY_ID, INVALID_HMAC, - AMOTION_EVENT_ACTION_MOVE, 0 /*actionButton*/, 0 /*flags*/, + event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_UNKNOWN, DISPLAY_ID, + INVALID_HMAC, AMOTION_EVENT_ACTION_MOVE, 0 /*actionButton*/, 0 /*flags*/, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/, MotionClassification::NONE, 1 /*xScale*/, 1 /*yScale*/, 0 /*xOffset*/, 0 /*yOffset*/, 0 /*xPrecision*/, 0 /*yPrecision*/, @@ -642,10 +649,10 @@ TEST_F(MotionEventTest, Initialize_SetsClassification) { } for (MotionClassification classification : classifications) { - event.initialize(0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, INVALID_HMAC, - AMOTION_EVENT_ACTION_DOWN, 0, 0, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, - 0, classification, 1 /*xScale*/, 1 /*yScale*/, 0, 0, 0, 0, - AMOTION_EVENT_INVALID_CURSOR_POSITION, + event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN, + DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, + AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0, classification, 1 /*xScale*/, + 1 /*yScale*/, 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, 0 /*downTime*/, 0 /*eventTime*/, pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(classification, event.getClassification()); @@ -663,10 +670,10 @@ TEST_F(MotionEventTest, Initialize_SetsCursorPosition) { pointerCoords[i].clear(); } - event.initialize(0 /*deviceId*/, AINPUT_SOURCE_MOUSE, DISPLAY_ID, INVALID_HMAC, - AMOTION_EVENT_ACTION_DOWN, 0, 0, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0, - MotionClassification::NONE, 1 /*xScale*/, 1 /*yScale*/, 0, 0, 0, 0, - 280 /*xCursorPosition*/, 540 /*yCursorPosition*/, 0 /*downTime*/, + event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_MOUSE, DISPLAY_ID, + INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, AMOTION_EVENT_EDGE_FLAG_NONE, + AMETA_NONE, 0, MotionClassification::NONE, 1 /*xScale*/, 1 /*yScale*/, 0, 0, 0, + 0, 280 /*xCursorPosition*/, 540 /*yCursorPosition*/, 0 /*downTime*/, 0 /*eventTime*/, pointerCount, pointerProperties, pointerCoords); event.offsetLocation(20, 60); ASSERT_EQ(280, event.getRawXCursorPosition()); diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp index 731eb6a000..bf452c07a3 100644 --- a/libs/input/tests/VelocityTracker_test.cpp +++ b/libs/input/tests/VelocityTracker_test.cpp @@ -176,11 +176,12 @@ static std::vector createMotionEventStream( EXPECT_EQ(pointerIndex, pointerCount); MotionEvent event; - event.initialize(0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, INVALID_HMAC, - action, 0 /*actionButton*/, 0 /*flags*/, AMOTION_EVENT_EDGE_FLAG_NONE, - AMETA_NONE, 0 /*buttonState*/, MotionClassification::NONE, 1 /*xScale*/, - 1 /*yScale*/, 0 /*xOffset*/, 0 /*yOffset*/, 0 /*xPrecision*/, - 0 /*yPrecision*/, AMOTION_EVENT_INVALID_CURSOR_POSITION, + event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN, + DISPLAY_ID, INVALID_HMAC, action, 0 /*actionButton*/, 0 /*flags*/, + AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/, + MotionClassification::NONE, 1 /*xScale*/, 1 /*yScale*/, 0 /*xOffset*/, + 0 /*yOffset*/, 0 /*xPrecision*/, 0 /*yPrecision*/, + AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, 0 /*downTime*/, entry.eventTime.count(), pointerCount, properties, coords); diff --git a/libs/input/tests/VerifiedInputEvent_test.cpp b/libs/input/tests/VerifiedInputEvent_test.cpp index a59dbe5987..4e8e840d1c 100644 --- a/libs/input/tests/VerifiedInputEvent_test.cpp +++ b/libs/input/tests/VerifiedInputEvent_test.cpp @@ -21,9 +21,10 @@ namespace android { static KeyEvent getKeyEventWithFlags(int32_t flags) { KeyEvent event; - event.initialize(2 /*deviceId*/, AINPUT_SOURCE_GAMEPAD, ADISPLAY_ID_DEFAULT, INVALID_HMAC, - AKEY_EVENT_ACTION_DOWN, flags, AKEYCODE_BUTTON_X, 121 /*scanCode*/, - AMETA_ALT_ON, 1 /*repeatCount*/, 1000 /*downTime*/, 2000 /*eventTime*/); + event.initialize(InputEvent::nextId(), 2 /*deviceId*/, AINPUT_SOURCE_GAMEPAD, + ADISPLAY_ID_DEFAULT, INVALID_HMAC, AKEY_EVENT_ACTION_DOWN, flags, + AKEYCODE_BUTTON_X, 121 /*scanCode*/, AMETA_ALT_ON, 1 /*repeatCount*/, + 1000 /*downTime*/, 2000 /*eventTime*/); return event; } @@ -38,8 +39,8 @@ static MotionEvent getMotionEventWithFlags(int32_t flags) { pointerCoords[i].clear(); } - event.initialize(0 /*deviceId*/, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_DEFAULT, INVALID_HMAC, - AMOTION_EVENT_ACTION_DOWN, 0 /*actionButton*/, flags, + event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_DEFAULT, + INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0 /*actionButton*/, flags, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/, MotionClassification::NONE, 2 /*xScale*/, 3 /*yScale*/, 4 /*xOffset*/, 5 /*yOffset*/, 0.1 /*xPrecision*/, 0.2 /*yPrecision*/, 280 /*xCursorPosition*/, diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp index 9a6ef21724..238cc39646 100644 --- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp +++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp @@ -203,8 +203,9 @@ static MotionEvent generateMotionEvent() { const nsecs_t currentTime = now(); MotionEvent event; - event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, INVALID_HMAC, - AMOTION_EVENT_ACTION_DOWN, /* actionButton */ 0, /* flags */ 0, + event.initialize(InputEvent::nextId(), DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, + /* actionButton */ 0, /* flags */ 0, /* edgeFlags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE, 1 /* xScale */, 1 /* yScale */, /* xOffset */ 0, /* yOffset */ 0, /* xPrecision */ 0, diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index f9a86dd6bb..70cee18330 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -3030,9 +3030,9 @@ void InputDispatcher::notifyKey(const NotifyKeyArgs* args) { accelerateMetaShortcuts(args->deviceId, args->action, keyCode, metaState); KeyEvent event; - event.initialize(args->deviceId, args->source, args->displayId, INVALID_HMAC, args->action, - flags, keyCode, args->scanCode, metaState, repeatCount, args->downTime, - args->eventTime); + event.initialize(args->sequenceNum, args->deviceId, args->source, args->displayId, INVALID_HMAC, + args->action, flags, keyCode, args->scanCode, metaState, repeatCount, + args->downTime, args->eventTime); android::base::Timer t; mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags); @@ -3125,13 +3125,14 @@ void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) { mLock.unlock(); MotionEvent event; - event.initialize(args->deviceId, args->source, args->displayId, INVALID_HMAC, - args->action, args->actionButton, args->flags, args->edgeFlags, - args->metaState, args->buttonState, args->classification, 1 /*xScale*/, - 1 /*yScale*/, 0 /* xOffset */, 0 /* yOffset */, args->xPrecision, - args->yPrecision, args->xCursorPosition, args->yCursorPosition, - args->downTime, args->eventTime, args->pointerCount, - args->pointerProperties, args->pointerCoords); + event.initialize(args->sequenceNum, args->deviceId, args->source, args->displayId, + INVALID_HMAC, args->action, args->actionButton, args->flags, + args->edgeFlags, args->metaState, args->buttonState, + args->classification, 1 /*xScale*/, 1 /*yScale*/, 0 /* xOffset */, + 0 /* yOffset */, args->xPrecision, args->yPrecision, + args->xCursorPosition, args->yCursorPosition, args->downTime, + args->eventTime, args->pointerCount, args->pointerProperties, + args->pointerCoords); policyFlags |= POLICY_FLAG_FILTERED; if (!mPolicy->filterInputEvent(&event, policyFlags)) { @@ -3227,7 +3228,7 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, int32_t injec accelerateMetaShortcuts(VIRTUAL_KEYBOARD_ID, action, /*byref*/ keyCode, /*byref*/ metaState); KeyEvent keyEvent; - keyEvent.initialize(VIRTUAL_KEYBOARD_ID, incomingKey.getSource(), + keyEvent.initialize(incomingKey.getId(), VIRTUAL_KEYBOARD_ID, incomingKey.getSource(), incomingKey.getDisplayId(), INVALID_HMAC, action, flags, keyCode, incomingKey.getScanCode(), metaState, incomingKey.getRepeatCount(), incomingKey.getDownTime(), incomingKey.getEventTime()); @@ -3247,11 +3248,12 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, int32_t injec mLock.lock(); KeyEntry* injectedEntry = - new KeyEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, keyEvent.getEventTime(), - VIRTUAL_KEYBOARD_ID, keyEvent.getSource(), keyEvent.getDisplayId(), - policyFlags, action, flags, keyEvent.getKeyCode(), - keyEvent.getScanCode(), keyEvent.getMetaState(), - keyEvent.getRepeatCount(), keyEvent.getDownTime()); + new KeyEntry(incomingKey.getId(), incomingKey.getEventTime(), + VIRTUAL_KEYBOARD_ID, incomingKey.getSource(), + incomingKey.getDisplayId(), policyFlags, action, flags, + incomingKey.getKeyCode(), incomingKey.getScanCode(), + incomingKey.getMetaState(), incomingKey.getRepeatCount(), + incomingKey.getDownTime()); injectedEntries.push(injectedEntry); break; } @@ -3281,13 +3283,12 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, int32_t injec const nsecs_t* sampleEventTimes = motionEvent->getSampleEventTimes(); const PointerCoords* samplePointerCoords = motionEvent->getSamplePointerCoords(); MotionEntry* injectedEntry = - new MotionEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, *sampleEventTimes, - VIRTUAL_KEYBOARD_ID, motionEvent->getSource(), - motionEvent->getDisplayId(), policyFlags, action, actionButton, - motionEvent->getFlags(), motionEvent->getMetaState(), - motionEvent->getButtonState(), motionEvent->getClassification(), - motionEvent->getEdgeFlags(), motionEvent->getXPrecision(), - motionEvent->getYPrecision(), + new MotionEntry(motionEvent->getId(), *sampleEventTimes, VIRTUAL_KEYBOARD_ID, + motionEvent->getSource(), motionEvent->getDisplayId(), + policyFlags, action, actionButton, motionEvent->getFlags(), + motionEvent->getMetaState(), motionEvent->getButtonState(), + motionEvent->getClassification(), motionEvent->getEdgeFlags(), + motionEvent->getXPrecision(), motionEvent->getYPrecision(), motionEvent->getRawXCursorPosition(), motionEvent->getRawYCursorPosition(), motionEvent->getDownTime(), uint32_t(pointerCount), @@ -3298,7 +3299,7 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, int32_t injec sampleEventTimes += 1; samplePointerCoords += pointerCount; MotionEntry* nextInjectedEntry = - new MotionEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, *sampleEventTimes, + new MotionEntry(motionEvent->getId(), *sampleEventTimes, VIRTUAL_KEYBOARD_ID, motionEvent->getSource(), motionEvent->getDisplayId(), policyFlags, action, actionButton, motionEvent->getFlags(), @@ -4817,9 +4818,9 @@ void InputDispatcher::doPokeUserActivityLockedInterruptible(CommandEntry* comman KeyEvent InputDispatcher::createKeyEvent(const KeyEntry& entry) { KeyEvent event; - event.initialize(entry.deviceId, entry.source, entry.displayId, INVALID_HMAC, entry.action, - entry.flags, entry.keyCode, entry.scanCode, entry.metaState, entry.repeatCount, - entry.downTime, entry.eventTime); + event.initialize(entry.sequenceNum, entry.deviceId, entry.source, entry.displayId, INVALID_HMAC, + entry.action, entry.flags, entry.keyCode, entry.scanCode, entry.metaState, + entry.repeatCount, entry.downTime, entry.eventTime); return event; } diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 2fb1b65d93..f05f7e5a79 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -49,9 +49,9 @@ struct PointF { static KeyEvent getTestKeyEvent() { KeyEvent event; - event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE, INVALID_HMAC, - AKEY_EVENT_ACTION_DOWN, 0, AKEYCODE_A, KEY_A, AMETA_NONE, 0, ARBITRARY_TIME, - ARBITRARY_TIME); + event.initialize(InputEvent::nextId(), DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE, + INVALID_HMAC, AKEY_EVENT_ACTION_DOWN, 0, AKEYCODE_A, KEY_A, AMETA_NONE, 0, + ARBITRARY_TIME, ARBITRARY_TIME); return event; } @@ -300,7 +300,8 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesKeyEvents) { KeyEvent event; // Rejects undefined key actions. - event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE, INVALID_HMAC, + event.initialize(InputEvent::nextId(), DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE, + INVALID_HMAC, /*action*/ -1, 0, AKEYCODE_A, KEY_A, AMETA_NONE, 0, ARBITRARY_TIME, ARBITRARY_TIME); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( @@ -309,8 +310,8 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesKeyEvents) { << "Should reject key events with undefined action."; // Rejects ACTION_MULTIPLE since it is not supported despite being defined in the API. - event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE, INVALID_HMAC, - AKEY_EVENT_ACTION_MULTIPLE, 0, AKEYCODE_A, KEY_A, AMETA_NONE, 0, + event.initialize(InputEvent::nextId(), DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE, + INVALID_HMAC, AKEY_EVENT_ACTION_MULTIPLE, 0, AKEYCODE_A, KEY_A, AMETA_NONE, 0, ARBITRARY_TIME, ARBITRARY_TIME); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, @@ -335,7 +336,7 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { constexpr MotionClassification classification = MotionClassification::NONE; // Rejects undefined motion actions. - event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, + event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, /*action*/ -1, 0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, @@ -346,7 +347,7 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { << "Should reject motion events with undefined action."; // Rejects pointer down with invalid index. - event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, + event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), 0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, @@ -358,7 +359,7 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) << "Should reject motion events with pointer down index too large."; - event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, + event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_POINTER_DOWN | (~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), 0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, @@ -371,7 +372,7 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { << "Should reject motion events with pointer down index too small."; // Rejects pointer up with invalid index. - event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, + event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), 0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, @@ -383,7 +384,7 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) << "Should reject motion events with pointer up index too large."; - event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, + event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_POINTER_UP | (~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), 0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, @@ -396,20 +397,22 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { << "Should reject motion events with pointer up index too small."; // Rejects motion events with invalid number of pointers. - event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, - edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, 0, 0, - 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, + event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, + AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification, + 1 /* xScale */, 1 /* yScale */, 0, 0, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, + ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 0, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) << "Should reject motion events with 0 pointers."; - event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, - edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, 0, 0, - 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, + event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, + AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification, + 1 /* xScale */, 1 /* yScale */, 0, 0, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, + ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ MAX_POINTERS + 1, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, @@ -418,10 +421,11 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { // Rejects motion events with invalid pointer ids. pointerProperties[0].id = -1; - event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, - edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, 0, 0, - 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, + event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, + AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification, + 1 /* xScale */, 1 /* yScale */, 0, 0, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, + ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, @@ -429,10 +433,11 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { << "Should reject motion events with pointer ids less than 0."; pointerProperties[0].id = MAX_POINTER_ID + 1; - event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, - edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, 0, 0, - 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, + event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, + AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification, + 1 /* xScale */, 1 /* yScale */, 0, 0, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, + ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, @@ -442,10 +447,11 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { // Rejects motion events with duplicate pointer ids. pointerProperties[0].id = 1; pointerProperties[1].id = 1; - event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, - edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, 0, 0, - 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, + event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, + AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification, + 1 /* xScale */, 1 /* yScale */, 0, 0, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, + ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 2, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, @@ -748,8 +754,9 @@ static int32_t injectKeyDown(const sp& dispatcher, nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); // Define a valid key down event. - event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, displayId, INVALID_HMAC, - AKEY_EVENT_ACTION_DOWN, /* flags */ 0, AKEYCODE_A, KEY_A, AMETA_NONE, + event.initialize(InputEvent::nextId(), DEVICE_ID, AINPUT_SOURCE_KEYBOARD, displayId, + INVALID_HMAC, AKEY_EVENT_ACTION_DOWN, /* flags */ 0, AKEYCODE_A, KEY_A, + AMETA_NONE, /* repeatCount */ 0, currentTime, currentTime); // Inject event until dispatch out. @@ -777,7 +784,8 @@ static int32_t injectMotionEvent(const sp& dispatcher, int32_t nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); // Define a valid motion down event. - event.initialize(DEVICE_ID, source, displayId, INVALID_HMAC, action, /* actionButton */ 0, + event.initialize(InputEvent::nextId(), DEVICE_ID, source, displayId, INVALID_HMAC, action, + /* actionButton */ 0, /* flags */ 0, /* edgeFlags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE, /* xScale */ 1, /* yScale */ 1, /* xOffset */ 0, /* yOffset */ 0, -- cgit v1.2.3-59-g8ed1b From 1c7bc86a9b1bdf16b240a96d083102127f036325 Mon Sep 17 00:00:00 2001 From: Garfield Tan Date: Tue, 28 Jan 2020 13:24:04 -0800 Subject: Let InputFlinger generate event IDs. Also send event IDs via InputMessage and add some atrace calls to form a complete chain of input event processing. Bug: 144889238 Test: systrace shows correct event IDs. Test: atest inputflinger_tests Change-Id: I3c561b03b0ba75c22115ae020e6b41855686ab64 Merged-In: I3c561b03b0ba75c22115ae020e6b41855686ab64 (cherry picked from commit ff1f1bb99489fd372c57908dafdd3817a33db0c5) --- include/input/InputTransport.h | 31 +++--- libs/input/InputTransport.cpp | 53 +++++----- .../input/tests/InputPublisherAndConsumer_test.cpp | 26 +++-- libs/input/tests/StructLayout_test.cpp | 9 +- services/inputflinger/Android.bp | 1 + services/inputflinger/InputListener.cpp | 18 ++++ services/inputflinger/dispatcher/Connection.cpp | 4 +- services/inputflinger/dispatcher/Connection.h | 2 +- services/inputflinger/dispatcher/Entry.h | 10 +- .../inputflinger/dispatcher/InputDispatcher.cpp | 72 ++++++++++---- services/inputflinger/dispatcher/InputDispatcher.h | 2 + services/inputflinger/dispatcher/InputState.cpp | 10 +- services/inputflinger/dispatcher/InputState.h | 4 +- services/inputflinger/reader/InputReader.cpp | 6 +- services/inputflinger/reader/include/InputReader.h | 4 +- .../inputflinger/tests/InputDispatcher_test.cpp | 109 +++++++++++++++++++++ services/inputflinger/tests/InputReader_test.cpp | 27 +++-- 17 files changed, 293 insertions(+), 95 deletions(-) (limited to 'include') diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h index 06fd3bb364..8ca178c1d7 100644 --- a/include/input/InputTransport.h +++ b/include/input/InputTransport.h @@ -82,7 +82,7 @@ struct InputMessage { union Body { struct Key { uint32_t seq; - uint32_t empty1; + int32_t eventId; nsecs_t eventTime __attribute__((aligned(8))); int32_t deviceId; int32_t source; @@ -102,7 +102,7 @@ struct InputMessage { struct Motion { uint32_t seq; - uint32_t empty1; + int32_t eventId; nsecs_t eventTime __attribute__((aligned(8))); int32_t deviceId; int32_t source; @@ -159,6 +159,8 @@ struct InputMessage { struct Focus { uint32_t seq; + int32_t eventId; + uint32_t empty1; // The following two fields take up 4 bytes total uint16_t hasFocus; // actually a bool uint16_t inTouchMode; // actually a bool, but we must maintain 8-byte alignment @@ -276,9 +278,9 @@ public: * Returns BAD_VALUE if seq is 0. * Other errors probably indicate that the channel is broken. */ - status_t publishKeyEvent(uint32_t seq, int32_t deviceId, int32_t source, int32_t displayId, - std::array hmac, int32_t action, int32_t flags, - int32_t keyCode, int32_t scanCode, int32_t metaState, + status_t publishKeyEvent(uint32_t seq, int32_t eventId, int32_t deviceId, int32_t source, + int32_t displayId, std::array hmac, int32_t action, + int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState, int32_t repeatCount, nsecs_t downTime, nsecs_t eventTime); /* Publishes a motion event to the input channel. @@ -289,14 +291,15 @@ public: * Returns BAD_VALUE if seq is 0 or if pointerCount is less than 1 or greater than MAX_POINTERS. * Other errors probably indicate that the channel is broken. */ - status_t publishMotionEvent(uint32_t seq, int32_t deviceId, int32_t source, int32_t displayId, - std::array hmac, int32_t action, int32_t actionButton, - int32_t flags, int32_t edgeFlags, int32_t metaState, - int32_t buttonState, MotionClassification classification, - float xScale, float yScale, float xOffset, float yOffset, - float xPrecision, float yPrecision, float xCursorPosition, - float yCursorPosition, nsecs_t downTime, nsecs_t eventTime, - uint32_t pointerCount, const PointerProperties* pointerProperties, + status_t publishMotionEvent(uint32_t seq, int32_t eventId, int32_t deviceId, int32_t source, + int32_t displayId, std::array hmac, int32_t action, + int32_t actionButton, int32_t flags, int32_t edgeFlags, + int32_t metaState, int32_t buttonState, + MotionClassification classification, float xScale, float yScale, + float xOffset, float yOffset, float xPrecision, float yPrecision, + float xCursorPosition, float yCursorPosition, nsecs_t downTime, + nsecs_t eventTime, uint32_t pointerCount, + const PointerProperties* pointerProperties, const PointerCoords* pointerCoords); /* Publishes a focus event to the input channel. @@ -306,7 +309,7 @@ public: * Returns DEAD_OBJECT if the channel's peer has been closed. * Other errors probably indicate that the channel is broken. */ - status_t publishFocusEvent(uint32_t seq, bool hasFocus, bool inTouchMode); + status_t publishFocusEvent(uint32_t seq, int32_t eventId, bool hasFocus, bool inTouchMode); /* Receives the finished signal from the consumer in reply to the original dispatch signal. * If a signal was received, returns the message sequence number, diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp index eda01c5032..7335b30a49 100644 --- a/libs/input/InputTransport.cpp +++ b/libs/input/InputTransport.cpp @@ -63,10 +63,6 @@ static const nsecs_t RESAMPLE_MAX_DELTA = 20 * NANOS_PER_MS; // far into the future. This time is further bounded by 50% of the last time delta. static const nsecs_t RESAMPLE_MAX_PREDICTION = 8 * NANOS_PER_MS; -// A placeholder sequence number used to initialize native input events before InputFlinger is -// migrated to new sequence number system. -static constexpr int32_t INPUT_FLINGER_SEQUENCE_NUM = 0; - /** * System property for enabling / disabling touch resampling. * Resampling extrapolates / interpolates the reported touch event coordinates to better @@ -143,6 +139,8 @@ void InputMessage::getSanitizedCopy(InputMessage* msg) const { case InputMessage::Type::KEY: { // uint32_t seq msg->body.key.seq = body.key.seq; + // int32_t eventId + msg->body.key.eventId = body.key.eventId; // nsecs_t eventTime msg->body.key.eventTime = body.key.eventTime; // int32_t deviceId @@ -172,6 +170,8 @@ void InputMessage::getSanitizedCopy(InputMessage* msg) const { case InputMessage::Type::MOTION: { // uint32_t seq msg->body.motion.seq = body.motion.seq; + // int32_t eventId + msg->body.motion.eventId = body.motion.eventId; // nsecs_t eventTime msg->body.motion.eventTime = body.motion.eventTime; // int32_t deviceId @@ -238,6 +238,7 @@ void InputMessage::getSanitizedCopy(InputMessage* msg) const { } case InputMessage::Type::FOCUS: { msg->body.focus.seq = body.focus.seq; + msg->body.focus.eventId = body.focus.eventId; msg->body.focus.hasFocus = body.focus.hasFocus; msg->body.focus.inTouchMode = body.focus.inTouchMode; break; @@ -436,11 +437,12 @@ InputPublisher::InputPublisher(const sp& channel) : InputPublisher::~InputPublisher() { } -status_t InputPublisher::publishKeyEvent(uint32_t seq, int32_t deviceId, int32_t source, - int32_t displayId, std::array hmac, - int32_t action, int32_t flags, int32_t keyCode, - int32_t scanCode, int32_t metaState, int32_t repeatCount, - nsecs_t downTime, nsecs_t eventTime) { +status_t InputPublisher::publishKeyEvent(uint32_t seq, int32_t eventId, int32_t deviceId, + int32_t source, int32_t displayId, + std::array hmac, int32_t action, + int32_t flags, int32_t keyCode, int32_t scanCode, + int32_t metaState, int32_t repeatCount, nsecs_t downTime, + nsecs_t eventTime) { if (ATRACE_ENABLED()) { std::string message = StringPrintf("publishKeyEvent(inputChannel=%s, keyCode=%" PRId32 ")", mChannel->getName().c_str(), keyCode); @@ -462,6 +464,7 @@ status_t InputPublisher::publishKeyEvent(uint32_t seq, int32_t deviceId, int32_t InputMessage msg; msg.header.type = InputMessage::Type::KEY; msg.body.key.seq = seq; + msg.body.key.eventId = eventId; msg.body.key.deviceId = deviceId; msg.body.key.source = source; msg.body.key.displayId = displayId; @@ -478,7 +481,7 @@ status_t InputPublisher::publishKeyEvent(uint32_t seq, int32_t deviceId, int32_t } status_t InputPublisher::publishMotionEvent( - uint32_t seq, int32_t deviceId, int32_t source, int32_t displayId, + uint32_t seq, int32_t eventId, int32_t deviceId, int32_t source, int32_t displayId, std::array hmac, int32_t action, int32_t actionButton, int32_t flags, int32_t edgeFlags, int32_t metaState, int32_t buttonState, MotionClassification classification, float xScale, float yScale, float xOffset, @@ -519,6 +522,7 @@ status_t InputPublisher::publishMotionEvent( InputMessage msg; msg.header.type = InputMessage::Type::MOTION; msg.body.motion.seq = seq; + msg.body.motion.eventId = eventId; msg.body.motion.deviceId = deviceId; msg.body.motion.source = source; msg.body.motion.displayId = displayId; @@ -549,7 +553,8 @@ status_t InputPublisher::publishMotionEvent( return mChannel->sendMessage(&msg); } -status_t InputPublisher::publishFocusEvent(uint32_t seq, bool hasFocus, bool inTouchMode) { +status_t InputPublisher::publishFocusEvent(uint32_t seq, int32_t eventId, bool hasFocus, + bool inTouchMode) { if (ATRACE_ENABLED()) { std::string message = StringPrintf("publishFocusEvent(inputChannel=%s, hasFocus=%s, inTouchMode=%s)", @@ -561,6 +566,7 @@ status_t InputPublisher::publishFocusEvent(uint32_t seq, bool hasFocus, bool inT InputMessage msg; msg.header.type = InputMessage::Type::FOCUS; msg.body.focus.seq = seq; + msg.body.focus.eventId = eventId; msg.body.focus.hasFocus = hasFocus ? 1 : 0; msg.body.focus.inTouchMode = inTouchMode ? 1 : 0; return mChannel->sendMessage(&msg); @@ -1146,7 +1152,7 @@ ssize_t InputConsumer::findTouchState(int32_t deviceId, int32_t source) const { } void InputConsumer::initializeKeyEvent(KeyEvent* event, const InputMessage* msg) { - event->initialize(INPUT_FLINGER_SEQUENCE_NUM, msg->body.key.deviceId, msg->body.key.source, + event->initialize(msg->body.key.eventId, msg->body.key.deviceId, msg->body.key.source, msg->body.key.displayId, msg->body.key.hmac, msg->body.key.action, msg->body.key.flags, msg->body.key.keyCode, msg->body.key.scanCode, msg->body.key.metaState, msg->body.key.repeatCount, msg->body.key.downTime, @@ -1154,7 +1160,7 @@ void InputConsumer::initializeKeyEvent(KeyEvent* event, const InputMessage* msg) } void InputConsumer::initializeFocusEvent(FocusEvent* event, const InputMessage* msg) { - event->initialize(INPUT_FLINGER_SEQUENCE_NUM, msg->body.focus.hasFocus == 1, + event->initialize(msg->body.focus.eventId, msg->body.focus.hasFocus == 1, msg->body.focus.inTouchMode == 1); } @@ -1167,17 +1173,16 @@ void InputConsumer::initializeMotionEvent(MotionEvent* event, const InputMessage pointerCoords[i].copyFrom(msg->body.motion.pointers[i].coords); } - event->initialize(INPUT_FLINGER_SEQUENCE_NUM, msg->body.motion.deviceId, - msg->body.motion.source, msg->body.motion.displayId, msg->body.motion.hmac, - msg->body.motion.action, msg->body.motion.actionButton, - msg->body.motion.flags, msg->body.motion.edgeFlags, - msg->body.motion.metaState, msg->body.motion.buttonState, - msg->body.motion.classification, msg->body.motion.xScale, - msg->body.motion.yScale, msg->body.motion.xOffset, msg->body.motion.yOffset, - msg->body.motion.xPrecision, msg->body.motion.yPrecision, - msg->body.motion.xCursorPosition, msg->body.motion.yCursorPosition, - msg->body.motion.downTime, msg->body.motion.eventTime, pointerCount, - pointerProperties, pointerCoords); + event->initialize(msg->body.motion.eventId, msg->body.motion.deviceId, msg->body.motion.source, + msg->body.motion.displayId, msg->body.motion.hmac, msg->body.motion.action, + msg->body.motion.actionButton, msg->body.motion.flags, + msg->body.motion.edgeFlags, msg->body.motion.metaState, + msg->body.motion.buttonState, msg->body.motion.classification, + msg->body.motion.xScale, msg->body.motion.yScale, msg->body.motion.xOffset, + msg->body.motion.yOffset, msg->body.motion.xPrecision, + msg->body.motion.yPrecision, msg->body.motion.xCursorPosition, + msg->body.motion.yCursorPosition, msg->body.motion.downTime, + msg->body.motion.eventTime, pointerCount, pointerProperties, pointerCoords); } void InputConsumer::addSample(MotionEvent* event, const InputMessage* msg) { diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp index 885196f3f3..8e2eec85ed 100644 --- a/libs/input/tests/InputPublisherAndConsumer_test.cpp +++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp @@ -73,6 +73,7 @@ void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() { status_t status; constexpr uint32_t seq = 15; + int32_t eventId = InputEvent::nextId(); constexpr int32_t deviceId = 1; constexpr uint32_t source = AINPUT_SOURCE_KEYBOARD; constexpr int32_t displayId = ADISPLAY_ID_DEFAULT; @@ -88,8 +89,8 @@ void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() { constexpr nsecs_t downTime = 3; constexpr nsecs_t eventTime = 4; - status = mPublisher->publishKeyEvent(seq, deviceId, source, displayId, hmac, action, flags, - keyCode, scanCode, metaState, repeatCount, downTime, + status = mPublisher->publishKeyEvent(seq, eventId, deviceId, source, displayId, hmac, action, + flags, keyCode, scanCode, metaState, repeatCount, downTime, eventTime); ASSERT_EQ(OK, status) << "publisher publishKeyEvent should return OK"; @@ -107,6 +108,7 @@ void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() { KeyEvent* keyEvent = static_cast(event); EXPECT_EQ(seq, consumeSeq); + EXPECT_EQ(eventId, keyEvent->getId()); EXPECT_EQ(deviceId, keyEvent->getDeviceId()); EXPECT_EQ(source, keyEvent->getSource()); EXPECT_EQ(displayId, keyEvent->getDisplayId()); @@ -139,6 +141,7 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { status_t status; constexpr uint32_t seq = 15; + int32_t eventId = InputEvent::nextId(); constexpr int32_t deviceId = 1; constexpr uint32_t source = AINPUT_SOURCE_TOUCHSCREEN; constexpr int32_t displayId = ADISPLAY_ID_DEFAULT; @@ -182,7 +185,7 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 3.5 * i); } - status = mPublisher->publishMotionEvent(seq, deviceId, source, displayId, hmac, action, + status = mPublisher->publishMotionEvent(seq, eventId, deviceId, source, displayId, hmac, action, actionButton, flags, edgeFlags, metaState, buttonState, classification, xScale, yScale, xOffset, yOffset, xPrecision, yPrecision, xCursorPosition, @@ -204,6 +207,7 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { MotionEvent* motionEvent = static_cast(event); EXPECT_EQ(seq, consumeSeq); + EXPECT_EQ(eventId, motionEvent->getId()); EXPECT_EQ(deviceId, motionEvent->getDeviceId()); EXPECT_EQ(source, motionEvent->getSource()); EXPECT_EQ(displayId, motionEvent->getDisplayId()); @@ -277,10 +281,11 @@ void InputPublisherAndConsumerTest::PublishAndConsumeFocusEvent() { status_t status; constexpr uint32_t seq = 15; + int32_t eventId = InputEvent::nextId(); constexpr bool hasFocus = true; constexpr bool inTouchMode = true; - status = mPublisher->publishFocusEvent(seq, hasFocus, inTouchMode); + status = mPublisher->publishFocusEvent(seq, eventId, hasFocus, inTouchMode); ASSERT_EQ(OK, status) << "publisher publishKeyEvent should return OK"; uint32_t consumeSeq; @@ -294,6 +299,7 @@ void InputPublisherAndConsumerTest::PublishAndConsumeFocusEvent() { FocusEvent* focusEvent = static_cast(event); EXPECT_EQ(seq, consumeSeq); + EXPECT_EQ(eventId, focusEvent->getId()); EXPECT_EQ(hasFocus, focusEvent->getHasFocus()); EXPECT_EQ(inTouchMode, focusEvent->getInTouchMode()); @@ -332,8 +338,8 @@ TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenSequenceNumberIsZer pointerCoords[i].clear(); } - status = mPublisher->publishMotionEvent(0, 0, 0, 0, INVALID_HMAC, 0, 0, 0, 0, 0, 0, - MotionClassification::NONE, 1 /* xScale */, + status = mPublisher->publishMotionEvent(0, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0, + 0, 0, 0, MotionClassification::NONE, 1 /* xScale */, 1 /* yScale */, 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0, @@ -348,8 +354,8 @@ TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenPointerCountLessTha PointerProperties pointerProperties[pointerCount]; PointerCoords pointerCoords[pointerCount]; - status = mPublisher->publishMotionEvent(1, 0, 0, 0, INVALID_HMAC, 0, 0, 0, 0, 0, 0, - MotionClassification::NONE, 1 /* xScale */, + status = mPublisher->publishMotionEvent(1, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0, + 0, 0, 0, MotionClassification::NONE, 1 /* xScale */, 1 /* yScale */, 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0, @@ -369,8 +375,8 @@ TEST_F(InputPublisherAndConsumerTest, pointerCoords[i].clear(); } - status = mPublisher->publishMotionEvent(1, 0, 0, 0, INVALID_HMAC, 0, 0, 0, 0, 0, 0, - MotionClassification::NONE, 1 /* xScale */, + status = mPublisher->publishMotionEvent(1, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0, + 0, 0, 0, MotionClassification::NONE, 1 /* xScale */, 1 /* yScale */, 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0, diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp index dd127fcabd..1fe7bb90ca 100644 --- a/libs/input/tests/StructLayout_test.cpp +++ b/libs/input/tests/StructLayout_test.cpp @@ -35,6 +35,7 @@ void TestInputMessageAlignment() { CHECK_OFFSET(InputMessage, body, 8); CHECK_OFFSET(InputMessage::Body::Key, seq, 0); + CHECK_OFFSET(InputMessage::Body::Key, eventId, 4); CHECK_OFFSET(InputMessage::Body::Key, eventTime, 8); CHECK_OFFSET(InputMessage::Body::Key, deviceId, 16); CHECK_OFFSET(InputMessage::Body::Key, source, 20); @@ -49,6 +50,7 @@ void TestInputMessageAlignment() { CHECK_OFFSET(InputMessage::Body::Key, downTime, 88); CHECK_OFFSET(InputMessage::Body::Motion, seq, 0); + CHECK_OFFSET(InputMessage::Body::Motion, eventId, 4); CHECK_OFFSET(InputMessage::Body::Motion, eventTime, 8); CHECK_OFFSET(InputMessage::Body::Motion, deviceId, 16); CHECK_OFFSET(InputMessage::Body::Motion, source, 20); @@ -74,8 +76,9 @@ void TestInputMessageAlignment() { CHECK_OFFSET(InputMessage::Body::Motion, pointers, 136); CHECK_OFFSET(InputMessage::Body::Focus, seq, 0); - CHECK_OFFSET(InputMessage::Body::Focus, hasFocus, 4); - CHECK_OFFSET(InputMessage::Body::Focus, inTouchMode, 6); + CHECK_OFFSET(InputMessage::Body::Focus, eventId, 4); + CHECK_OFFSET(InputMessage::Body::Focus, hasFocus, 12); + CHECK_OFFSET(InputMessage::Body::Focus, inTouchMode, 14); CHECK_OFFSET(InputMessage::Body::Finished, seq, 0); CHECK_OFFSET(InputMessage::Body::Finished, handled, 4); @@ -95,7 +98,7 @@ void TestBodySize() { offsetof(InputMessage::Body::Motion, pointers) + sizeof(InputMessage::Body::Motion::Pointer) * MAX_POINTERS); static_assert(sizeof(InputMessage::Body::Finished) == 8); - static_assert(sizeof(InputMessage::Body::Focus) == 8); + static_assert(sizeof(InputMessage::Body::Focus) == 16); } // --- VerifiedInputEvent --- diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp index 439d9bf312..4ec4e25010 100644 --- a/services/inputflinger/Android.bp +++ b/services/inputflinger/Android.bp @@ -108,6 +108,7 @@ cc_defaults { srcs: [":libinputflinger_base_sources"], shared_libs: [ "libbase", + "libcutils", "libinput", "liblog", "libutils", diff --git a/services/inputflinger/InputListener.cpp b/services/inputflinger/InputListener.cpp index e91e803fb8..84838ec8a7 100644 --- a/services/inputflinger/InputListener.cpp +++ b/services/inputflinger/InputListener.cpp @@ -16,12 +16,18 @@ #define LOG_TAG "InputListener" +#define ATRACE_TAG ATRACE_TAG_INPUT + //#define LOG_NDEBUG 0 #include "InputListener.h" +#include #include #include +#include + +using android::base::StringPrintf; namespace android { @@ -228,6 +234,13 @@ void NotifyDeviceResetArgs::notify(const sp& listener) c // --- QueuedInputListener --- +static inline void traceEvent(const char* functionName, int32_t id) { + if (ATRACE_ENABLED()) { + std::string message = StringPrintf("%s(id=0x%" PRIx32 ")", functionName, id); + ATRACE_NAME(message.c_str()); + } +} + QueuedInputListener::QueuedInputListener(const sp& innerListener) : mInnerListener(innerListener) { } @@ -241,22 +254,27 @@ QueuedInputListener::~QueuedInputListener() { void QueuedInputListener::notifyConfigurationChanged( const NotifyConfigurationChangedArgs* args) { + traceEvent(__func__, args->id); mArgsQueue.push_back(new NotifyConfigurationChangedArgs(*args)); } void QueuedInputListener::notifyKey(const NotifyKeyArgs* args) { + traceEvent(__func__, args->id); mArgsQueue.push_back(new NotifyKeyArgs(*args)); } void QueuedInputListener::notifyMotion(const NotifyMotionArgs* args) { + traceEvent(__func__, args->id); mArgsQueue.push_back(new NotifyMotionArgs(*args)); } void QueuedInputListener::notifySwitch(const NotifySwitchArgs* args) { + traceEvent(__func__, args->id); mArgsQueue.push_back(new NotifySwitchArgs(*args)); } void QueuedInputListener::notifyDeviceReset(const NotifyDeviceResetArgs* args) { + traceEvent(__func__, args->id); mArgsQueue.push_back(new NotifyDeviceResetArgs(*args)); } diff --git a/services/inputflinger/dispatcher/Connection.cpp b/services/inputflinger/dispatcher/Connection.cpp index 6f82f4fd10..188212bccf 100644 --- a/services/inputflinger/dispatcher/Connection.cpp +++ b/services/inputflinger/dispatcher/Connection.cpp @@ -20,11 +20,13 @@ namespace android::inputdispatcher { -Connection::Connection(const sp& inputChannel, bool monitor) +Connection::Connection(const sp& inputChannel, bool monitor, + const IdGenerator& idGenerator) : status(STATUS_NORMAL), inputChannel(inputChannel), monitor(monitor), inputPublisher(inputChannel), + inputState(idGenerator), inputPublisherBlocked(false) {} Connection::~Connection() {} diff --git a/services/inputflinger/dispatcher/Connection.h b/services/inputflinger/dispatcher/Connection.h index 8423010502..bb3f2fee19 100644 --- a/services/inputflinger/dispatcher/Connection.h +++ b/services/inputflinger/dispatcher/Connection.h @@ -58,7 +58,7 @@ public: // yet received a "finished" response from the application. std::deque waitQueue; - explicit Connection(const sp& inputChannel, bool monitor); + Connection(const sp& inputChannel, bool monitor, const IdGenerator& idGenerator); inline const std::string getInputChannelName() const { return inputChannel->getName(); } diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h index c58ae23cc5..ab481bd411 100644 --- a/services/inputflinger/dispatcher/Entry.h +++ b/services/inputflinger/dispatcher/Entry.h @@ -29,9 +29,6 @@ namespace android::inputdispatcher { -// Sequence number for synthesized or injected events. -constexpr int32_t SYNTHESIZED_EVENT_ID = 0; - struct EventEntry { enum class Type { CONFIGURATION_CHANGED, @@ -78,7 +75,9 @@ struct EventEntry { * Key repeat is a synthesized event, because it is related to an actual hardware state * (a key is currently pressed), but the repeat itself is generated by the framework. */ - inline bool isSynthesized() const { return isInjected() || id == SYNTHESIZED_EVENT_ID; } + inline bool isSynthesized() const { + return isInjected() || IdGenerator::getSource(id) != IdGenerator::Source::INPUT_READER; + } void release(); @@ -199,7 +198,8 @@ struct DispatchEntry { float windowYScale = 1.0f; nsecs_t deliveryTime; // time when the event was actually delivered - // Set to the resolved action and flags when the event is enqueued. + // Set to the resolved ID, action and flags when the event is enqueued. + int32_t resolvedEventId; int32_t resolvedAction; int32_t resolvedFlags; diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 2f7b5ad9e6..308d19b085 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -383,6 +383,7 @@ InputDispatcher::InputDispatcher(const sp& polic : mPolicy(policy), mPendingEvent(nullptr), mLastDropReason(DropReason::NOT_DROPPED), + mIdGenerator(IdGenerator::Source::INPUT_DISPATCHER), mAppSwitchSawKeyDown(false), mAppSwitchDueTime(LONG_LONG_MAX), mNextUnblockedEvent(nullptr), @@ -925,12 +926,13 @@ KeyEntry* InputDispatcher::synthesizeKeyRepeatLocked(nsecs_t currentTime) { (POLICY_FLAG_RAW_MASK | POLICY_FLAG_PASS_TO_USER | POLICY_FLAG_TRUSTED); if (entry->refCount == 1) { entry->recycle(); + entry->id = mIdGenerator.nextId(); entry->eventTime = currentTime; entry->policyFlags = policyFlags; entry->repeatCount += 1; } else { KeyEntry* newEntry = - new KeyEntry(SYNTHESIZED_EVENT_ID, currentTime, entry->deviceId, entry->source, + new KeyEntry(mIdGenerator.nextId(), currentTime, entry->deviceId, entry->source, entry->displayId, policyFlags, entry->action, entry->flags, entry->keyCode, entry->scanCode, entry->metaState, entry->repeatCount + 1, entry->downTime); @@ -981,7 +983,7 @@ bool InputDispatcher::dispatchDeviceResetLocked(nsecs_t currentTime, DeviceReset void InputDispatcher::enqueueFocusEventLocked(const InputWindowHandle& window, bool hasFocus) { FocusEntry* focusEntry = - new FocusEntry(SYNTHESIZED_EVENT_ID, now(), window.getToken(), hasFocus); + new FocusEntry(mIdGenerator.nextId(), now(), window.getToken(), hasFocus); enqueueInboundEventLocked(focusEntry); } @@ -2188,7 +2190,7 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, const InputTarget& inputTarget) { if (ATRACE_ENABLED()) { std::string message = - StringPrintf("prepareDispatchCycleLocked(inputChannel=%s, id=%" PRIx32 ")", + StringPrintf("prepareDispatchCycleLocked(inputChannel=%s, id=0x%" PRIx32 ")", connection->getInputChannelName().c_str(), eventEntry->id); ATRACE_NAME(message.c_str()); } @@ -2244,7 +2246,7 @@ void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime, const InputTarget& inputTarget) { if (ATRACE_ENABLED()) { std::string message = - StringPrintf("enqueueDispatchEntriesLocked(inputChannel=%s, id=%" PRIx32 ")", + StringPrintf("enqueueDispatchEntriesLocked(inputChannel=%s, id=0x%" PRIx32 ")", connection->getInputChannelName().c_str(), eventEntry->id); ATRACE_NAME(message.c_str()); } @@ -2299,6 +2301,7 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp& connectio switch (newEntry->type) { case EventEntry::Type::KEY: { const KeyEntry& keyEntry = static_cast(*newEntry); + dispatchEntry->resolvedEventId = keyEntry.id; dispatchEntry->resolvedAction = keyEntry.action; dispatchEntry->resolvedFlags = keyEntry.flags; @@ -2315,6 +2318,11 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp& connectio case EventEntry::Type::MOTION: { const MotionEntry& motionEntry = static_cast(*newEntry); + // Assign a default value to dispatchEntry that will never be generated by InputReader, + // and assign a InputDispatcher value if it doesn't change in the if-else chain below. + constexpr int32_t DEFAULT_RESOLVED_EVENT_ID = + static_cast(IdGenerator::Source::OTHER); + dispatchEntry->resolvedEventId = DEFAULT_RESOLVED_EVENT_ID; if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) { dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_OUTSIDE; } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT) { @@ -2327,6 +2335,7 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp& connectio dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_DOWN; } else { dispatchEntry->resolvedAction = motionEntry.action; + dispatchEntry->resolvedEventId = motionEntry.id; } if (dispatchEntry->resolvedAction == AMOTION_EVENT_ACTION_HOVER_MOVE && !connection->inputState.isHovering(motionEntry.deviceId, motionEntry.source, @@ -2357,6 +2366,17 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp& connectio return; // skip the inconsistent event } + dispatchEntry->resolvedEventId = + dispatchEntry->resolvedEventId == DEFAULT_RESOLVED_EVENT_ID + ? mIdGenerator.nextId() + : motionEntry.id; + if (ATRACE_ENABLED() && dispatchEntry->resolvedEventId != motionEntry.id) { + std::string message = StringPrintf("Transmute MotionEvent(id=0x%" PRIx32 + ") to MotionEvent(id=0x%" PRIx32 ").", + motionEntry.id, dispatchEntry->resolvedEventId); + ATRACE_NAME(message.c_str()); + } + dispatchPointerDownOutsideFocus(motionEntry.source, dispatchEntry->resolvedAction, inputTarget.inputChannel->getConnectionToken()); @@ -2438,14 +2458,16 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, std::array hmac = mHmacKeyManager.sign(verifiedEvent); // Publish the key event. - status = connection->inputPublisher - .publishKeyEvent(dispatchEntry->seq, keyEntry->deviceId, - keyEntry->source, keyEntry->displayId, - std::move(hmac), dispatchEntry->resolvedAction, - dispatchEntry->resolvedFlags, keyEntry->keyCode, - keyEntry->scanCode, keyEntry->metaState, - keyEntry->repeatCount, keyEntry->downTime, - keyEntry->eventTime); + status = + connection->inputPublisher + .publishKeyEvent(dispatchEntry->seq, dispatchEntry->resolvedEventId, + keyEntry->deviceId, keyEntry->source, + keyEntry->displayId, std::move(hmac), + dispatchEntry->resolvedAction, + dispatchEntry->resolvedFlags, keyEntry->keyCode, + keyEntry->scanCode, keyEntry->metaState, + keyEntry->repeatCount, keyEntry->downTime, + keyEntry->eventTime); break; } @@ -2494,9 +2516,11 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, // Publish the motion event. status = connection->inputPublisher - .publishMotionEvent(dispatchEntry->seq, motionEntry->deviceId, - motionEntry->source, motionEntry->displayId, - std::move(hmac), dispatchEntry->resolvedAction, + .publishMotionEvent(dispatchEntry->seq, + dispatchEntry->resolvedEventId, + motionEntry->deviceId, motionEntry->source, + motionEntry->displayId, std::move(hmac), + dispatchEntry->resolvedAction, motionEntry->actionButton, dispatchEntry->resolvedFlags, motionEntry->edgeFlags, motionEntry->metaState, @@ -2515,6 +2539,7 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, case EventEntry::Type::FOCUS: { FocusEntry* focusEntry = static_cast(eventEntry); status = connection->inputPublisher.publishFocusEvent(dispatchEntry->seq, + focusEntry->id, focusEntry->hasFocus, mInTouchMode); break; @@ -2920,10 +2945,17 @@ MotionEntry* InputDispatcher::splitMotionEvent(const MotionEntry& originalMotion } } + int32_t newId = mIdGenerator.nextId(); + if (ATRACE_ENABLED()) { + std::string message = StringPrintf("Split MotionEvent(id=0x%" PRIx32 + ") to MotionEvent(id=0x%" PRIx32 ").", + originalMotionEntry.id, newId); + ATRACE_NAME(message.c_str()); + } MotionEntry* splitMotionEntry = - new MotionEntry(originalMotionEntry.id, originalMotionEntry.eventTime, - originalMotionEntry.deviceId, originalMotionEntry.source, - originalMotionEntry.displayId, originalMotionEntry.policyFlags, action, + new MotionEntry(newId, originalMotionEntry.eventTime, originalMotionEntry.deviceId, + originalMotionEntry.source, originalMotionEntry.displayId, + originalMotionEntry.policyFlags, action, originalMotionEntry.actionButton, originalMotionEntry.flags, originalMotionEntry.metaState, originalMotionEntry.buttonState, originalMotionEntry.classification, originalMotionEntry.edgeFlags, @@ -4225,7 +4257,7 @@ status_t InputDispatcher::registerInputChannel(const sp& inputChan return BAD_VALUE; } - sp connection = new Connection(inputChannel, false /*monitor*/); + sp connection = new Connection(inputChannel, false /*monitor*/, mIdGenerator); int fd = inputChannel->getFd(); mConnectionsByFd[fd] = connection; @@ -4254,7 +4286,7 @@ status_t InputDispatcher::registerInputMonitor(const sp& inputChan return BAD_VALUE; } - sp connection = new Connection(inputChannel, true /*monitor*/); + sp connection = new Connection(inputChannel, true /*monitor*/, mIdGenerator); const int fd = inputChannel->getFd(); mConnectionsByFd[fd] = connection; diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index 482133ee90..4aa47f89f0 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -156,6 +156,8 @@ private: DropReason mLastDropReason GUARDED_BY(mLock); + const IdGenerator mIdGenerator; + // With each iteration, InputDispatcher nominally processes one queued event, // a timeout, or a response from an input consumer. // This method should only be called on the input dispatcher's own thread. diff --git a/services/inputflinger/dispatcher/InputState.cpp b/services/inputflinger/dispatcher/InputState.cpp index 7fa9e09aad..386056d9b2 100644 --- a/services/inputflinger/dispatcher/InputState.cpp +++ b/services/inputflinger/dispatcher/InputState.cpp @@ -16,9 +16,11 @@ #include "InputState.h" +#include "InputDispatcher.h" + namespace android::inputdispatcher { -InputState::InputState() {} +InputState::InputState(const IdGenerator& idGenerator) : mIdGenerator(idGenerator) {} InputState::~InputState() {} @@ -268,7 +270,7 @@ std::vector InputState::synthesizeCancelationEvents( std::vector events; for (KeyMemento& memento : mKeyMementos) { if (shouldCancelKey(memento, options)) { - events.push_back(new KeyEntry(SYNTHESIZED_EVENT_ID, currentTime, memento.deviceId, + events.push_back(new KeyEntry(mIdGenerator.nextId(), currentTime, memento.deviceId, memento.source, memento.displayId, memento.policyFlags, AKEY_EVENT_ACTION_UP, memento.flags | AKEY_EVENT_FLAG_CANCELED, memento.keyCode, @@ -281,7 +283,7 @@ std::vector InputState::synthesizeCancelationEvents( if (shouldCancelMotion(memento, options)) { const int32_t action = memento.hovering ? AMOTION_EVENT_ACTION_HOVER_EXIT : AMOTION_EVENT_ACTION_CANCEL; - events.push_back(new MotionEntry(SYNTHESIZED_EVENT_ID, currentTime, memento.deviceId, + events.push_back(new MotionEntry(mIdGenerator.nextId(), currentTime, memento.deviceId, memento.source, memento.displayId, memento.policyFlags, action, 0 /*actionButton*/, memento.flags, AMETA_NONE, 0 /*buttonState*/, MotionClassification::NONE, @@ -331,7 +333,7 @@ std::vector InputState::synthesizePointerDownEvents(nsecs_t current : AMOTION_EVENT_ACTION_POINTER_DOWN | (i << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); - events.push_back(new MotionEntry(SYNTHESIZED_EVENT_ID, currentTime, memento.deviceId, + events.push_back(new MotionEntry(mIdGenerator.nextId(), currentTime, memento.deviceId, memento.source, memento.displayId, memento.policyFlags, action, 0 /*actionButton*/, memento.flags, AMETA_NONE, 0 /*buttonState*/, MotionClassification::NONE, diff --git a/services/inputflinger/dispatcher/InputState.h b/services/inputflinger/dispatcher/InputState.h index 08266ae9cd..d97a664d74 100644 --- a/services/inputflinger/dispatcher/InputState.h +++ b/services/inputflinger/dispatcher/InputState.h @@ -30,7 +30,7 @@ static constexpr int32_t INVALID_POINTER_INDEX = -1; * synthesized when events are dropped. */ class InputState { public: - InputState(); + explicit InputState(const IdGenerator& idGenerator); ~InputState(); // Returns true if there is no state to be canceled. @@ -111,6 +111,8 @@ private: void mergePointerStateTo(MotionMemento& other) const; }; + const IdGenerator& mIdGenerator; // InputDispatcher owns it so we won't have dangling reference. + std::vector mKeyMementos; std::vector mMotionMementos; KeyedVector mFallbackKeys; diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp index 2998cc9619..657a134865 100644 --- a/services/inputflinger/reader/InputReader.cpp +++ b/services/inputflinger/reader/InputReader.cpp @@ -46,7 +46,6 @@ InputReader::InputReader(std::shared_ptr eventHub, : mContext(this), mEventHub(eventHub), mPolicy(policy), - mNextId(1), mGlobalMetaState(0), mGeneration(1), mNextInputDeviceId(END_RESERVED_ID), @@ -697,7 +696,8 @@ void InputReader::monitor() { // --- InputReader::ContextImpl --- -InputReader::ContextImpl::ContextImpl(InputReader* reader) : mReader(reader) {} +InputReader::ContextImpl::ContextImpl(InputReader* reader) + : mReader(reader), mIdGenerator(IdGenerator::Source::INPUT_READER) {} void InputReader::ContextImpl::updateGlobalMetaState() { // lock is already held by the input loop @@ -762,7 +762,7 @@ EventHubInterface* InputReader::ContextImpl::getEventHub() { } int32_t InputReader::ContextImpl::getNextId() { - return (mReader->mNextId)++; + return mIdGenerator.nextId(); } } // namespace android diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h index eaa105a8f6..693ec30b7d 100644 --- a/services/inputflinger/reader/include/InputReader.h +++ b/services/inputflinger/reader/include/InputReader.h @@ -94,6 +94,7 @@ protected: class ContextImpl : public InputReaderContext { InputReader* mReader; + IdGenerator mIdGenerator; public: explicit ContextImpl(InputReader* reader); @@ -132,9 +133,6 @@ private: InputReaderConfiguration mConfig; - // used by InputReaderContext::getNextId() as a counter for event sequence numbers - uint32_t mNextId; - // The event queue. static const int EVENT_BUFFER_SIZE = 256; RawEvent mEventBuffer[EVENT_BUFFER_SIZE]; diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 270f891d36..d30c4f1635 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -16,13 +16,18 @@ #include "../dispatcher/InputDispatcher.h" +#include #include #include #include #include +#include +#include #include +using android::base::StringPrintf; + namespace android::inputdispatcher { // An arbitrary time value. @@ -107,6 +112,11 @@ public: << "Expected onPointerDownOutsideFocus to not have been called"; } + void setKeyRepeatConfiguration(nsecs_t timeout, nsecs_t delay) { + mConfig.keyRepeatTimeout = timeout; + mConfig.keyRepeatDelay = delay; + } + private: std::unique_ptr mFilteredEvent; std::optional mConfigurationChangedTime; @@ -1443,6 +1453,105 @@ TEST_F(InputDispatcherTest, VerifyInputEvent_KeyEvent) { ASSERT_EQ(0, verifiedKey.repeatCount); } +class InputDispatcherKeyRepeatTest : public InputDispatcherTest { +protected: + static constexpr nsecs_t KEY_REPEAT_TIMEOUT = 40 * 1000000; // 40 ms + static constexpr nsecs_t KEY_REPEAT_DELAY = 40 * 1000000; // 40 ms + + sp mApp; + sp mWindow; + + virtual void SetUp() override { + mFakePolicy = new FakeInputDispatcherPolicy(); + mFakePolicy->setKeyRepeatConfiguration(KEY_REPEAT_TIMEOUT, KEY_REPEAT_DELAY); + mDispatcher = new InputDispatcher(mFakePolicy); + mDispatcher->setInputDispatchMode(/*enabled*/ true, /*frozen*/ false); + ASSERT_EQ(OK, mDispatcher->start()); + + setUpWindow(); + } + + void setUpWindow() { + mApp = new FakeApplicationHandle(); + mWindow = new FakeWindowHandle(mApp, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); + + mWindow->setFocus(true); + mDispatcher->setInputWindows({mWindow}, ADISPLAY_ID_DEFAULT); + + mWindow->consumeFocusEvent(true); + } + + void sendAndConsumeKeyDown() { + NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT); + keyArgs.policyFlags |= POLICY_FLAG_TRUSTED; // Otherwise it won't generate repeat event + mDispatcher->notifyKey(&keyArgs); + + // Window should receive key down event. + mWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT); + } + + void expectKeyRepeatOnce(int32_t repeatCount) { + SCOPED_TRACE(StringPrintf("Checking event with repeat count %" PRId32, repeatCount)); + InputEvent* repeatEvent = mWindow->consume(); + ASSERT_NE(nullptr, repeatEvent); + + uint32_t eventType = repeatEvent->getType(); + ASSERT_EQ(AINPUT_EVENT_TYPE_KEY, eventType); + + KeyEvent* repeatKeyEvent = static_cast(repeatEvent); + uint32_t eventAction = repeatKeyEvent->getAction(); + EXPECT_EQ(AKEY_EVENT_ACTION_DOWN, eventAction); + EXPECT_EQ(repeatCount, repeatKeyEvent->getRepeatCount()); + } + + void sendAndConsumeKeyUp() { + NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT); + keyArgs.policyFlags |= POLICY_FLAG_TRUSTED; // Unless it won't generate repeat event + mDispatcher->notifyKey(&keyArgs); + + // Window should receive key down event. + mWindow->consumeEvent(AINPUT_EVENT_TYPE_KEY, AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT, + 0 /*expectedFlags*/); + } +}; + +TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_ReceivesKeyRepeat) { + sendAndConsumeKeyDown(); + for (int32_t repeatCount = 1; repeatCount <= 10; ++repeatCount) { + expectKeyRepeatOnce(repeatCount); + } +} + +TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_StopsKeyRepeatAfterUp) { + sendAndConsumeKeyDown(); + expectKeyRepeatOnce(1 /*repeatCount*/); + sendAndConsumeKeyUp(); + mWindow->assertNoEvents(); +} + +TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_RepeatKeyEventsUseEventIdFromInputDispatcher) { + sendAndConsumeKeyDown(); + for (int32_t repeatCount = 1; repeatCount <= 10; ++repeatCount) { + InputEvent* repeatEvent = mWindow->consume(); + ASSERT_NE(nullptr, repeatEvent) << "Didn't receive event with repeat count " << repeatCount; + EXPECT_EQ(IdGenerator::Source::INPUT_DISPATCHER, + IdGenerator::getSource(repeatEvent->getId())); + } +} + +TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_RepeatKeyEventsUseUniqueEventId) { + sendAndConsumeKeyDown(); + + std::unordered_set idSet; + for (int32_t repeatCount = 1; repeatCount <= 10; ++repeatCount) { + InputEvent* repeatEvent = mWindow->consume(); + ASSERT_NE(nullptr, repeatEvent) << "Didn't receive event with repeat count " << repeatCount; + int32_t id = repeatEvent->getId(); + EXPECT_EQ(idSet.end(), idSet.find(id)); + idSet.insert(id); + } +} + /* Test InputDispatcher for MultiDisplay */ class InputDispatcherFocusOnTwoDisplaysTest : public InputDispatcherTest { public: diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index 46cd4809fc..3ae8b56c11 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -1642,7 +1642,7 @@ TEST_F(InputReaderTest, LoopOnce_ForwardsRawEventsToMappers) { ASSERT_EQ(1, event.value); } -TEST_F(InputReaderTest, DeviceReset_IncrementsId) { +TEST_F(InputReaderTest, DeviceReset_RandomId) { constexpr int32_t deviceId = END_RESERVED_ID + 1000; constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD; constexpr int32_t eventHubId = 1; @@ -1659,22 +1659,37 @@ TEST_F(InputReaderTest, DeviceReset_IncrementsId) { disableDevice(deviceId); mReader->loopOnce(); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs)); - ASSERT_TRUE(prevId < resetArgs.id); + ASSERT_NE(prevId, resetArgs.id); prevId = resetArgs.id; enableDevice(deviceId); mReader->loopOnce(); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs)); - ASSERT_TRUE(prevId < resetArgs.id); + ASSERT_NE(prevId, resetArgs.id); prevId = resetArgs.id; disableDevice(deviceId); mReader->loopOnce(); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs)); - ASSERT_TRUE(prevId < resetArgs.id); + ASSERT_NE(prevId, resetArgs.id); prevId = resetArgs.id; } +TEST_F(InputReaderTest, DeviceReset_GenerateIdWithInputReaderSource) { + constexpr int32_t deviceId = 1; + constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD; + constexpr int32_t eventHubId = 1; + std::shared_ptr device = mReader->newDevice(deviceId, "fake"); + // Must add at least one mapper or the device will be ignored! + device->addMapper(eventHubId, AINPUT_SOURCE_KEYBOARD); + mReader->setNextDevice(device); + ASSERT_NO_FATAL_FAILURE(addDevice(deviceId, "fake", deviceClass, nullptr)); + + NotifyDeviceResetArgs resetArgs; + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs)); + ASSERT_EQ(IdGenerator::Source::INPUT_READER, IdGenerator::getSource(resetArgs.id)); +} + TEST_F(InputReaderTest, Device_CanDispatchToDisplay) { constexpr int32_t deviceId = END_RESERVED_ID + 1000; constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD; @@ -1821,14 +1836,14 @@ TEST_F(InputReaderIntegrationTest, SendsEventsToInputListener) { keyboard->pressAndReleaseHomeKey(); ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(&keyArgs)); ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action); - ASSERT_LT(prevId, keyArgs.id); + ASSERT_NE(prevId, keyArgs.id); prevId = keyArgs.id; ASSERT_LE(prevTimestamp, keyArgs.eventTime); prevTimestamp = keyArgs.eventTime; ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(&keyArgs)); ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action); - ASSERT_LT(prevId, keyArgs.id); + ASSERT_NE(prevId, keyArgs.id); ASSERT_LE(prevTimestamp, keyArgs.eventTime); } -- cgit v1.2.3-59-g8ed1b From fbcf9d77fe6321ba7e03d50f965e8c16ea4290c3 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Mon, 24 Feb 2020 15:26:37 -0800 Subject: Make bitmap.h C-compatible when __ANDROID_API__ >= 30 C requires a typedef for AHardwareBuffer. Fixes: frameworks/native/include/android/bitmap.h:246:9: error: must use 'struct' tag to refer to type 'AHardwareBuffer' Fixes: 150165785 Test: m checkbuild Change-Id: I6d475e91317ff7a9264144d4cd6c6c9b46d10196 --- include/android/bitmap.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/android/bitmap.h b/include/android/bitmap.h index 727a4af2f9..2631b144af 100644 --- a/include/android/bitmap.h +++ b/include/android/bitmap.h @@ -227,6 +227,7 @@ int AndroidBitmap_compress(const AndroidBitmapInfo* info, AndroidBitmap_CompressWriteFunc fn) __INTRODUCED_IN(30); struct AHardwareBuffer; +typedef struct AHardwareBuffer AHardwareBuffer; /** * Retrieve the native object associated with a HARDWARE Bitmap. -- cgit v1.2.3-59-g8ed1b From b11abe70adc17204605946cbe4b71b6705fd19b1 Mon Sep 17 00:00:00 2001 From: Tim Murray Date: Mon, 24 Feb 2020 21:30:20 +0000 Subject: Revert "libui: rewrite Region with FatVector" Revert submission 10248126-fatvector-region Reason for revert: b/149254345 Reverted Changes: I09dc2fddd:hwui: remove FatVector I265c6c831:libui: rewrite Region with FatVector Change-Id: I2697cea4d4714592fa6a1f170b53ff51d2d62714 Test: boots Bug: 149254345 Change-Id: Ib60dbf3ef41d6439fba095e3e905580bb59d3739 --- include/ui/FatVector.h | 1 - libs/ui/Region.cpp | 94 ++++++++++++++++++------------------- libs/ui/include/ui/FatVector.h | 93 ------------------------------------ libs/ui/include/ui/Region.h | 7 +-- libs/ui/include_vndk/ui/FatVector.h | 1 - 5 files changed, 50 insertions(+), 146 deletions(-) delete mode 120000 include/ui/FatVector.h delete mode 100644 libs/ui/include/ui/FatVector.h delete mode 120000 libs/ui/include_vndk/ui/FatVector.h (limited to 'include') diff --git a/include/ui/FatVector.h b/include/ui/FatVector.h deleted file mode 120000 index c2047c07e1..0000000000 --- a/include/ui/FatVector.h +++ /dev/null @@ -1 +0,0 @@ -../../libs/ui/include/ui/FatVector.h \ No newline at end of file diff --git a/libs/ui/Region.cpp b/libs/ui/Region.cpp index cd2a448c3e..bf487c4aec 100644 --- a/libs/ui/Region.cpp +++ b/libs/ui/Region.cpp @@ -67,20 +67,19 @@ const Region Region::INVALID_REGION(Rect::INVALID_RECT); // ---------------------------------------------------------------------------- Region::Region() { - mStorage.push_back(Rect(0, 0)); + mStorage.add(Rect(0,0)); } Region::Region(const Region& rhs) + : mStorage(rhs.mStorage) { - mStorage.clear(); - mStorage.insert(mStorage.begin(), rhs.mStorage.begin(), rhs.mStorage.end()); #if defined(VALIDATE_REGIONS) validate(rhs, "rhs copy-ctor"); #endif } Region::Region(const Rect& rhs) { - mStorage.push_back(rhs); + mStorage.add(rhs); } Region::~Region() @@ -101,8 +100,8 @@ Region::~Region() * final, correctly ordered region buffer. Each rectangle will be compared with the span directly * above it, and subdivided to resolve any remaining T-junctions. */ -static void reverseRectsResolvingJunctions(const Rect* begin, const Rect* end, FatVector& dst, - int spanDirection) { +static void reverseRectsResolvingJunctions(const Rect* begin, const Rect* end, + Vector& dst, int spanDirection) { dst.clear(); const Rect* current = end - 1; @@ -110,7 +109,7 @@ static void reverseRectsResolvingJunctions(const Rect* begin, const Rect* end, F // add first span immediately do { - dst.push_back(*current); + dst.add(*current); current--; } while (current->top == lastTop && current >= begin); @@ -148,12 +147,12 @@ static void reverseRectsResolvingJunctions(const Rect* begin, const Rect* end, F if (prev.right <= left) break; if (prev.right > left && prev.right < right) { - dst.push_back(Rect(prev.right, top, right, bottom)); + dst.add(Rect(prev.right, top, right, bottom)); right = prev.right; } if (prev.left > left && prev.left < right) { - dst.push_back(Rect(prev.left, top, right, bottom)); + dst.add(Rect(prev.left, top, right, bottom)); right = prev.left; } @@ -167,12 +166,12 @@ static void reverseRectsResolvingJunctions(const Rect* begin, const Rect* end, F if (prev.left >= right) break; if (prev.left > left && prev.left < right) { - dst.push_back(Rect(left, top, prev.left, bottom)); + dst.add(Rect(left, top, prev.left, bottom)); left = prev.left; } if (prev.right > left && prev.right < right) { - dst.push_back(Rect(left, top, prev.right, bottom)); + dst.add(Rect(left, top, prev.right, bottom)); left = prev.right; } // if an entry in the previous span is too far left, nothing further right in the @@ -184,7 +183,7 @@ static void reverseRectsResolvingJunctions(const Rect* begin, const Rect* end, F } if (left < right) { - dst.push_back(Rect(left, top, right, bottom)); + dst.add(Rect(left, top, right, bottom)); } current--; @@ -202,14 +201,13 @@ Region Region::createTJunctionFreeRegion(const Region& r) { if (r.isEmpty()) return r; if (r.isRect()) return r; - FatVector reversed; + Vector reversed; reverseRectsResolvingJunctions(r.begin(), r.end(), reversed, direction_RTL); Region outputRegion; - reverseRectsResolvingJunctions(reversed.data(), reversed.data() + reversed.size(), - outputRegion.mStorage, direction_LTR); - outputRegion.mStorage.push_back( - r.getBounds()); // to make region valid, mStorage must end with bounds + reverseRectsResolvingJunctions(reversed.begin(), reversed.end(), + outputRegion.mStorage, direction_LTR); + outputRegion.mStorage.add(r.getBounds()); // to make region valid, mStorage must end with bounds #if defined(VALIDATE_REGIONS) validate(outputRegion, "T-Junction free region"); @@ -224,8 +222,7 @@ Region& Region::operator = (const Region& rhs) validate(*this, "this->operator="); validate(rhs, "rhs.operator="); #endif - mStorage.clear(); - mStorage.insert(mStorage.begin(), rhs.mStorage.begin(), rhs.mStorage.end()); + mStorage = rhs.mStorage; return *this; } @@ -234,7 +231,7 @@ Region& Region::makeBoundsSelf() if (mStorage.size() >= 2) { const Rect bounds(getBounds()); mStorage.clear(); - mStorage.push_back(bounds); + mStorage.add(bounds); } return *this; } @@ -258,25 +255,25 @@ bool Region::contains(int x, int y) const { void Region::clear() { mStorage.clear(); - mStorage.push_back(Rect(0, 0)); + mStorage.add(Rect(0,0)); } void Region::set(const Rect& r) { mStorage.clear(); - mStorage.push_back(r); + mStorage.add(r); } void Region::set(int32_t w, int32_t h) { mStorage.clear(); - mStorage.push_back(Rect(w, h)); + mStorage.add(Rect(w, h)); } void Region::set(uint32_t w, uint32_t h) { mStorage.clear(); - mStorage.push_back(Rect(w, h)); + mStorage.add(Rect(w, h)); } bool Region::isTriviallyEqual(const Region& region) const { @@ -302,7 +299,8 @@ bool Region::hasSameRects(const Region& other) const { void Region::addRectUnchecked(int l, int t, int r, int b) { Rect rect(l,t,r,b); - mStorage.insert(mStorage.end() - 1, rect); + size_t where = mStorage.size() - 1; + mStorage.insertAt(rect, where, 1); } // ---------------------------------------------------------------------------- @@ -352,7 +350,7 @@ Region& Region::translateSelf(int x, int y) { Region& Region::scaleSelf(float sx, float sy) { size_t count = mStorage.size(); - Rect* rects = mStorage.data(); + Rect* rects = mStorage.editArray(); while (count) { rects->left = static_cast(static_cast(rects->left) * sx + 0.5f); rects->right = static_cast(static_cast(rects->right) * sx + 0.5f); @@ -457,10 +455,10 @@ const Region Region::operation(const Region& rhs, int dx, int dy, uint32_t op) c class Region::rasterizer : public region_operator::region_rasterizer { Rect bounds; - FatVector& storage; + Vector& storage; Rect* head; Rect* tail; - FatVector span; + Vector span; Rect* cur; public: explicit rasterizer(Region& reg) @@ -487,8 +485,8 @@ Region::rasterizer::~rasterizer() flushSpan(); } if (storage.size()) { - bounds.top = storage.front().top; - bounds.bottom = storage.back().bottom; + bounds.top = storage.itemAt(0).top; + bounds.bottom = storage.top().bottom; if (storage.size() == 1) { storage.clear(); } @@ -496,7 +494,7 @@ Region::rasterizer::~rasterizer() bounds.left = 0; bounds.right = 0; } - storage.push_back(bounds); + storage.add(bounds); } void Region::rasterizer::operator()(const Rect& rect) @@ -511,15 +509,15 @@ void Region::rasterizer::operator()(const Rect& rect) return; } } - span.push_back(rect); - cur = span.data() + (span.size() - 1); + span.add(rect); + cur = span.editArray() + (span.size() - 1); } void Region::rasterizer::flushSpan() { bool merge = false; if (tail-head == ssize_t(span.size())) { - Rect const* p = span.data(); + Rect const* p = span.editArray(); Rect const* q = head; if (p->top == q->bottom) { merge = true; @@ -534,17 +532,17 @@ void Region::rasterizer::flushSpan() } } if (merge) { - const int bottom = span.front().bottom; + const int bottom = span[0].bottom; Rect* r = head; while (r != tail) { r->bottom = bottom; r++; } } else { - bounds.left = min(span.front().left, bounds.left); - bounds.right = max(span.back().right, bounds.right); - storage.insert(storage.end(), span.begin(), span.end()); - tail = storage.data() + storage.size(); + bounds.left = min(span.itemAt(0).left, bounds.left); + bounds.right = max(span.top().right, bounds.right); + storage.appendVector(span); + tail = storage.editArray() + storage.size(); head = tail - span.size(); } span.clear(); @@ -552,7 +550,7 @@ void Region::rasterizer::flushSpan() bool Region::validate(const Region& reg, const char* name, bool silent) { - if (reg.mStorage.empty()) { + if (reg.mStorage.isEmpty()) { ALOGE_IF(!silent, "%s: mStorage is empty, which is never valid", name); // return immediately as the code below assumes mStorage is non-empty return false; @@ -691,8 +689,9 @@ void Region::boolean_operation(uint32_t op, Region& dst, } sk_dst.op(sk_lhs, sk_rhs, sk_op); - if (sk_dst.empty() && dst.empty()) return; - + if (sk_dst.isEmpty() && dst.isEmpty()) + return; + bool same = true; Region::const_iterator head = dst.begin(); Region::const_iterator const tail = dst.end(); @@ -787,7 +786,7 @@ void Region::translate(Region& reg, int dx, int dy) validate(reg, "translate (before)"); #endif size_t count = reg.mStorage.size(); - Rect* rects = reg.mStorage.data(); + Rect* rects = reg.mStorage.editArray(); while (count) { rects->offsetBy(dx, dy); rects++; @@ -867,25 +866,24 @@ status_t Region::unflatten(void const* buffer, size_t size) { ALOGE("Region::unflatten() failed, invalid region"); return BAD_VALUE; } - mStorage.clear(); - mStorage.insert(mStorage.begin(), result.mStorage.begin(), result.mStorage.end()); + mStorage = result.mStorage; return NO_ERROR; } // ---------------------------------------------------------------------------- Region::const_iterator Region::begin() const { - return mStorage.data(); + return mStorage.array(); } Region::const_iterator Region::end() const { // Workaround for b/77643177 // mStorage should never be empty, but somehow it is and it's causing // an abort in ubsan - if (mStorage.empty()) return mStorage.data(); + if (mStorage.isEmpty()) return mStorage.array(); size_t numRects = isRect() ? 1 : mStorage.size() - 1; - return mStorage.data() + numRects; + return mStorage.array() + numRects; } Rect const* Region::getArray(size_t* count) const { diff --git a/libs/ui/include/ui/FatVector.h b/libs/ui/include/ui/FatVector.h deleted file mode 100644 index 25fe3a0a2a..0000000000 --- a/libs/ui/include/ui/FatVector.h +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright 2019, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_REGION_FAT_VECTOR_H -#define ANDROID_REGION_FAT_VECTOR_H - -#include -#include -#include -#include - -#include - -namespace android { - -template -class InlineStdAllocator { -public: - struct Allocation { - private: - Allocation(const Allocation&) = delete; - void operator=(const Allocation&) = delete; - - public: - Allocation() {} - // char array instead of T array, so memory is uninitialized, with no destructors run - char array[sizeof(T) * SIZE]; - bool inUse = false; - }; - - typedef T value_type; // needed to implement std::allocator - typedef T* pointer; // needed to implement std::allocator - - explicit InlineStdAllocator(Allocation& allocation) : mAllocation(allocation) {} - InlineStdAllocator(const InlineStdAllocator& other) : mAllocation(other.mAllocation) {} - ~InlineStdAllocator() {} - - T* allocate(size_t num, const void* = 0) { - if (!mAllocation.inUse && num <= SIZE) { - mAllocation.inUse = true; - return static_cast(static_cast(mAllocation.array)); - } else { - return static_cast(static_cast(malloc(num * sizeof(T)))); - } - } - - void deallocate(pointer p, size_t) { - if (p == static_cast(static_cast(mAllocation.array))) { - mAllocation.inUse = false; - } else { - // 'free' instead of delete here - destruction handled separately - free(p); - } - } - Allocation& mAllocation; -}; - -/** - * std::vector with SIZE elements preallocated into an internal buffer. - * - * Useful for avoiding the cost of malloc in cases where only SIZE or - * fewer elements are needed in the common case. - */ -template -class FatVector : public std::vector> { -public: - FatVector() - : std::vector>(InlineStdAllocator(mAllocation)) { - this->reserve(SIZE); - } - - explicit FatVector(size_t capacity) : FatVector() { this->resize(capacity); } - -private: - typename InlineStdAllocator::Allocation mAllocation; -}; - -} // namespace android - -#endif // ANDROID_REGION_FAT_VECTOR_H diff --git a/libs/ui/include/ui/Region.h b/libs/ui/include/ui/Region.h index 6bb7b8d21c..2db3b10f13 100644 --- a/libs/ui/include/ui/Region.h +++ b/libs/ui/include/ui/Region.h @@ -21,13 +21,13 @@ #include #include +#include + #include #include #include -#include "FatVector.h" - #include namespace android { @@ -180,7 +180,7 @@ private: // with an extra Rect as the last element which is set to the // bounds of the region. However, if the region is // a simple Rect then mStorage contains only that rect. - FatVector mStorage; + Vector mStorage; }; @@ -235,3 +235,4 @@ static inline void PrintTo(const Region& region, ::std::ostream* os) { }; // namespace android #endif // ANDROID_UI_REGION_H + diff --git a/libs/ui/include_vndk/ui/FatVector.h b/libs/ui/include_vndk/ui/FatVector.h deleted file mode 120000 index bf30166784..0000000000 --- a/libs/ui/include_vndk/ui/FatVector.h +++ /dev/null @@ -1 +0,0 @@ -../../include/ui/FatVector.h \ No newline at end of file -- cgit v1.2.3-59-g8ed1b From c7812be6375a2a9f2c7db1e5f0015d452721d9c2 Mon Sep 17 00:00:00 2001 From: Arthur Hung Date: Thu, 27 Feb 2020 22:40:27 +0800 Subject: Pass source to dispatchBatchedInputEventPending (2/2) The API requestUnbufferedDispatch allow View could receive the event in unbuffered way. But doing processUnbufferedRequest in onInputEvent is too late for the first event. Instead, we should pass the source of the input event up to dispatchBatchedInputEventPending, and then we can use that to determine whether we could consume event immediately or not. Bug: 149715123 Test: atest ViewUnbufferedTest Change-Id: I9cd6f76cc3ba74647b57036b3f4979efa8751b95 --- include/input/InputTransport.h | 7 +++++++ libs/input/InputTransport.cpp | 10 ++++++++++ 2 files changed, 17 insertions(+) (limited to 'include') diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h index 8ca178c1d7..7ca9031f77 100644 --- a/include/input/InputTransport.h +++ b/include/input/InputTransport.h @@ -404,6 +404,13 @@ public: */ bool hasPendingBatch() const; + /* Returns the source of first pending batch if exist. + * + * Should be called after calling consume() with consumeBatches == false to determine + * whether consume() should be called again later on with consumeBatches == true. + */ + int32_t getPendingBatchSource() const; + private: // True if touch resampling is enabled. const bool mResampleTouch; diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp index 7335b30a49..ef7cc7d531 100644 --- a/libs/input/InputTransport.cpp +++ b/libs/input/InputTransport.cpp @@ -1130,6 +1130,16 @@ bool InputConsumer::hasPendingBatch() const { return !mBatches.isEmpty(); } +int32_t InputConsumer::getPendingBatchSource() const { + if (mBatches.isEmpty()) { + return AINPUT_SOURCE_CLASS_NONE; + } + + const Batch& batch = mBatches.itemAt(0); + const InputMessage& head = batch.samples.itemAt(0); + return head.body.motion.source; +} + ssize_t InputConsumer::findBatch(int32_t deviceId, int32_t source) const { for (size_t i = 0; i < mBatches.size(); i++) { const Batch& batch = mBatches.itemAt(i); -- cgit v1.2.3-59-g8ed1b From 80826e0e14e547aba00ad558fb1d0c1d93e7cebc Mon Sep 17 00:00:00 2001 From: Chris Ye Date: Thu, 5 Mar 2020 15:14:21 -0800 Subject: Move the non-NDK APIs to powermanager internal directory. Move the header files with non-NDK APIs to be inside powermanager. Bug: 150878392 Change-Id: I34d0619a0fd3bc913ef79040207b5943add6b372 --- include/android/CoolingDevice.h | 49 -------------------- include/android/Temperature.h | 53 ---------------------- services/powermanager/Android.bp | 7 ++- .../powermanager/include/android/CoolingDevice.h | 49 ++++++++++++++++++++ .../powermanager/include/android/Temperature.h | 53 ++++++++++++++++++++++ 5 files changed, 108 insertions(+), 103 deletions(-) delete mode 100644 include/android/CoolingDevice.h delete mode 100644 include/android/Temperature.h create mode 100644 services/powermanager/include/android/CoolingDevice.h create mode 100644 services/powermanager/include/android/Temperature.h (limited to 'include') diff --git a/include/android/CoolingDevice.h b/include/android/CoolingDevice.h deleted file mode 100644 index 2f366be544..0000000000 --- a/include/android/CoolingDevice.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_OS_COOLINGDEVICE_H -#define ANDROID_OS_COOLINGDEVICE_H - -#include -#include - -namespace android { -namespace os { - -/** - * CoolingDevice is a structure to encapsulate cooling device status. - */ -struct CoolingDevice : public android::Parcelable { - /** Current throttle state of the cooling device. */ - float mValue; - /** A cooling device type from ThermalHAL */ - uint32_t mType; - /** Name of this cooling device */ - String16 mName; - - CoolingDevice() - : mValue(0.0f), - mType(0), - mName("") { - } - virtual status_t readFromParcel(const android::Parcel* parcel) override; - virtual status_t writeToParcel(android::Parcel* parcel) const override; -}; - -} // namespace os -} // namespace android - -#endif /* ANDROID_OS_COOLINGDEVICE_H */ diff --git a/include/android/Temperature.h b/include/android/Temperature.h deleted file mode 100644 index 2e68ec4899..0000000000 --- a/include/android/Temperature.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_OS_TEMPERATURE_H -#define ANDROID_OS_TEMPERATURE_H - -#include -#include - -namespace android { -namespace os { - -/** - * Temperature is a structure to encapsulate temperature status. - */ -struct Temperature : public android::Parcelable { - /** Temperature value */ - float mValue; - /** A Temperature type from ThermalHAL */ - int32_t mType; - /** Name of this Temperature */ - String16 mName; - /** The level of the sensor is currently in throttling */ - int32_t mStatus; - - Temperature() - : mValue(0.0f), - mType(0), - mName(""), - mStatus(0) { - } - - virtual status_t readFromParcel(const android::Parcel* parcel) override; - virtual status_t writeToParcel(android::Parcel* parcel) const override; -}; - -} // namespace os -} // namespace android - -#endif /* ANDROID_OS_TEMPERATURE_H */ diff --git a/services/powermanager/Android.bp b/services/powermanager/Android.bp index 3e0f136dfb..b0d3e3bde0 100644 --- a/services/powermanager/Android.bp +++ b/services/powermanager/Android.bp @@ -9,7 +9,7 @@ cc_library_shared { ], aidl: { - local_include_dirs: ["."], + local_include_dirs: ["include"], include_dirs: [ "frameworks/base/core/java/android/os", ], @@ -28,6 +28,11 @@ cc_library_shared { "-Wunused", "-Wunreachable-code", ], + + local_include_dirs: ["include"], + export_include_dirs: [ + "include", + ], } cc_test { diff --git a/services/powermanager/include/android/CoolingDevice.h b/services/powermanager/include/android/CoolingDevice.h new file mode 100644 index 0000000000..2f366be544 --- /dev/null +++ b/services/powermanager/include/android/CoolingDevice.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_OS_COOLINGDEVICE_H +#define ANDROID_OS_COOLINGDEVICE_H + +#include +#include + +namespace android { +namespace os { + +/** + * CoolingDevice is a structure to encapsulate cooling device status. + */ +struct CoolingDevice : public android::Parcelable { + /** Current throttle state of the cooling device. */ + float mValue; + /** A cooling device type from ThermalHAL */ + uint32_t mType; + /** Name of this cooling device */ + String16 mName; + + CoolingDevice() + : mValue(0.0f), + mType(0), + mName("") { + } + virtual status_t readFromParcel(const android::Parcel* parcel) override; + virtual status_t writeToParcel(android::Parcel* parcel) const override; +}; + +} // namespace os +} // namespace android + +#endif /* ANDROID_OS_COOLINGDEVICE_H */ diff --git a/services/powermanager/include/android/Temperature.h b/services/powermanager/include/android/Temperature.h new file mode 100644 index 0000000000..2e68ec4899 --- /dev/null +++ b/services/powermanager/include/android/Temperature.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_OS_TEMPERATURE_H +#define ANDROID_OS_TEMPERATURE_H + +#include +#include + +namespace android { +namespace os { + +/** + * Temperature is a structure to encapsulate temperature status. + */ +struct Temperature : public android::Parcelable { + /** Temperature value */ + float mValue; + /** A Temperature type from ThermalHAL */ + int32_t mType; + /** Name of this Temperature */ + String16 mName; + /** The level of the sensor is currently in throttling */ + int32_t mStatus; + + Temperature() + : mValue(0.0f), + mType(0), + mName(""), + mStatus(0) { + } + + virtual status_t readFromParcel(const android::Parcel* parcel) override; + virtual status_t writeToParcel(android::Parcel* parcel) const override; +}; + +} // namespace os +} // namespace android + +#endif /* ANDROID_OS_TEMPERATURE_H */ -- cgit v1.2.3-59-g8ed1b From 8e8cec4c48603621552db8dbd2c090847ff0b5e4 Mon Sep 17 00:00:00 2001 From: Chris Ye Date: Thu, 26 Mar 2020 14:14:58 -0700 Subject: AThermal_StatusCallback should return void AThermal_StatusCallback returns an int but it's totally ignored and undocumented, we should just make this a void function. Bug: 152509392 Change-Id: I044733f63007e942a6b1d04ce761891101b94e8f --- include/android/thermal.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/android/thermal.h b/include/android/thermal.h index 0f4b4d9b8d..3247fa167b 100644 --- a/include/android/thermal.h +++ b/include/android/thermal.h @@ -109,7 +109,7 @@ typedef struct AThermalManager AThermalManager; * It's passed the updated thermal status as parameter, as well as the * pointer provided by the client that registered a callback. */ -typedef int (*AThermal_StatusCallback)(void *data, AThermalStatus status); +typedef void (*AThermal_StatusCallback)(void *data, AThermalStatus status); /** * Acquire an instance of the thermal manager. This must be freed using -- cgit v1.2.3-59-g8ed1b From 15b6f9c8961019debe63358b73572bd294b35349 Mon Sep 17 00:00:00 2001 From: Steven Thomas Date: Thu, 26 Mar 2020 13:44:28 -0700 Subject: Clean up some formatting problems in the setFrameRate ndk docs Bug: 152413112 Test: Unfortunately there's no convenient way to build the ndk docs locally, so we're just going to submit and see if the regenerated DAC docs look correct. I talked with Dan Albert on the ndk team about this, and he decided to reopen b/115644359 to see if we can get some easy way to build the ndk docs locally again, so we can verify minor formatting fixes like this. Change-Id: Ie29a19a386887df6305ee9a4447702126fed7972 --- include/android/surface_control.h | 8 ++++---- libs/nativewindow/include/android/native_window.h | 12 ++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/android/surface_control.h b/include/android/surface_control.h index c30dcfee09..cbcf6ec5c0 100644 --- a/include/android/surface_control.h +++ b/include/android/surface_control.h @@ -409,7 +409,7 @@ void ASurfaceTransaction_setHdrMetadata_cta861_3(ASurfaceTransaction* transactio #if __ANDROID_API__ >= 30 -/* +/** * Sets the intended frame rate for |surface_control|. * * On devices that are capable of running the display at different refresh rates, the system may @@ -421,9 +421,9 @@ void ASurfaceTransaction_setHdrMetadata_cta861_3(ASurfaceTransaction* transactio * * |frameRate| is the intended frame rate of this surface, in frames per second. 0 is a special * value that indicates the app will accept the system's choice for the display frame rate, which is - * the default behavior if this function isn't called. The frameRate param does *not* need to be a - * valid refresh rate for this device's display - e.g., it's fine to pass 30fps to a device that can - * only run the display at 60fps. + * the default behavior if this function isn't called. The frameRate param does not need to + * be a valid refresh rate for this device's display - e.g., it's fine to pass 30fps to a device + * that can only run the display at 60fps. * * |compatibility| The frame rate compatibility of this surface. The compatibility value may * influence the system's choice of display frame rate. To specify a compatibility use the diff --git a/libs/nativewindow/include/android/native_window.h b/libs/nativewindow/include/android/native_window.h index 59aa6655b8..25130e2a03 100644 --- a/libs/nativewindow/include/android/native_window.h +++ b/libs/nativewindow/include/android/native_window.h @@ -233,15 +233,15 @@ int32_t ANativeWindow_getBuffersDataSpace(ANativeWindow* window) __INTRODUCED_IN #if __ANDROID_API__ >= 30 -/* Parameter for ANativeWindow_setFrameRate */ -enum { +/** Compatibility value for ANativeWindow_setFrameRate. */ +enum ANativeWindow_FrameRateCompatibility { /** * There are no inherent restrictions on the frame rate of this window. */ ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT = 0, /** * This window is being used to display content with an inherently fixed - * frame rate, e.g. a video that has a specific frame rate. When the system + * frame rate, e.g.\ a video that has a specific frame rate. When the system * selects a frame rate other than what the app requested, the app will need * to do pull down or use some other technique to adapt to the system's * frame rate. The user experience is likely to be worse (e.g. more frame @@ -272,9 +272,9 @@ enum { * \param frameRate The intended frame rate of this window, in frames per * second. 0 is a special value that indicates the app will accept the system's * choice for the display frame rate, which is the default behavior if this - * function isn't called. The frameRate param does *not* need to be a valid - * refresh rate for this device's display - e.g., it's fine to pass 30fps to a - * device that can only run the display at 60fps. + * function isn't called. The frameRate param does not need to be a + * valid refresh rate for this device's display - e.g., it's fine to pass 30fps + * to a device that can only run the display at 60fps. * * \param compatibility The frame rate compatibility of this window. The * compatibility value may influence the system's choice of display refresh -- cgit v1.2.3-59-g8ed1b From 7174efe2182376411ba2bcfb67d7e6025111183f Mon Sep 17 00:00:00 2001 From: Robert Carr Date: Mon, 13 Apr 2020 16:55:27 -0700 Subject: InputWindow: Default initialize all values A future patch will initialize InputWindows from a new location. It's a little troublesome to have to keep track of which values are initialized by default and not. Bug: 152064592 Test: Existing tests pass Change-Id: I200d9c050d8bf6d96c45ce75f95cb4d6537ab0ba --- include/input/InputWindow.h | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/include/input/InputWindow.h b/include/input/InputWindow.h index c44db514d2..edaf8f530b 100644 --- a/include/input/InputWindow.h +++ b/include/input/InputWindow.h @@ -123,17 +123,17 @@ struct InputWindowInfo { // input windows that have the same token. sp token; // This uniquely identifies the input window. - int32_t id = 0; + int32_t id = -1; std::string name; - int32_t layoutParamsFlags; - int32_t layoutParamsType; - nsecs_t dispatchingTimeout; + int32_t layoutParamsFlags = 0; + int32_t layoutParamsType = 0; + nsecs_t dispatchingTimeout = -1; /* These values are filled in by SurfaceFlinger. */ - int32_t frameLeft; - int32_t frameTop; - int32_t frameRight; - int32_t frameBottom; + int32_t frameLeft = -1; + int32_t frameTop = -1; + int32_t frameRight = -1; + int32_t frameBottom = -1; /* * SurfaceFlinger consumes this value to shrink the computed frame. This is @@ -145,7 +145,7 @@ struct InputWindowInfo { // A global scaling factor for all windows. Unlike windowScaleX/Y this results // in scaling of the TOUCH_MAJOR/TOUCH_MINOR axis. - float globalScaleFactor; + float globalScaleFactor = 1.0f; // Scaling factors applied to individual windows. float windowXScale = 1.0f; @@ -156,18 +156,18 @@ struct InputWindowInfo { * to absolute coordinates by SurfaceFlinger once the frame is computed. */ Region touchableRegion; - bool visible; - bool canReceiveKeys; - bool hasFocus; - bool hasWallpaper; - bool paused; - int32_t ownerPid; - int32_t ownerUid; - int32_t inputFeatures; - int32_t displayId; + bool visible = false; + bool canReceiveKeys = false; + bool hasFocus = false; + bool hasWallpaper = false; + bool paused = false; + int32_t ownerPid = -1; + int32_t ownerUid = -1; + int32_t inputFeatures = 0; + int32_t displayId = ADISPLAY_ID_NONE; int32_t portalToDisplayId = ADISPLAY_ID_NONE; InputApplicationInfo applicationInfo; - bool replaceTouchableRegionWithCrop; + bool replaceTouchableRegionWithCrop = false; wp touchableRegionCropHandle; void addTouchableRegion(const Rect& region); -- cgit v1.2.3-59-g8ed1b From 2984b7af4f7969cdc02dea6a1722635cc9a432dd Mon Sep 17 00:00:00 2001 From: Robert Carr Date: Mon, 13 Apr 2020 17:06:45 -0700 Subject: InputDispatcher: Fix support for INPUT_FEATURE_NO_INPUT_CHANNEL In preparation for an occlusion detection fix, we are going to begin sending windows with NO_INPUT_CHANNEL to InputFlinger. There are 3 obstacles we address here: 1. InputDispatcher ignores windows with no InputChannel when updating input windows. We modify the code to allow such windows if they have INPUT_FEATURE_NO_INPUT_CHANNEL. 2. The parcelling code currently has an optimization to avoid sending the rest of the fields if token is null. We rebase this optimization on whether or not a name is set. 3. InputWindowHandle::getName checks if there is a token to consider the window invalid. We instead check if a name is set. Bug: 152064592 Test: Existing tests pass Change-Id: I8a85f46b6c44866c7f73daafa5e8ff4c9251c366 --- include/input/InputWindow.h | 2 +- libs/input/InputWindow.cpp | 9 ++------- services/inputflinger/dispatcher/InputDispatcher.cpp | 2 +- 3 files changed, 4 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/input/InputWindow.h b/include/input/InputWindow.h index edaf8f530b..a695a8ffda 100644 --- a/include/input/InputWindow.h +++ b/include/input/InputWindow.h @@ -213,7 +213,7 @@ public: } inline std::string getName() const { - return mInfo.token ? mInfo.name : ""; + return !mInfo.name.empty() ? mInfo.name : ""; } inline nsecs_t getDispatchingTimeout(nsecs_t defaultValue) const { diff --git a/libs/input/InputWindow.cpp b/libs/input/InputWindow.cpp index 03ca459fb9..6e4c97ded2 100644 --- a/libs/input/InputWindow.cpp +++ b/libs/input/InputWindow.cpp @@ -65,7 +65,7 @@ bool InputWindowInfo::overlaps(const InputWindowInfo* other) const { } status_t InputWindowInfo::write(Parcel& output) const { - if (token == nullptr) { + if (name.empty()) { output.writeInt32(0); return OK; } @@ -110,12 +110,7 @@ InputWindowInfo InputWindowInfo::read(const Parcel& from) { return ret; } - sp token = from.readStrongBinder(); - if (token == nullptr) { - return ret; - } - - ret.token = token; + ret.token = from.readStrongBinder(); ret.id = from.readInt32(); ret.name = from.readString8().c_str(); ret.layoutParamsFlags = from.readInt32(); diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 4ec61b0c63..403e21dffd 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -3598,8 +3598,8 @@ void InputDispatcher::updateWindowHandlesForDisplayLocked( if (canReceiveInput && !noInputChannel) { ALOGV("Window handle %s has no registered input channel", handle->getName().c_str()); + continue; } - continue; } if (info->displayId != displayId) { -- cgit v1.2.3-59-g8ed1b From a9a6b46b9cbba23c161117f99ab15f46784d7ece Mon Sep 17 00:00:00 2001 From: Jagadeesh Pakaravoor Date: Fri, 1 May 2020 00:01:40 +0000 Subject: Revert^2 "libui: rewrite Region with FatVector" Revert submission 10449863-revert-fatvector Reason for revert: b/149254345 Original change (of introducing FatVector) was reverted as a stop-gap solution to fix the aforementioned bug. The bug was caused by an ABI lock between Surface's definition (that changed with Region) and lib-imsvt prebuilt. Enabling this change now as we have re-compiled the prebuilt with the change enabled. Doing that via a revert of the revert. Reverted Changes: I8ac66acb8:Revert "hwui: remove FatVector" Ib60dbf3ef:Revert "libui: rewrite Region with FatVector" Original changes: I09dc2fddd:hwui: remove FatVector I265c6c831:libui: rewrite Region with FatVector bug: 149254345 Change-Id: I723283a952e0908f595967af0037b0dc1351671b --- include/ui/FatVector.h | 1 + libs/ui/Region.cpp | 94 +++++++++++++++++++------------------ libs/ui/include/ui/FatVector.h | 93 ++++++++++++++++++++++++++++++++++++ libs/ui/include/ui/Region.h | 7 ++- libs/ui/include_vndk/ui/FatVector.h | 1 + 5 files changed, 146 insertions(+), 50 deletions(-) create mode 120000 include/ui/FatVector.h create mode 100644 libs/ui/include/ui/FatVector.h create mode 120000 libs/ui/include_vndk/ui/FatVector.h (limited to 'include') diff --git a/include/ui/FatVector.h b/include/ui/FatVector.h new file mode 120000 index 0000000000..c2047c07e1 --- /dev/null +++ b/include/ui/FatVector.h @@ -0,0 +1 @@ +../../libs/ui/include/ui/FatVector.h \ No newline at end of file diff --git a/libs/ui/Region.cpp b/libs/ui/Region.cpp index bf487c4aec..cd2a448c3e 100644 --- a/libs/ui/Region.cpp +++ b/libs/ui/Region.cpp @@ -67,19 +67,20 @@ const Region Region::INVALID_REGION(Rect::INVALID_RECT); // ---------------------------------------------------------------------------- Region::Region() { - mStorage.add(Rect(0,0)); + mStorage.push_back(Rect(0, 0)); } Region::Region(const Region& rhs) - : mStorage(rhs.mStorage) { + mStorage.clear(); + mStorage.insert(mStorage.begin(), rhs.mStorage.begin(), rhs.mStorage.end()); #if defined(VALIDATE_REGIONS) validate(rhs, "rhs copy-ctor"); #endif } Region::Region(const Rect& rhs) { - mStorage.add(rhs); + mStorage.push_back(rhs); } Region::~Region() @@ -100,8 +101,8 @@ Region::~Region() * final, correctly ordered region buffer. Each rectangle will be compared with the span directly * above it, and subdivided to resolve any remaining T-junctions. */ -static void reverseRectsResolvingJunctions(const Rect* begin, const Rect* end, - Vector& dst, int spanDirection) { +static void reverseRectsResolvingJunctions(const Rect* begin, const Rect* end, FatVector& dst, + int spanDirection) { dst.clear(); const Rect* current = end - 1; @@ -109,7 +110,7 @@ static void reverseRectsResolvingJunctions(const Rect* begin, const Rect* end, // add first span immediately do { - dst.add(*current); + dst.push_back(*current); current--; } while (current->top == lastTop && current >= begin); @@ -147,12 +148,12 @@ static void reverseRectsResolvingJunctions(const Rect* begin, const Rect* end, if (prev.right <= left) break; if (prev.right > left && prev.right < right) { - dst.add(Rect(prev.right, top, right, bottom)); + dst.push_back(Rect(prev.right, top, right, bottom)); right = prev.right; } if (prev.left > left && prev.left < right) { - dst.add(Rect(prev.left, top, right, bottom)); + dst.push_back(Rect(prev.left, top, right, bottom)); right = prev.left; } @@ -166,12 +167,12 @@ static void reverseRectsResolvingJunctions(const Rect* begin, const Rect* end, if (prev.left >= right) break; if (prev.left > left && prev.left < right) { - dst.add(Rect(left, top, prev.left, bottom)); + dst.push_back(Rect(left, top, prev.left, bottom)); left = prev.left; } if (prev.right > left && prev.right < right) { - dst.add(Rect(left, top, prev.right, bottom)); + dst.push_back(Rect(left, top, prev.right, bottom)); left = prev.right; } // if an entry in the previous span is too far left, nothing further right in the @@ -183,7 +184,7 @@ static void reverseRectsResolvingJunctions(const Rect* begin, const Rect* end, } if (left < right) { - dst.add(Rect(left, top, right, bottom)); + dst.push_back(Rect(left, top, right, bottom)); } current--; @@ -201,13 +202,14 @@ Region Region::createTJunctionFreeRegion(const Region& r) { if (r.isEmpty()) return r; if (r.isRect()) return r; - Vector reversed; + FatVector reversed; reverseRectsResolvingJunctions(r.begin(), r.end(), reversed, direction_RTL); Region outputRegion; - reverseRectsResolvingJunctions(reversed.begin(), reversed.end(), - outputRegion.mStorage, direction_LTR); - outputRegion.mStorage.add(r.getBounds()); // to make region valid, mStorage must end with bounds + reverseRectsResolvingJunctions(reversed.data(), reversed.data() + reversed.size(), + outputRegion.mStorage, direction_LTR); + outputRegion.mStorage.push_back( + r.getBounds()); // to make region valid, mStorage must end with bounds #if defined(VALIDATE_REGIONS) validate(outputRegion, "T-Junction free region"); @@ -222,7 +224,8 @@ Region& Region::operator = (const Region& rhs) validate(*this, "this->operator="); validate(rhs, "rhs.operator="); #endif - mStorage = rhs.mStorage; + mStorage.clear(); + mStorage.insert(mStorage.begin(), rhs.mStorage.begin(), rhs.mStorage.end()); return *this; } @@ -231,7 +234,7 @@ Region& Region::makeBoundsSelf() if (mStorage.size() >= 2) { const Rect bounds(getBounds()); mStorage.clear(); - mStorage.add(bounds); + mStorage.push_back(bounds); } return *this; } @@ -255,25 +258,25 @@ bool Region::contains(int x, int y) const { void Region::clear() { mStorage.clear(); - mStorage.add(Rect(0,0)); + mStorage.push_back(Rect(0, 0)); } void Region::set(const Rect& r) { mStorage.clear(); - mStorage.add(r); + mStorage.push_back(r); } void Region::set(int32_t w, int32_t h) { mStorage.clear(); - mStorage.add(Rect(w, h)); + mStorage.push_back(Rect(w, h)); } void Region::set(uint32_t w, uint32_t h) { mStorage.clear(); - mStorage.add(Rect(w, h)); + mStorage.push_back(Rect(w, h)); } bool Region::isTriviallyEqual(const Region& region) const { @@ -299,8 +302,7 @@ bool Region::hasSameRects(const Region& other) const { void Region::addRectUnchecked(int l, int t, int r, int b) { Rect rect(l,t,r,b); - size_t where = mStorage.size() - 1; - mStorage.insertAt(rect, where, 1); + mStorage.insert(mStorage.end() - 1, rect); } // ---------------------------------------------------------------------------- @@ -350,7 +352,7 @@ Region& Region::translateSelf(int x, int y) { Region& Region::scaleSelf(float sx, float sy) { size_t count = mStorage.size(); - Rect* rects = mStorage.editArray(); + Rect* rects = mStorage.data(); while (count) { rects->left = static_cast(static_cast(rects->left) * sx + 0.5f); rects->right = static_cast(static_cast(rects->right) * sx + 0.5f); @@ -455,10 +457,10 @@ const Region Region::operation(const Region& rhs, int dx, int dy, uint32_t op) c class Region::rasterizer : public region_operator::region_rasterizer { Rect bounds; - Vector& storage; + FatVector& storage; Rect* head; Rect* tail; - Vector span; + FatVector span; Rect* cur; public: explicit rasterizer(Region& reg) @@ -485,8 +487,8 @@ Region::rasterizer::~rasterizer() flushSpan(); } if (storage.size()) { - bounds.top = storage.itemAt(0).top; - bounds.bottom = storage.top().bottom; + bounds.top = storage.front().top; + bounds.bottom = storage.back().bottom; if (storage.size() == 1) { storage.clear(); } @@ -494,7 +496,7 @@ Region::rasterizer::~rasterizer() bounds.left = 0; bounds.right = 0; } - storage.add(bounds); + storage.push_back(bounds); } void Region::rasterizer::operator()(const Rect& rect) @@ -509,15 +511,15 @@ void Region::rasterizer::operator()(const Rect& rect) return; } } - span.add(rect); - cur = span.editArray() + (span.size() - 1); + span.push_back(rect); + cur = span.data() + (span.size() - 1); } void Region::rasterizer::flushSpan() { bool merge = false; if (tail-head == ssize_t(span.size())) { - Rect const* p = span.editArray(); + Rect const* p = span.data(); Rect const* q = head; if (p->top == q->bottom) { merge = true; @@ -532,17 +534,17 @@ void Region::rasterizer::flushSpan() } } if (merge) { - const int bottom = span[0].bottom; + const int bottom = span.front().bottom; Rect* r = head; while (r != tail) { r->bottom = bottom; r++; } } else { - bounds.left = min(span.itemAt(0).left, bounds.left); - bounds.right = max(span.top().right, bounds.right); - storage.appendVector(span); - tail = storage.editArray() + storage.size(); + bounds.left = min(span.front().left, bounds.left); + bounds.right = max(span.back().right, bounds.right); + storage.insert(storage.end(), span.begin(), span.end()); + tail = storage.data() + storage.size(); head = tail - span.size(); } span.clear(); @@ -550,7 +552,7 @@ void Region::rasterizer::flushSpan() bool Region::validate(const Region& reg, const char* name, bool silent) { - if (reg.mStorage.isEmpty()) { + if (reg.mStorage.empty()) { ALOGE_IF(!silent, "%s: mStorage is empty, which is never valid", name); // return immediately as the code below assumes mStorage is non-empty return false; @@ -689,9 +691,8 @@ void Region::boolean_operation(uint32_t op, Region& dst, } sk_dst.op(sk_lhs, sk_rhs, sk_op); - if (sk_dst.isEmpty() && dst.isEmpty()) - return; - + if (sk_dst.empty() && dst.empty()) return; + bool same = true; Region::const_iterator head = dst.begin(); Region::const_iterator const tail = dst.end(); @@ -786,7 +787,7 @@ void Region::translate(Region& reg, int dx, int dy) validate(reg, "translate (before)"); #endif size_t count = reg.mStorage.size(); - Rect* rects = reg.mStorage.editArray(); + Rect* rects = reg.mStorage.data(); while (count) { rects->offsetBy(dx, dy); rects++; @@ -866,24 +867,25 @@ status_t Region::unflatten(void const* buffer, size_t size) { ALOGE("Region::unflatten() failed, invalid region"); return BAD_VALUE; } - mStorage = result.mStorage; + mStorage.clear(); + mStorage.insert(mStorage.begin(), result.mStorage.begin(), result.mStorage.end()); return NO_ERROR; } // ---------------------------------------------------------------------------- Region::const_iterator Region::begin() const { - return mStorage.array(); + return mStorage.data(); } Region::const_iterator Region::end() const { // Workaround for b/77643177 // mStorage should never be empty, but somehow it is and it's causing // an abort in ubsan - if (mStorage.isEmpty()) return mStorage.array(); + if (mStorage.empty()) return mStorage.data(); size_t numRects = isRect() ? 1 : mStorage.size() - 1; - return mStorage.array() + numRects; + return mStorage.data() + numRects; } Rect const* Region::getArray(size_t* count) const { diff --git a/libs/ui/include/ui/FatVector.h b/libs/ui/include/ui/FatVector.h new file mode 100644 index 0000000000..25fe3a0a2a --- /dev/null +++ b/libs/ui/include/ui/FatVector.h @@ -0,0 +1,93 @@ +/* + * Copyright 2019, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_REGION_FAT_VECTOR_H +#define ANDROID_REGION_FAT_VECTOR_H + +#include +#include +#include +#include + +#include + +namespace android { + +template +class InlineStdAllocator { +public: + struct Allocation { + private: + Allocation(const Allocation&) = delete; + void operator=(const Allocation&) = delete; + + public: + Allocation() {} + // char array instead of T array, so memory is uninitialized, with no destructors run + char array[sizeof(T) * SIZE]; + bool inUse = false; + }; + + typedef T value_type; // needed to implement std::allocator + typedef T* pointer; // needed to implement std::allocator + + explicit InlineStdAllocator(Allocation& allocation) : mAllocation(allocation) {} + InlineStdAllocator(const InlineStdAllocator& other) : mAllocation(other.mAllocation) {} + ~InlineStdAllocator() {} + + T* allocate(size_t num, const void* = 0) { + if (!mAllocation.inUse && num <= SIZE) { + mAllocation.inUse = true; + return static_cast(static_cast(mAllocation.array)); + } else { + return static_cast(static_cast(malloc(num * sizeof(T)))); + } + } + + void deallocate(pointer p, size_t) { + if (p == static_cast(static_cast(mAllocation.array))) { + mAllocation.inUse = false; + } else { + // 'free' instead of delete here - destruction handled separately + free(p); + } + } + Allocation& mAllocation; +}; + +/** + * std::vector with SIZE elements preallocated into an internal buffer. + * + * Useful for avoiding the cost of malloc in cases where only SIZE or + * fewer elements are needed in the common case. + */ +template +class FatVector : public std::vector> { +public: + FatVector() + : std::vector>(InlineStdAllocator(mAllocation)) { + this->reserve(SIZE); + } + + explicit FatVector(size_t capacity) : FatVector() { this->resize(capacity); } + +private: + typename InlineStdAllocator::Allocation mAllocation; +}; + +} // namespace android + +#endif // ANDROID_REGION_FAT_VECTOR_H diff --git a/libs/ui/include/ui/Region.h b/libs/ui/include/ui/Region.h index 2db3b10f13..6bb7b8d21c 100644 --- a/libs/ui/include/ui/Region.h +++ b/libs/ui/include/ui/Region.h @@ -21,13 +21,13 @@ #include #include -#include - #include #include #include +#include "FatVector.h" + #include namespace android { @@ -180,7 +180,7 @@ private: // with an extra Rect as the last element which is set to the // bounds of the region. However, if the region is // a simple Rect then mStorage contains only that rect. - Vector mStorage; + FatVector mStorage; }; @@ -235,4 +235,3 @@ static inline void PrintTo(const Region& region, ::std::ostream* os) { }; // namespace android #endif // ANDROID_UI_REGION_H - diff --git a/libs/ui/include_vndk/ui/FatVector.h b/libs/ui/include_vndk/ui/FatVector.h new file mode 120000 index 0000000000..bf30166784 --- /dev/null +++ b/libs/ui/include_vndk/ui/FatVector.h @@ -0,0 +1 @@ +../../include/ui/FatVector.h \ No newline at end of file -- cgit v1.2.3-59-g8ed1b From 9499a112e3a0ba25e8f5ff2049cc529c2982698f Mon Sep 17 00:00:00 2001 From: wilsonshih Date: Mon, 4 May 2020 12:01:17 +0800 Subject: Makes TYPE_NOTIFICATION_SHADE as trusted overlay Fix the side effect after we split notification_shade window from status bar. Bug: 155373298 Test: follow the steps from b/149320322 Change-Id: I3362186b22505d21ec6e0ad779d4a26304ed782c Merged-In: I3362186b22505d21ec6e0ad779d4a26304ed782c --- include/input/InputWindow.h | 77 +++++++++++++++++++++++---------------------- libs/input/InputWindow.cpp | 19 ++++++----- 2 files changed, 48 insertions(+), 48 deletions(-) (limited to 'include') diff --git a/include/input/InputWindow.h b/include/input/InputWindow.h index a695a8ffda..856c54d89e 100644 --- a/include/input/InputWindow.h +++ b/include/input/InputWindow.h @@ -68,46 +68,47 @@ struct InputWindowInfo { // Window types from WindowManager.LayoutParams enum { FIRST_APPLICATION_WINDOW = 1, - TYPE_BASE_APPLICATION = 1, - TYPE_APPLICATION = 2, + TYPE_BASE_APPLICATION = 1, + TYPE_APPLICATION = 2, TYPE_APPLICATION_STARTING = 3, LAST_APPLICATION_WINDOW = 99, - FIRST_SUB_WINDOW = 1000, - TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW, - TYPE_APPLICATION_MEDIA = FIRST_SUB_WINDOW+1, - TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW+2, - TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW+3, - TYPE_APPLICATION_MEDIA_OVERLAY = FIRST_SUB_WINDOW+4, - LAST_SUB_WINDOW = 1999, - FIRST_SYSTEM_WINDOW = 2000, - TYPE_STATUS_BAR = FIRST_SYSTEM_WINDOW, - TYPE_SEARCH_BAR = FIRST_SYSTEM_WINDOW+1, - TYPE_PHONE = FIRST_SYSTEM_WINDOW+2, - TYPE_SYSTEM_ALERT = FIRST_SYSTEM_WINDOW+3, - TYPE_KEYGUARD = FIRST_SYSTEM_WINDOW+4, - TYPE_TOAST = FIRST_SYSTEM_WINDOW+5, - TYPE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW+6, - TYPE_PRIORITY_PHONE = FIRST_SYSTEM_WINDOW+7, - TYPE_SYSTEM_DIALOG = FIRST_SYSTEM_WINDOW+8, - TYPE_KEYGUARD_DIALOG = FIRST_SYSTEM_WINDOW+9, - TYPE_SYSTEM_ERROR = FIRST_SYSTEM_WINDOW+10, - TYPE_INPUT_METHOD = FIRST_SYSTEM_WINDOW+11, - TYPE_INPUT_METHOD_DIALOG= FIRST_SYSTEM_WINDOW+12, - TYPE_WALLPAPER = FIRST_SYSTEM_WINDOW+13, - TYPE_STATUS_BAR_PANEL = FIRST_SYSTEM_WINDOW+14, - TYPE_SECURE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW+15, - TYPE_DRAG = FIRST_SYSTEM_WINDOW+16, - TYPE_STATUS_BAR_SUB_PANEL = FIRST_SYSTEM_WINDOW+17, - TYPE_POINTER = FIRST_SYSTEM_WINDOW+18, - TYPE_NAVIGATION_BAR = FIRST_SYSTEM_WINDOW+19, - TYPE_VOLUME_OVERLAY = FIRST_SYSTEM_WINDOW+20, - TYPE_BOOT_PROGRESS = FIRST_SYSTEM_WINDOW+21, - TYPE_INPUT_CONSUMER = FIRST_SYSTEM_WINDOW+22, - TYPE_NAVIGATION_BAR_PANEL = FIRST_SYSTEM_WINDOW+24, - TYPE_MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW+27, - TYPE_ACCESSIBILITY_OVERLAY = FIRST_SYSTEM_WINDOW+32, - TYPE_DOCK_DIVIDER = FIRST_SYSTEM_WINDOW+34, - LAST_SYSTEM_WINDOW = 2999, + FIRST_SUB_WINDOW = 1000, + TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW, + TYPE_APPLICATION_MEDIA = FIRST_SUB_WINDOW + 1, + TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW + 2, + TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW + 3, + TYPE_APPLICATION_MEDIA_OVERLAY = FIRST_SUB_WINDOW + 4, + LAST_SUB_WINDOW = 1999, + FIRST_SYSTEM_WINDOW = 2000, + TYPE_STATUS_BAR = FIRST_SYSTEM_WINDOW, + TYPE_SEARCH_BAR = FIRST_SYSTEM_WINDOW + 1, + TYPE_PHONE = FIRST_SYSTEM_WINDOW + 2, + TYPE_SYSTEM_ALERT = FIRST_SYSTEM_WINDOW + 3, + TYPE_KEYGUARD = FIRST_SYSTEM_WINDOW + 4, + TYPE_TOAST = FIRST_SYSTEM_WINDOW + 5, + TYPE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW + 6, + TYPE_PRIORITY_PHONE = FIRST_SYSTEM_WINDOW + 7, + TYPE_SYSTEM_DIALOG = FIRST_SYSTEM_WINDOW + 8, + TYPE_KEYGUARD_DIALOG = FIRST_SYSTEM_WINDOW + 9, + TYPE_SYSTEM_ERROR = FIRST_SYSTEM_WINDOW + 10, + TYPE_INPUT_METHOD = FIRST_SYSTEM_WINDOW + 11, + TYPE_INPUT_METHOD_DIALOG = FIRST_SYSTEM_WINDOW + 12, + TYPE_WALLPAPER = FIRST_SYSTEM_WINDOW + 13, + TYPE_STATUS_BAR_PANEL = FIRST_SYSTEM_WINDOW + 14, + TYPE_SECURE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW + 15, + TYPE_DRAG = FIRST_SYSTEM_WINDOW + 16, + TYPE_STATUS_BAR_SUB_PANEL = FIRST_SYSTEM_WINDOW + 17, + TYPE_POINTER = FIRST_SYSTEM_WINDOW + 18, + TYPE_NAVIGATION_BAR = FIRST_SYSTEM_WINDOW + 19, + TYPE_VOLUME_OVERLAY = FIRST_SYSTEM_WINDOW + 20, + TYPE_BOOT_PROGRESS = FIRST_SYSTEM_WINDOW + 21, + TYPE_INPUT_CONSUMER = FIRST_SYSTEM_WINDOW + 22, + TYPE_NAVIGATION_BAR_PANEL = FIRST_SYSTEM_WINDOW + 24, + TYPE_MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 27, + TYPE_ACCESSIBILITY_OVERLAY = FIRST_SYSTEM_WINDOW + 32, + TYPE_DOCK_DIVIDER = FIRST_SYSTEM_WINDOW + 34, + TYPE_NOTIFICATION_SHADE = FIRST_SYSTEM_WINDOW + 40, + LAST_SYSTEM_WINDOW = 2999, }; enum { diff --git a/libs/input/InputWindow.cpp b/libs/input/InputWindow.cpp index 6e4c97ded2..b27b050d28 100644 --- a/libs/input/InputWindow.cpp +++ b/libs/input/InputWindow.cpp @@ -43,16 +43,15 @@ bool InputWindowInfo::frameContainsPoint(int32_t x, int32_t y) const { } bool InputWindowInfo::isTrustedOverlay() const { - return layoutParamsType == TYPE_INPUT_METHOD - || layoutParamsType == TYPE_INPUT_METHOD_DIALOG - || layoutParamsType == TYPE_MAGNIFICATION_OVERLAY - || layoutParamsType == TYPE_STATUS_BAR - || layoutParamsType == TYPE_NAVIGATION_BAR - || layoutParamsType == TYPE_NAVIGATION_BAR_PANEL - || layoutParamsType == TYPE_SECURE_SYSTEM_OVERLAY - || layoutParamsType == TYPE_DOCK_DIVIDER - || layoutParamsType == TYPE_ACCESSIBILITY_OVERLAY - || layoutParamsType == TYPE_INPUT_CONSUMER; + return layoutParamsType == TYPE_INPUT_METHOD || layoutParamsType == TYPE_INPUT_METHOD_DIALOG || + layoutParamsType == TYPE_MAGNIFICATION_OVERLAY || layoutParamsType == TYPE_STATUS_BAR || + layoutParamsType == TYPE_NOTIFICATION_SHADE || + layoutParamsType == TYPE_NAVIGATION_BAR || + layoutParamsType == TYPE_NAVIGATION_BAR_PANEL || + layoutParamsType == TYPE_SECURE_SYSTEM_OVERLAY || + layoutParamsType == TYPE_DOCK_DIVIDER || + layoutParamsType == TYPE_ACCESSIBILITY_OVERLAY || + layoutParamsType == TYPE_INPUT_CONSUMER; } bool InputWindowInfo::supportsSplitTouch() const { -- cgit v1.2.3-59-g8ed1b From 466cdea8d2de302f6be4d9f59816dd2c6078decf Mon Sep 17 00:00:00 2001 From: Joshua Tsuji Date: Mon, 4 May 2020 13:53:00 -0400 Subject: Make TYPE_TRUSTED_APPLICATION_OVERLAY a trusted overlay. Test: accept a permission dialog while bubbles are there too Fixes: 149320322 Change-Id: I3767e2d93d0bcb216483a12d94ffb13ca0051c7e --- include/input/InputWindow.h | 1 + libs/input/InputWindow.cpp | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/input/InputWindow.h b/include/input/InputWindow.h index 856c54d89e..c5e56fd91f 100644 --- a/include/input/InputWindow.h +++ b/include/input/InputWindow.h @@ -108,6 +108,7 @@ struct InputWindowInfo { TYPE_ACCESSIBILITY_OVERLAY = FIRST_SYSTEM_WINDOW + 32, TYPE_DOCK_DIVIDER = FIRST_SYSTEM_WINDOW + 34, TYPE_NOTIFICATION_SHADE = FIRST_SYSTEM_WINDOW + 40, + TYPE_TRUSTED_APPLICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 42, LAST_SYSTEM_WINDOW = 2999, }; diff --git a/libs/input/InputWindow.cpp b/libs/input/InputWindow.cpp index b27b050d28..85a2015e43 100644 --- a/libs/input/InputWindow.cpp +++ b/libs/input/InputWindow.cpp @@ -42,6 +42,7 @@ bool InputWindowInfo::frameContainsPoint(int32_t x, int32_t y) const { && y >= frameTop && y < frameBottom; } +// TODO(b/155781676): Remove and replace call points with trustedOverlay when that is ready. bool InputWindowInfo::isTrustedOverlay() const { return layoutParamsType == TYPE_INPUT_METHOD || layoutParamsType == TYPE_INPUT_METHOD_DIALOG || layoutParamsType == TYPE_MAGNIFICATION_OVERLAY || layoutParamsType == TYPE_STATUS_BAR || @@ -51,7 +52,8 @@ bool InputWindowInfo::isTrustedOverlay() const { layoutParamsType == TYPE_SECURE_SYSTEM_OVERLAY || layoutParamsType == TYPE_DOCK_DIVIDER || layoutParamsType == TYPE_ACCESSIBILITY_OVERLAY || - layoutParamsType == TYPE_INPUT_CONSUMER; + layoutParamsType == TYPE_INPUT_CONSUMER || + layoutParamsType == TYPE_TRUSTED_APPLICATION_OVERLAY; } bool InputWindowInfo::supportsSplitTouch() const { -- cgit v1.2.3-59-g8ed1b From 271de040ffc1371251a9741d2f347642a5de0995 Mon Sep 17 00:00:00 2001 From: Alec Mouri Date: Mon, 27 Apr 2020 22:38:19 -0700 Subject: Receive refresh rate callbacks from DMS AChoreographer will use DMS as the source of truth for these callbacks instead of SurfaceFlinger. Bug: 154874011 Test: ChoreographerNativeTest Tes: Manually verify that HWUI is processing refresh rate callbacks Change-Id: I961a7d1ab335800d3e260ba7564ddca9c0595cfc --- include/android/choreographer.h | 156 ++++++++++ libs/gui/DisplayEventDispatcher.cpp | 13 +- libs/gui/DisplayEventReceiver.cpp | 5 +- libs/gui/IDisplayEventConnection.cpp | 15 +- libs/gui/include/gui/DisplayEventDispatcher.h | 3 +- libs/gui/include/gui/DisplayEventReceiver.h | 5 +- libs/gui/include/gui/IDisplayEventConnection.h | 6 +- libs/nativedisplay/AChoreographer.cpp | 324 +++++++++++++++++---- libs/nativedisplay/Android.bp | 17 +- .../private/android/choreographer.h | 55 ++++ libs/nativedisplay/include/android/choreographer.h | 152 ---------- .../surfacetexture/surface_texture_platform.h | 16 +- libs/nativedisplay/libnativedisplay.map.txt | 17 ++ .../surfacetexture/surface_texture.cpp | 31 ++ services/surfaceflinger/Scheduler/EventThread.cpp | 27 +- services/surfaceflinger/Scheduler/EventThread.h | 11 +- .../tests/unittests/mock/MockEventThread.h | 2 +- 17 files changed, 585 insertions(+), 270 deletions(-) create mode 100644 include/android/choreographer.h create mode 100644 libs/nativedisplay/include-private/private/android/choreographer.h delete mode 100644 libs/nativedisplay/include/android/choreographer.h (limited to 'include') diff --git a/include/android/choreographer.h b/include/android/choreographer.h new file mode 100644 index 0000000000..c1c4a72cd3 --- /dev/null +++ b/include/android/choreographer.h @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @addtogroup Choreographer + * @{ + */ + +/** + * @file choreographer.h + */ + +#ifndef ANDROID_CHOREOGRAPHER_H +#define ANDROID_CHOREOGRAPHER_H + +#include +#include + +__BEGIN_DECLS + +struct AChoreographer; +typedef struct AChoreographer AChoreographer; + +/** + * Prototype of the function that is called when a new frame is being rendered. + * It's passed the time that the frame is being rendered as nanoseconds in the + * CLOCK_MONOTONIC time base, as well as the data pointer provided by the + * application that registered a callback. All callbacks that run as part of + * rendering a frame will observe the same frame time, so it should be used + * whenever events need to be synchronized (e.g. animations). + */ +typedef void (*AChoreographer_frameCallback)(long frameTimeNanos, void* data); + +/** + * Prototype of the function that is called when a new frame is being rendered. + * It's passed the time that the frame is being rendered as nanoseconds in the + * CLOCK_MONOTONIC time base, as well as the data pointer provided by the + * application that registered a callback. All callbacks that run as part of + * rendering a frame will observe the same frame time, so it should be used + * whenever events need to be synchronized (e.g. animations). + */ +typedef void (*AChoreographer_frameCallback64)(int64_t frameTimeNanos, void* data); + +/** + * Prototype of the function that is called when the display refresh rate + * changes. It's passed the new vsync period in nanoseconds, as well as the data + * pointer provided by the application that registered a callback. + */ +typedef void (*AChoreographer_refreshRateCallback)(int64_t vsyncPeriodNanos, void* data); + +#if __ANDROID_API__ >= 24 + +/** + * Get the AChoreographer instance for the current thread. This must be called + * on an ALooper thread. + * + * Available since API level 24. + */ +AChoreographer* AChoreographer_getInstance() __INTRODUCED_IN(24); + +/** + * Deprecated: Use AChoreographer_postFrameCallback64 instead. + */ +void AChoreographer_postFrameCallback(AChoreographer* choreographer, + AChoreographer_frameCallback callback, void* data) + __INTRODUCED_IN(24) __DEPRECATED_IN(29); + +/** + * Deprecated: Use AChoreographer_postFrameCallbackDelayed64 instead. + */ +void AChoreographer_postFrameCallbackDelayed(AChoreographer* choreographer, + AChoreographer_frameCallback callback, void* data, + long delayMillis) __INTRODUCED_IN(24) + __DEPRECATED_IN(29); + +#endif /* __ANDROID_API__ >= 24 */ + +#if __ANDROID_API__ >= 29 + +/** + * Power a callback to be run on the next frame. The data pointer provided will + * be passed to the callback function when it's called. + * + * Available since API level 29. + */ +void AChoreographer_postFrameCallback64(AChoreographer* choreographer, + AChoreographer_frameCallback64 callback, void* data) + __INTRODUCED_IN(29); + +/** + * Post a callback to be run on the frame following the specified delay. The + * data pointer provided will be passed to the callback function when it's + * called. + * + * Available since API level 29. + */ +void AChoreographer_postFrameCallbackDelayed64(AChoreographer* choreographer, + AChoreographer_frameCallback64 callback, void* data, + uint32_t delayMillis) __INTRODUCED_IN(29); + +#endif /* __ANDROID_API__ >= 29 */ + +#if __ANDROID_API__ >= 30 + +/** + * Registers a callback to be run when the display refresh rate changes. The + * data pointer provided will be passed to the callback function when it's + * called. The same callback may be registered multiple times, provided that a + * different data pointer is provided each time. + * + * If an application registers a callback for this choreographer instance when + * no new callbacks were previously registered, that callback is guaranteed to + * be dispatched. However, if the callback and associated data pointer are + * unregistered prior to running the callback, then the callback may be silently + * dropped. + * + * This api is thread-safe. Any thread is allowed to register a new refresh + * rate callback for the choreographer instance. + */ +void AChoreographer_registerRefreshRateCallback(AChoreographer* choreographer, + AChoreographer_refreshRateCallback, void* data); + +/** + * Unregisters a callback to be run when the display refresh rate changes, along + * with the data pointer previously provided when registering the callback. The + * callback is only unregistered when the data pointer matches one that was + * previously registered. + * + * This api is thread-safe. Any thread is allowed to unregister an existing + * refresh rate callback for the choreographer instance. When a refresh rate + * callback and associated data pointer are unregistered, then there is a + * guarantee that when the unregistration completes that that callback will not + * be run with the data pointer passed. + */ +void AChoreographer_unregisterRefreshRateCallback(AChoreographer* choreographer, + AChoreographer_refreshRateCallback, void* data); +#endif /* __ANDROID_API__ >= 30 */ + +__END_DECLS + +#endif // ANDROID_CHOREOGRAPHER_H + +/** @} */ diff --git a/libs/gui/DisplayEventDispatcher.cpp b/libs/gui/DisplayEventDispatcher.cpp index 15f966d062..b33bc9e556 100644 --- a/libs/gui/DisplayEventDispatcher.cpp +++ b/libs/gui/DisplayEventDispatcher.cpp @@ -36,10 +36,7 @@ static const size_t EVENT_BUFFER_SIZE = 100; DisplayEventDispatcher::DisplayEventDispatcher(const sp& looper, ISurfaceComposer::VsyncSource vsyncSource, ISurfaceComposer::ConfigChanged configChanged) - : mLooper(looper), - mReceiver(vsyncSource, configChanged), - mWaitingForVsync(false), - mConfigChangeFlag(configChanged) { + : mLooper(looper), mReceiver(vsyncSource, configChanged), mWaitingForVsync(false) { ALOGV("dispatcher %p ~ Initializing display event dispatcher.", this); } @@ -92,16 +89,12 @@ status_t DisplayEventDispatcher::scheduleVsync() { return OK; } -void DisplayEventDispatcher::toggleConfigEvents(ISurfaceComposer::ConfigChanged configChangeFlag) { - if (mConfigChangeFlag == configChangeFlag) { - return; - } - status_t status = mReceiver.toggleConfigEvents(configChangeFlag); +void DisplayEventDispatcher::requestLatestConfig() { + status_t status = mReceiver.requestLatestConfig(); if (status) { ALOGW("Failed enable config events, status=%d", status); return; } - mConfigChangeFlag = configChangeFlag; } int DisplayEventDispatcher::getFd() const { diff --git a/libs/gui/DisplayEventReceiver.cpp b/libs/gui/DisplayEventReceiver.cpp index fd6aaf8b46..1fed509003 100644 --- a/libs/gui/DisplayEventReceiver.cpp +++ b/libs/gui/DisplayEventReceiver.cpp @@ -79,10 +79,9 @@ status_t DisplayEventReceiver::requestNextVsync() { return NO_INIT; } -status_t DisplayEventReceiver::toggleConfigEvents( - ISurfaceComposer::ConfigChanged configChangeFlag) { +status_t DisplayEventReceiver::requestLatestConfig() { if (mEventConnection != nullptr) { - mEventConnection->toggleConfigEvents(configChangeFlag); + mEventConnection->requestLatestConfig(); return NO_ERROR; } return NO_INIT; diff --git a/libs/gui/IDisplayEventConnection.cpp b/libs/gui/IDisplayEventConnection.cpp index dda5acf8a7..aa74bfd3f8 100644 --- a/libs/gui/IDisplayEventConnection.cpp +++ b/libs/gui/IDisplayEventConnection.cpp @@ -26,8 +26,8 @@ enum class Tag : uint32_t { STEAL_RECEIVE_CHANNEL = IBinder::FIRST_CALL_TRANSACTION, SET_VSYNC_RATE, REQUEST_NEXT_VSYNC, - TOGGLE_CONFIG_EVENTS, - LAST = TOGGLE_CONFIG_EVENTS, + REQUEST_LATEST_CONFIG, + LAST = REQUEST_LATEST_CONFIG, }; } // Anonymous namespace @@ -55,10 +55,9 @@ public: Tag::REQUEST_NEXT_VSYNC); } - void toggleConfigEvents(ISurfaceComposer::ConfigChanged configChangeFlag) override { - callRemoteAsync(Tag::TOGGLE_CONFIG_EVENTS, - configChangeFlag); + void requestLatestConfig() override { + callRemoteAsync( + Tag::REQUEST_LATEST_CONFIG); } }; @@ -81,8 +80,8 @@ status_t BnDisplayEventConnection::onTransact(uint32_t code, const Parcel& data, return callLocal(data, reply, &IDisplayEventConnection::setVsyncRate); case Tag::REQUEST_NEXT_VSYNC: return callLocalAsync(data, reply, &IDisplayEventConnection::requestNextVsync); - case Tag::TOGGLE_CONFIG_EVENTS: - return callLocalAsync(data, reply, &IDisplayEventConnection::toggleConfigEvents); + case Tag::REQUEST_LATEST_CONFIG: + return callLocalAsync(data, reply, &IDisplayEventConnection::requestLatestConfig); } } diff --git a/libs/gui/include/gui/DisplayEventDispatcher.h b/libs/gui/include/gui/DisplayEventDispatcher.h index fcdf6bfa7e..f210c34196 100644 --- a/libs/gui/include/gui/DisplayEventDispatcher.h +++ b/libs/gui/include/gui/DisplayEventDispatcher.h @@ -31,7 +31,7 @@ public: status_t initialize(); void dispose(); status_t scheduleVsync(); - void toggleConfigEvents(ISurfaceComposer::ConfigChanged configChangeFlag); + void requestLatestConfig(); int getFd() const; virtual int handleEvent(int receiveFd, int events, void* data); @@ -42,7 +42,6 @@ private: sp mLooper; DisplayEventReceiver mReceiver; bool mWaitingForVsync; - ISurfaceComposer::ConfigChanged mConfigChangeFlag; virtual void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count) = 0; virtual void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, diff --git a/libs/gui/include/gui/DisplayEventReceiver.h b/libs/gui/include/gui/DisplayEventReceiver.h index d9a0253781..8d49184caf 100644 --- a/libs/gui/include/gui/DisplayEventReceiver.h +++ b/libs/gui/include/gui/DisplayEventReceiver.h @@ -147,9 +147,10 @@ public: status_t requestNextVsync(); /* - * toggleConfigEvents() toggles delivery of config change events. + * requestLatestConfig() force-requests the current config for the primary + * display. */ - status_t toggleConfigEvents(ISurfaceComposer::ConfigChanged configChangeFlag); + status_t requestLatestConfig(); private: sp mEventConnection; diff --git a/libs/gui/include/gui/IDisplayEventConnection.h b/libs/gui/include/gui/IDisplayEventConnection.h index 8b35ef6486..674aafd81c 100644 --- a/libs/gui/include/gui/IDisplayEventConnection.h +++ b/libs/gui/include/gui/IDisplayEventConnection.h @@ -53,11 +53,9 @@ public: virtual void requestNextVsync() = 0; // Asynchronous /* - * togglesConfigEvents() configures whether or not display config changes - * should be propagated. + * requestLatestConfig() requests the config for the primary display. */ - virtual void toggleConfigEvents( - ISurfaceComposer::ConfigChanged configChangeFlag) = 0; // Asynchronous + virtual void requestLatestConfig() = 0; // Asynchronous }; class BnDisplayEventConnection : public SafeBnInterface { diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp index 0ff33ac747..ea51245ac6 100644 --- a/libs/nativedisplay/AChoreographer.cpp +++ b/libs/nativedisplay/AChoreographer.cpp @@ -17,24 +17,63 @@ #define LOG_TAG "Choreographer" //#define LOG_NDEBUG 0 -#include +#include #include #include #include +#include +#include #include -#include #include #include +#include #include #include #include -namespace android { +namespace { +struct { + // Global JVM that is provided by zygote + JavaVM* jvm = nullptr; + struct { + jclass clazz; + jmethodID getInstance; + jmethodID registerNativeChoreographerForRefreshRateCallbacks; + jmethodID unregisterNativeChoreographerForRefreshRateCallbacks; + } displayManagerGlobal; +} gJni; + +// Gets the JNIEnv* for this thread, and performs one-off initialization if we +// have never retrieved a JNIEnv* pointer before. +JNIEnv* getJniEnv() { + if (gJni.jvm == nullptr) { + ALOGW("AChoreographer: No JVM provided!"); + return nullptr; + } + + JNIEnv* env = nullptr; + if (gJni.jvm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK) { + ALOGD("Attaching thread to JVM for AChoreographer"); + JavaVMAttachArgs args = {JNI_VERSION_1_4, "AChoreographer_env", NULL}; + jint attachResult = gJni.jvm->AttachCurrentThreadAsDaemon(&env, (void*)&args); + if (attachResult != JNI_OK) { + ALOGE("Unable to attach thread. Error: %d", attachResult); + return nullptr; + } + } + if (env == nullptr) { + ALOGW("AChoreographer: No JNI env available!"); + } + return env; +} -static inline const char* toString(bool value) { +inline const char* toString(bool value) { return value ? "true" : "false"; } +} // namespace + +namespace android { struct FrameCallback { AChoreographer_frameCallback callback; @@ -52,24 +91,43 @@ struct FrameCallback { struct RefreshRateCallback { AChoreographer_refreshRateCallback callback; void* data; + bool firstCallbackFired = false; }; +class Choreographer; + +struct { + std::mutex lock; + std::vector ptrs GUARDED_BY(lock); + bool registeredToDisplayManager GUARDED_BY(lock) = false; + + std::atomic mLastKnownVsync = -1; +} gChoreographers; + class Choreographer : public DisplayEventDispatcher, public MessageHandler { public: - explicit Choreographer(const sp& looper); + explicit Choreographer(const sp& looper) EXCLUDES(gChoreographers.lock); void postFrameCallbackDelayed(AChoreographer_frameCallback cb, AChoreographer_frameCallback64 cb64, void* data, nsecs_t delay); - void registerRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data); + void registerRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data) + EXCLUDES(gChoreographers.lock); void unregisterRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data); + // Drains the queue of pending vsync periods and dispatches refresh rate + // updates to callbacks. + // The assumption is that this method is only called on a single + // processing thread, either by looper or by AChoreographer_handleEvents + void handleRefreshRateUpdates(); + void scheduleLatestConfigRequest(); enum { MSG_SCHEDULE_CALLBACKS = 0, - MSG_SCHEDULE_VSYNC = 1 + MSG_SCHEDULE_VSYNC = 1, + MSG_HANDLE_REFRESH_RATE_UPDATES = 2, }; virtual void handleMessage(const Message& message) override; static Choreographer* getForThread(); - virtual ~Choreographer() = default; + virtual ~Choreographer() override EXCLUDES(gChoreographers.lock); private: Choreographer(const Choreographer&) = delete; @@ -81,21 +139,17 @@ private: void scheduleCallbacks(); + std::mutex mLock; // Protected by mLock std::priority_queue mFrameCallbacks; - - // Protected by mLock std::vector mRefreshRateCallbacks; - nsecs_t mVsyncPeriod = 0; - mutable Mutex mLock; + nsecs_t mLatestVsyncPeriod = -1; const sp mLooper; const std::thread::id mThreadId; - const std::optional mInternalDisplayId; }; - static thread_local Choreographer* gChoreographer; Choreographer* Choreographer::getForThread() { if (gChoreographer == nullptr) { @@ -115,17 +169,47 @@ Choreographer* Choreographer::getForThread() { } Choreographer::Choreographer(const sp& looper) - : DisplayEventDispatcher(looper), + : DisplayEventDispatcher(looper, ISurfaceComposer::VsyncSource::eVsyncSourceApp, + ISurfaceComposer::ConfigChanged::eConfigChangedDispatch), mLooper(looper), - mThreadId(std::this_thread::get_id()), - mInternalDisplayId(SurfaceComposerClient::getInternalDisplayId()) {} + mThreadId(std::this_thread::get_id()) { + std::lock_guard _l(gChoreographers.lock); + gChoreographers.ptrs.push_back(this); +} + +Choreographer::~Choreographer() { + std::lock_guard _l(gChoreographers.lock); + gChoreographers.ptrs.erase(std::remove_if(gChoreographers.ptrs.begin(), + gChoreographers.ptrs.end(), + [=](Choreographer* c) { return c == this; }), + gChoreographers.ptrs.end()); + // Only poke DisplayManagerGlobal to unregister if we previously registered + // callbacks. + if (gChoreographers.ptrs.empty() && gChoreographers.registeredToDisplayManager) { + JNIEnv* env = getJniEnv(); + if (env == nullptr) { + ALOGW("JNI environment is unavailable, skipping choreographer cleanup"); + return; + } + jobject dmg = env->CallStaticObjectMethod(gJni.displayManagerGlobal.clazz, + gJni.displayManagerGlobal.getInstance); + if (dmg == nullptr) { + ALOGW("DMS is not initialized yet, skipping choreographer cleanup"); + } else { + env->CallVoidMethod(dmg, + gJni.displayManagerGlobal + .unregisterNativeChoreographerForRefreshRateCallbacks); + env->DeleteLocalRef(dmg); + } + } +} void Choreographer::postFrameCallbackDelayed( AChoreographer_frameCallback cb, AChoreographer_frameCallback64 cb64, void* data, nsecs_t delay) { nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); FrameCallback callback{cb, cb64, data, now + delay}; { - AutoMutex _l{mLock}; + std::lock_guard _l{mLock}; mFrameCallbacks.push(callback); } if (callback.dueTime <= now) { @@ -150,37 +234,68 @@ void Choreographer::postFrameCallbackDelayed( } void Choreographer::registerRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data) { + std::lock_guard _l{mLock}; + for (const auto& callback : mRefreshRateCallbacks) { + // Don't re-add callbacks. + if (cb == callback.callback && data == callback.data) { + return; + } + } + mRefreshRateCallbacks.emplace_back( + RefreshRateCallback{.callback = cb, .data = data, .firstCallbackFired = false}); + bool needsRegistration = false; { - AutoMutex _l{mLock}; - for (const auto& callback : mRefreshRateCallbacks) { - // Don't re-add callbacks. - if (cb == callback.callback && data == callback.data) { - return; + std::lock_guard _l2(gChoreographers.lock); + needsRegistration = !gChoreographers.registeredToDisplayManager; + } + if (needsRegistration) { + JNIEnv* env = getJniEnv(); + jobject dmg = env->CallStaticObjectMethod(gJni.displayManagerGlobal.clazz, + gJni.displayManagerGlobal.getInstance); + if (env == nullptr) { + ALOGW("JNI environment is unavailable, skipping registeration"); + return; + } + if (dmg == nullptr) { + ALOGW("DMS is not initialized yet: skipping registration"); + return; + } else { + env->CallVoidMethod(dmg, + gJni.displayManagerGlobal + .registerNativeChoreographerForRefreshRateCallbacks, + reinterpret_cast(this)); + env->DeleteLocalRef(dmg); + { + std::lock_guard _l2(gChoreographers.lock); + gChoreographers.registeredToDisplayManager = true; } } - mRefreshRateCallbacks.emplace_back(RefreshRateCallback{cb, data}); - toggleConfigEvents(ISurfaceComposer::ConfigChanged::eConfigChangedDispatch); + } else { + scheduleLatestConfigRequest(); } } void Choreographer::unregisterRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data) { - { - AutoMutex _l{mLock}; - mRefreshRateCallbacks.erase(std::remove_if(mRefreshRateCallbacks.begin(), - mRefreshRateCallbacks.end(), - [&](const RefreshRateCallback& callback) { - return cb == callback.callback && - data == callback.data; - }), - mRefreshRateCallbacks.end()); - if (mRefreshRateCallbacks.empty()) { - toggleConfigEvents(ISurfaceComposer::ConfigChanged::eConfigChangedSuppress); - // If callbacks are empty then clear out the most recently seen - // vsync period so that when another callback is registered then the - // up-to-date refresh rate can be communicated to the app again. - mVsyncPeriod = 0; - } + std::lock_guard _l{mLock}; + mRefreshRateCallbacks.erase(std::remove_if(mRefreshRateCallbacks.begin(), + mRefreshRateCallbacks.end(), + [&](const RefreshRateCallback& callback) { + return cb == callback.callback && + data == callback.data; + }), + mRefreshRateCallbacks.end()); +} + +void Choreographer::scheduleLatestConfigRequest() { + if (mLooper != nullptr) { + Message m{MSG_HANDLE_REFRESH_RATE_UPDATES}; + mLooper->sendMessage(this, m); + } else { + // If the looper thread is detached from Choreographer, then refresh rate + // changes will be handled in AChoreographer_handlePendingEvents, so we + // need to redispatch a config from SF + requestLatestConfig(); } } @@ -188,7 +303,7 @@ void Choreographer::scheduleCallbacks() { const nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); nsecs_t dueTime; { - AutoMutex _{mLock}; + std::lock_guard _l{mLock}; // If there are no pending callbacks then don't schedule a vsync if (mFrameCallbacks.empty()) { return; @@ -203,13 +318,35 @@ void Choreographer::scheduleCallbacks() { } } +void Choreographer::handleRefreshRateUpdates() { + std::vector callbacks{}; + const nsecs_t pendingPeriod = gChoreographers.mLastKnownVsync.load(); + const nsecs_t lastPeriod = mLatestVsyncPeriod; + if (pendingPeriod > 0) { + mLatestVsyncPeriod = pendingPeriod; + } + { + std::lock_guard _l{mLock}; + for (auto& cb : mRefreshRateCallbacks) { + callbacks.push_back(cb); + cb.firstCallbackFired = true; + } + } + + for (auto& cb : callbacks) { + if (!cb.firstCallbackFired || (pendingPeriod > 0 && pendingPeriod != lastPeriod)) { + cb.callback(pendingPeriod, cb.data); + } + } +} + // TODO(b/74619554): The PhysicalDisplayId is ignored because SF only emits VSYNC events for the // internal display and DisplayEventReceiver::requestNextVsync only allows requesting VSYNC for // the internal display implicitly. void Choreographer::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId, uint32_t) { std::vector callbacks{}; { - AutoMutex _l{mLock}; + std::lock_guard _l{mLock}; nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); while (!mFrameCallbacks.empty() && mFrameCallbacks.top().dueTime < now) { callbacks.push_back(mFrameCallbacks.top()); @@ -236,20 +373,29 @@ void Choreographer::dispatchHotplug(nsecs_t, PhysicalDisplayId displayId, bool c // display, so as such Choreographer does not support the notion of multiple // displays. When multi-display choreographer is properly supported, then // PhysicalDisplayId should no longer be ignored. -void Choreographer::dispatchConfigChanged(nsecs_t, PhysicalDisplayId, int32_t, +void Choreographer::dispatchConfigChanged(nsecs_t, PhysicalDisplayId displayId, int32_t configId, nsecs_t vsyncPeriod) { + ALOGV("choreographer %p ~ received config change event " + "(displayId=%" ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", configId=%d).", + this, displayId, configId); + + const nsecs_t lastPeriod = mLatestVsyncPeriod; + std::vector callbacks{}; { - AutoMutex _l{mLock}; - for (const auto& cb : mRefreshRateCallbacks) { - // Only perform the callback when the old refresh rate is different - // from the new refresh rate, so that we don't dispatch the callback - // on every single configuration change. - if (mVsyncPeriod != vsyncPeriod) { - cb.callback(vsyncPeriod, cb.data); - } + std::lock_guard _l{mLock}; + for (auto& cb : mRefreshRateCallbacks) { + callbacks.push_back(cb); + cb.firstCallbackFired = true; + } + } + + for (auto& cb : callbacks) { + if (!cb.firstCallbackFired || (vsyncPeriod > 0 && vsyncPeriod != lastPeriod)) { + cb.callback(vsyncPeriod, cb.data); } - mVsyncPeriod = vsyncPeriod; } + + mLatestVsyncPeriod = vsyncPeriod; } void Choreographer::handleMessage(const Message& message) { @@ -260,19 +406,80 @@ void Choreographer::handleMessage(const Message& message) { case MSG_SCHEDULE_VSYNC: scheduleVsync(); break; + case MSG_HANDLE_REFRESH_RATE_UPDATES: + handleRefreshRateUpdates(); + break; } } -} - -/* Glue for the NDK interface */ - +} // namespace android using namespace android; static inline Choreographer* AChoreographer_to_Choreographer(AChoreographer* choreographer) { return reinterpret_cast(choreographer); } +// Glue for private C api +namespace android { +void AChoreographer_signalRefreshRateCallbacks(nsecs_t vsyncPeriod) EXCLUDES(gChoreographers.lock) { + std::lock_guard _l(gChoreographers.lock); + gChoreographers.mLastKnownVsync.store(vsyncPeriod); + for (auto c : gChoreographers.ptrs) { + c->scheduleLatestConfigRequest(); + } +} + +void AChoreographer_initJVM(JNIEnv* env) { + env->GetJavaVM(&gJni.jvm); + // Now we need to find the java classes. + jclass dmgClass = env->FindClass("android/hardware/display/DisplayManagerGlobal"); + gJni.displayManagerGlobal.clazz = static_cast(env->NewGlobalRef(dmgClass)); + gJni.displayManagerGlobal.getInstance = + env->GetStaticMethodID(dmgClass, "getInstance", + "()Landroid/hardware/display/DisplayManagerGlobal;"); + gJni.displayManagerGlobal.registerNativeChoreographerForRefreshRateCallbacks = + env->GetMethodID(dmgClass, "registerNativeChoreographerForRefreshRateCallbacks", "()V"); + gJni.displayManagerGlobal.unregisterNativeChoreographerForRefreshRateCallbacks = + env->GetMethodID(dmgClass, "unregisterNativeChoreographerForRefreshRateCallbacks", + "()V"); +} + +AChoreographer* AChoreographer_routeGetInstance() { + return AChoreographer_getInstance(); +} +void AChoreographer_routePostFrameCallback(AChoreographer* choreographer, + AChoreographer_frameCallback callback, void* data) { + return AChoreographer_postFrameCallback(choreographer, callback, data); +} +void AChoreographer_routePostFrameCallbackDelayed(AChoreographer* choreographer, + AChoreographer_frameCallback callback, void* data, + long delayMillis) { + return AChoreographer_postFrameCallbackDelayed(choreographer, callback, data, delayMillis); +} +void AChoreographer_routePostFrameCallback64(AChoreographer* choreographer, + AChoreographer_frameCallback64 callback, void* data) { + return AChoreographer_postFrameCallback64(choreographer, callback, data); +} +void AChoreographer_routePostFrameCallbackDelayed64(AChoreographer* choreographer, + AChoreographer_frameCallback64 callback, + void* data, uint32_t delayMillis) { + return AChoreographer_postFrameCallbackDelayed64(choreographer, callback, data, delayMillis); +} +void AChoreographer_routeRegisterRefreshRateCallback(AChoreographer* choreographer, + AChoreographer_refreshRateCallback callback, + void* data) { + return AChoreographer_registerRefreshRateCallback(choreographer, callback, data); +} +void AChoreographer_routeUnregisterRefreshRateCallback(AChoreographer* choreographer, + AChoreographer_refreshRateCallback callback, + void* data) { + return AChoreographer_unregisterRefreshRateCallback(choreographer, callback, data); +} + +} // namespace android + +/* Glue for the NDK interface */ + static inline const Choreographer* AChoreographer_to_Choreographer( const AChoreographer* choreographer) { return reinterpret_cast(choreographer); @@ -343,5 +550,6 @@ void AChoreographer_handlePendingEvents(AChoreographer* choreographer, void* dat // Pass dummy fd and events args to handleEvent, since the underlying // DisplayEventDispatcher doesn't need them outside of validating that a // Looper instance didn't break, but these args circumvent those checks. - AChoreographer_to_Choreographer(choreographer)->handleEvent(-1, Looper::EVENT_INPUT, data); + Choreographer* impl = AChoreographer_to_Choreographer(choreographer); + impl->handleEvent(-1, Looper::EVENT_INPUT, data); } diff --git a/libs/nativedisplay/Android.bp b/libs/nativedisplay/Android.bp index c9565780e0..f56b3a2178 100644 --- a/libs/nativedisplay/Android.bp +++ b/libs/nativedisplay/Android.bp @@ -12,23 +12,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -ndk_headers { - name: "libnativedisplay_ndk_headers", - from: "include/android", - to: "android", - srcs: ["include/android/*.h"], - license: "NOTICE", -} - cc_library_headers { name: "libnativedisplay_headers", - export_include_dirs: ["include"], + export_include_dirs: ["include",], } -cc_library { +cc_library_shared { name: "libnativedisplay", export_include_dirs: [ "include", + "include-private", ], clang: true, @@ -63,6 +56,10 @@ cc_library { "libnativehelper", ], + export_shared_lib_headers: [ + "libnativehelper", + ], + header_libs: [ "libnativedisplay_headers", ], diff --git a/libs/nativedisplay/include-private/private/android/choreographer.h b/libs/nativedisplay/include-private/private/android/choreographer.h new file mode 100644 index 0000000000..21649304bf --- /dev/null +++ b/libs/nativedisplay/include-private/private/android/choreographer.h @@ -0,0 +1,55 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include + +namespace android { + +// Registers the global JVM for AChoreographer +void AChoreographer_initJVM(JNIEnv* env); + +// Signals all AChoregorapher* instances that a new vsync period is available +// for consumption by callbacks. +void AChoreographer_signalRefreshRateCallbacks(int64_t vsyncPeriod); + +// Trampoline functions allowing libandroid.so to define the NDK symbols without including +// the entirety of libnativedisplay as a whole static lib. As libnativedisplay +// maintains global state, libnativedisplay can never be directly statically +// linked so that global state won't be duplicated. This way libandroid.so can +// reroute the NDK methods into the implementations defined by libnativedisplay +AChoreographer* AChoreographer_routeGetInstance(); +void AChoreographer_routePostFrameCallback(AChoreographer* choreographer, + AChoreographer_frameCallback callback, void* data); +void AChoreographer_routePostFrameCallbackDelayed(AChoreographer* choreographer, + AChoreographer_frameCallback callback, void* data, + long delayMillis); +void AChoreographer_routePostFrameCallback64(AChoreographer* choreographer, + AChoreographer_frameCallback64 callback, void* data); +void AChoreographer_routePostFrameCallbackDelayed64(AChoreographer* choreographer, + AChoreographer_frameCallback64 callback, + void* data, uint32_t delayMillis); +void AChoreographer_routeRegisterRefreshRateCallback(AChoreographer* choreographer, + AChoreographer_refreshRateCallback callback, + void* data); +void AChoreographer_routeUnregisterRefreshRateCallback(AChoreographer* choreographer, + AChoreographer_refreshRateCallback callback, + void* data); + +} // namespace android diff --git a/libs/nativedisplay/include/android/choreographer.h b/libs/nativedisplay/include/android/choreographer.h deleted file mode 100644 index 5fd3de9f3c..0000000000 --- a/libs/nativedisplay/include/android/choreographer.h +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @addtogroup Choreographer - * @{ - */ - -/** - * @file choreographer.h - */ - -#ifndef ANDROID_CHOREOGRAPHER_H -#define ANDROID_CHOREOGRAPHER_H - -#include -#include - -__BEGIN_DECLS - -struct AChoreographer; -typedef struct AChoreographer AChoreographer; - -/** - * Prototype of the function that is called when a new frame is being rendered. - * It's passed the time that the frame is being rendered as nanoseconds in the - * CLOCK_MONOTONIC time base, as well as the data pointer provided by the - * application that registered a callback. All callbacks that run as part of - * rendering a frame will observe the same frame time, so it should be used - * whenever events need to be synchronized (e.g. animations). - */ -typedef void (*AChoreographer_frameCallback)(long frameTimeNanos, void* data); - -/** - * Prototype of the function that is called when a new frame is being rendered. - * It's passed the time that the frame is being rendered as nanoseconds in the - * CLOCK_MONOTONIC time base, as well as the data pointer provided by the - * application that registered a callback. All callbacks that run as part of - * rendering a frame will observe the same frame time, so it should be used - * whenever events need to be synchronized (e.g. animations). - */ -typedef void (*AChoreographer_frameCallback64)(int64_t frameTimeNanos, void* data); - -/** - * Prototype of the function that is called when the display refresh rate - * changes. It's passed the new vsync period in nanoseconds, as well as the data - * pointer provided by the application that registered a callback. - */ -typedef void (*AChoreographer_refreshRateCallback)(int64_t vsyncPeriodNanos, void* data); - -#if __ANDROID_API__ >= 24 - -/** - * Get the AChoreographer instance for the current thread. This must be called - * on an ALooper thread. - * - * Available since API level 24. - */ -AChoreographer* AChoreographer_getInstance() __INTRODUCED_IN(24); - -/** - * Deprecated: Use AChoreographer_postFrameCallback64 instead. - */ -void AChoreographer_postFrameCallback(AChoreographer* choreographer, - AChoreographer_frameCallback callback, void* data) __INTRODUCED_IN(24) __DEPRECATED_IN(29); - -/** - * Deprecated: Use AChoreographer_postFrameCallbackDelayed64 instead. - */ -void AChoreographer_postFrameCallbackDelayed(AChoreographer* choreographer, - AChoreographer_frameCallback callback, void* data, - long delayMillis) __INTRODUCED_IN(24) __DEPRECATED_IN(29); - -#endif /* __ANDROID_API__ >= 24 */ - -#if __ANDROID_API__ >= 29 - -/** - * Power a callback to be run on the next frame. The data pointer provided will - * be passed to the callback function when it's called. - * - * Available since API level 29. - */ -void AChoreographer_postFrameCallback64(AChoreographer* choreographer, - AChoreographer_frameCallback64 callback, void* data) __INTRODUCED_IN(29); - -/** - * Post a callback to be run on the frame following the specified delay. The - * data pointer provided will be passed to the callback function when it's - * called. - * - * Available since API level 29. - */ -void AChoreographer_postFrameCallbackDelayed64(AChoreographer* choreographer, - AChoreographer_frameCallback64 callback, void* data, uint32_t delayMillis) __INTRODUCED_IN(29); - -#endif /* __ANDROID_API__ >= 29 */ - -#if __ANDROID_API__ >= 30 - -/** - * Registers a callback to be run when the display refresh rate changes. The - * data pointer provided will be passed to the callback function when it's - * called. The same callback may be registered multiple times, provided that a - * different data pointer is provided each time. - * - * If an application registers a callback for this choreographer instance when - * no new callbacks were previously registered, that callback is guaranteed to - * be dispatched. However, if the callback and associated data pointer are - * unregistered prior to running the callback, then the callback may be silently - * dropped. - * - * This api is thread-safe. Any thread is allowed to register a new refresh - * rate callback for the choreographer instance. - */ -void AChoreographer_registerRefreshRateCallback(AChoreographer* choreographer, - AChoreographer_refreshRateCallback, void* data); - -/** - * Unregisters a callback to be run when the display refresh rate changes, along - * with the data pointer previously provided when registering the callback. The - * callback is only unregistered when the data pointer matches one that was - * previously registered. - * - * This api is thread-safe. Any thread is allowed to unregister an existing - * refresh rate callback for the choreographer instance. When a refresh rate - * callback and associated data pointer are unregistered, then there is a - * guarantee that when the unregistration completes that that callback will not - * be run with the data pointer passed. - */ -void AChoreographer_unregisterRefreshRateCallback(AChoreographer* choreographer, - AChoreographer_refreshRateCallback, void* data); -#endif /* __ANDROID_API__ >= 30 */ - -__END_DECLS - -#endif // ANDROID_CHOREOGRAPHER_H - -/** @} */ diff --git a/libs/nativedisplay/include/surfacetexture/surface_texture_platform.h b/libs/nativedisplay/include/surfacetexture/surface_texture_platform.h index 6a94a771e8..e2d036bfb0 100644 --- a/libs/nativedisplay/include/surfacetexture/surface_texture_platform.h +++ b/libs/nativedisplay/include/surfacetexture/surface_texture_platform.h @@ -19,7 +19,7 @@ #include #include - +#include #include // This file provides a facade API on top of SurfaceTexture, which avoids using @@ -30,6 +30,20 @@ struct ASurfaceTexture; namespace android { +// Trampoline functions allowing libandroid.so to define the NDK symbols without including +// the entirety of libnativedisplay as a whole static lib. As libnativedisplay +// maintains global state, libnativedisplay can never be directly statically +// linked so that global state won't be duplicated. This way libandroid.so can +// reroute the NDK methods into the implementations defined by libnativedisplay +ANativeWindow* ASurfaceTexture_routeAcquireANativeWindow(ASurfaceTexture* st); +int ASurfaceTexture_routeAttachToGLContext(ASurfaceTexture* st, uint32_t texName); +int ASurfaceTexture_routeDetachFromGLContext(ASurfaceTexture* st); +void ASurfaceTexture_routeRelease(ASurfaceTexture* st); +int ASurfaceTexture_routeUpdateTexImage(ASurfaceTexture* st); +void ASurfaceTexture_routeGetTransformMatrix(ASurfaceTexture* st, float mtx[16]); +int64_t ASurfaceTexture_routeGetTimestamp(ASurfaceTexture* st); +ASurfaceTexture* ASurfaceTexture_routeFromSurfaceTexture(JNIEnv* env, jobject surfacetexture); + /** * ASurfaceTexture_getCurrentTextureTarget returns the texture target of the * current texture. diff --git a/libs/nativedisplay/libnativedisplay.map.txt b/libs/nativedisplay/libnativedisplay.map.txt index 483fb25cb7..fc59431d08 100644 --- a/libs/nativedisplay/libnativedisplay.map.txt +++ b/libs/nativedisplay/libnativedisplay.map.txt @@ -20,6 +20,15 @@ LIBNATIVEDISPLAY { LIBNATIVEDISPLAY_PLATFORM { global: extern "C++" { + android::AChoreographer_initJVM*; + android::AChoreographer_routeGetInstance*; + android::AChoreographer_routePostFrameCallback*; + android::AChoreographer_routePostFrameCallbackDelayed*; + android::AChoreographer_routePostFrameCallback64*; + android::AChoreographer_routePostFrameCallbackDelayed64*; + android::AChoreographer_routeRegisterRefreshRateCallback*; + android::AChoreographer_routeUnregisterRefreshRateCallback*; + android::AChoreographer_signalRefreshRateCallbacks*; android::ADisplay_acquirePhysicalDisplays*; android::ADisplay_release*; android::ADisplay_getMaxSupportedFps*; @@ -36,6 +45,14 @@ LIBNATIVEDISPLAY_PLATFORM { android::ASurfaceTexture_takeConsumerOwnership*; android::ASurfaceTexture_releaseConsumerOwnership*; android::ASurfaceTexture_dequeueBuffer*; + android::ASurfaceTexture_routeAcquireANativeWindow*; + android::ASurfaceTexture_routeAttachToGLContext*; + android::ASurfaceTexture_routeDetachFromGLContext*; + android::ASurfaceTexture_routeGetTimestamp*; + android::ASurfaceTexture_routeGetTransformMatrix*; + android::ASurfaceTexture_routeUpdateTexImage*; + android::ASurfaceTexture_routeFromSurfaceTexture*; + android::ASurfaceTexture_routeRelease*; android::SurfaceTexture*; }; ASurfaceTexture_acquireANativeWindow; diff --git a/libs/nativedisplay/surfacetexture/surface_texture.cpp b/libs/nativedisplay/surfacetexture/surface_texture.cpp index 1670fbba57..d1bcd8d1b1 100644 --- a/libs/nativedisplay/surfacetexture/surface_texture.cpp +++ b/libs/nativedisplay/surfacetexture/surface_texture.cpp @@ -149,6 +149,37 @@ int64_t ASurfaceTexture_getTimestamp(ASurfaceTexture* st) { // The following functions are private/unstable API. namespace android { +ANativeWindow* ASurfaceTexture_routeAcquireANativeWindow(ASurfaceTexture* st) { + return ASurfaceTexture_acquireANativeWindow(st); +} + +int ASurfaceTexture_routeAttachToGLContext(ASurfaceTexture* st, uint32_t texName) { + return ASurfaceTexture_attachToGLContext(st, texName); +} + +void ASurfaceTexture_routeRelease(ASurfaceTexture* st) { + return ASurfaceTexture_release(st); +} + +int ASurfaceTexture_routeDetachFromGLContext(ASurfaceTexture* st) { + return ASurfaceTexture_detachFromGLContext(st); +} + +int ASurfaceTexture_routeUpdateTexImage(ASurfaceTexture* st) { + return ASurfaceTexture_updateTexImage(st); +} + +void ASurfaceTexture_routeGetTransformMatrix(ASurfaceTexture* st, float mtx[16]) { + return ASurfaceTexture_getTransformMatrix(st, mtx); +} + +int64_t ASurfaceTexture_routeGetTimestamp(ASurfaceTexture* st) { + return ASurfaceTexture_getTimestamp(st); +} + +ASurfaceTexture* ASurfaceTexture_routeFromSurfaceTexture(JNIEnv* env, jobject surfacetexture) { + return ASurfaceTexture_fromSurfaceTexture(env, surfacetexture); +} unsigned int ASurfaceTexture_getCurrentTextureTarget(ASurfaceTexture* st) { return st->consumer->getCurrentTextureTarget(); diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp index 5dedb6a1e7..cee36a121f 100644 --- a/services/surfaceflinger/Scheduler/EventThread.cpp +++ b/services/surfaceflinger/Scheduler/EventThread.cpp @@ -152,16 +152,9 @@ void EventThreadConnection::requestNextVsync() { mEventThread->requestNextVsync(this); } -void EventThreadConnection::toggleConfigEvents(ISurfaceComposer::ConfigChanged configChangeFlag) { - ATRACE_NAME("enableConfigEvents"); - mConfigChanged = configChangeFlag; - - // In principle it's possible for rapidly toggling config events to drop an - // event here, but it's unlikely in practice. - if (configChangeFlag == ISurfaceComposer::eConfigChangedDispatch) { - mForcedConfigChangeDispatch = true; - mEventThread->requestLatestConfig(); - } +void EventThreadConnection::requestLatestConfig() { + ATRACE_NAME("requestLatestConfig"); + mEventThread->requestLatestConfig(this); } status_t EventThreadConnection::postEvent(const DisplayEventReceiver::Event& event) { @@ -276,8 +269,12 @@ void EventThread::requestNextVsync(const sp& connection) } } -void EventThread::requestLatestConfig() { +void EventThread::requestLatestConfig(const sp& connection) { std::lock_guard lock(mMutex); + if (connection->mForcedConfigChangeDispatch) { + return; + } + connection->mForcedConfigChangeDispatch = true; auto pendingConfigChange = std::find_if(std::begin(mPendingEvents), std::end(mPendingEvents), [&](const DisplayEventReceiver::Event& event) { @@ -384,6 +381,10 @@ void EventThread::threadMain(std::unique_lock& lock) { vsyncRequested |= connection->vsyncRequest != VSyncRequest::None; if (event && shouldConsumeEvent(*event, connection)) { + if (event->header.type == DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED && + connection->mForcedConfigChangeDispatch) { + connection->mForcedConfigChangeDispatch = false; + } consumers.push_back(connection); } @@ -459,8 +460,8 @@ bool EventThread::shouldConsumeEvent(const DisplayEventReceiver::Event& event, return true; case DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED: { - const bool forcedDispatch = connection->mForcedConfigChangeDispatch.exchange(false); - return forcedDispatch || + const bool oneTimeDispatch = connection->mForcedConfigChangeDispatch; + return oneTimeDispatch || connection->mConfigChanged == ISurfaceComposer::eConfigChangedDispatch; } diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h index 9e7086eb0c..64acbd72d0 100644 --- a/services/surfaceflinger/Scheduler/EventThread.h +++ b/services/surfaceflinger/Scheduler/EventThread.h @@ -81,19 +81,19 @@ public: status_t stealReceiveChannel(gui::BitTube* outChannel) override; status_t setVsyncRate(uint32_t rate) override; void requestNextVsync() override; // asynchronous - void toggleConfigEvents(ISurfaceComposer::ConfigChanged configChangeFlag) override; + void requestLatestConfig() override; // asynchronous // Called in response to requestNextVsync. const ResyncCallback resyncCallback; VSyncRequest vsyncRequest = VSyncRequest::None; - std::atomic mConfigChanged = + ISurfaceComposer::ConfigChanged mConfigChanged = ISurfaceComposer::ConfigChanged::eConfigChangedSuppress; // Store whether we need to force dispatching a config change separately - // if mConfigChanged ever changes before the config change is dispatched // then we still need to propagate an initial config to the app if we // haven't already. - std::atomic mForcedConfigChangeDispatch = false; + bool mForcedConfigChangeDispatch = false; private: virtual void onFirstRef(); @@ -129,11 +129,10 @@ public: virtual void setVsyncRate(uint32_t rate, const sp& connection) = 0; // Requests the next vsync. If resetIdleTimer is set to true, it resets the idle timer. virtual void requestNextVsync(const sp& connection) = 0; - // Dispatches the most recent configuration // Usage of this method assumes that only the primary internal display // supports multiple display configurations. - virtual void requestLatestConfig() = 0; + virtual void requestLatestConfig(const sp& connection) = 0; // Retrieves the number of event connections tracked by this EventThread. virtual size_t getEventThreadConnectionCount() = 0; @@ -154,7 +153,7 @@ public: status_t registerDisplayEventConnection(const sp& connection) override; void setVsyncRate(uint32_t rate, const sp& connection) override; void requestNextVsync(const sp& connection) override; - void requestLatestConfig() override; + void requestLatestConfig(const sp& connection) override; // called before the screen is turned off from main thread void onScreenReleased() override; diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h index 50eb390471..054aaf8ae1 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h +++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h @@ -40,7 +40,7 @@ public: status_t(const sp &)); MOCK_METHOD2(setVsyncRate, void(uint32_t, const sp &)); MOCK_METHOD1(requestNextVsync, void(const sp &)); - MOCK_METHOD0(requestLatestConfig, void()); + MOCK_METHOD1(requestLatestConfig, void(const sp &)); MOCK_METHOD1(pauseVsyncCallback, void(bool)); MOCK_METHOD0(getEventThreadConnectionCount, size_t()); }; -- cgit v1.2.3-59-g8ed1b From 796fbbbcbbf9b9830a3c1307978787cfe20d7f9f Mon Sep 17 00:00:00 2001 From: Chris Ye Date: Sat, 25 Apr 2020 10:58:45 -0700 Subject: Generate ACTION_CANCEL event when screen turned off by proximity sensor. When using PROXIMITY_SCREEN_OFF_WAKE_LOCK and screen was turned off by proximity sensor, input service should generate ACTION_CANCEL, same as display turned off by pressing power button. Bug: 154074380 Test: atest libgui_test Change-Id: I72a10c98adad4a236dbd445cefe5f37c4ec3fd81 Merged-In: I72a10c98adad4a236dbd445cefe5f37c4ec3fd81 --- include/input/DisplayViewport.h | 71 +++++++++++++++++++++-------------------- 1 file changed, 37 insertions(+), 34 deletions(-) (limited to 'include') diff --git a/include/input/DisplayViewport.h b/include/input/DisplayViewport.h index 610062697c..2427a075a1 100644 --- a/include/input/DisplayViewport.h +++ b/include/input/DisplayViewport.h @@ -74,36 +74,40 @@ struct DisplayViewport { int32_t physicalBottom; int32_t deviceWidth; int32_t deviceHeight; + bool isActive; std::string uniqueId; // The actual (hardware) port that the associated display is connected to. // Not all viewports will have this specified. std::optional physicalPort; ViewportType type; - DisplayViewport() : - displayId(ADISPLAY_ID_NONE), orientation(DISPLAY_ORIENTATION_0), - logicalLeft(0), logicalTop(0), logicalRight(0), logicalBottom(0), - physicalLeft(0), physicalTop(0), physicalRight(0), physicalBottom(0), - deviceWidth(0), deviceHeight(0), uniqueId(), physicalPort(std::nullopt), - type(ViewportType::VIEWPORT_INTERNAL) { - } + DisplayViewport() + : displayId(ADISPLAY_ID_NONE), + orientation(DISPLAY_ORIENTATION_0), + logicalLeft(0), + logicalTop(0), + logicalRight(0), + logicalBottom(0), + physicalLeft(0), + physicalTop(0), + physicalRight(0), + physicalBottom(0), + deviceWidth(0), + deviceHeight(0), + isActive(false), + uniqueId(), + physicalPort(std::nullopt), + type(ViewportType::VIEWPORT_INTERNAL) {} bool operator==(const DisplayViewport& other) const { - return displayId == other.displayId - && orientation == other.orientation - && logicalLeft == other.logicalLeft - && logicalTop == other.logicalTop - && logicalRight == other.logicalRight - && logicalBottom == other.logicalBottom - && physicalLeft == other.physicalLeft - && physicalTop == other.physicalTop - && physicalRight == other.physicalRight - && physicalBottom == other.physicalBottom - && deviceWidth == other.deviceWidth - && deviceHeight == other.deviceHeight - && uniqueId == other.uniqueId - && physicalPort == other.physicalPort - && type == other.type; + return displayId == other.displayId && orientation == other.orientation && + logicalLeft == other.logicalLeft && logicalTop == other.logicalTop && + logicalRight == other.logicalRight && logicalBottom == other.logicalBottom && + physicalLeft == other.physicalLeft && physicalTop == other.physicalTop && + physicalRight == other.physicalRight && physicalBottom == other.physicalBottom && + deviceWidth == other.deviceWidth && deviceHeight == other.deviceHeight && + isActive == other.isActive && uniqueId == other.uniqueId && + physicalPort == other.physicalPort && type == other.type; } bool operator!=(const DisplayViewport& other) const { @@ -127,6 +131,7 @@ struct DisplayViewport { physicalBottom = height; deviceWidth = width; deviceHeight = height; + isActive = false; uniqueId.clear(); physicalPort = std::nullopt; type = ViewportType::VIEWPORT_INTERNAL; @@ -134,18 +139,16 @@ struct DisplayViewport { std::string toString() const { return StringPrintf("Viewport %s: displayId=%d, uniqueId=%s, port=%s, orientation=%d, " - "logicalFrame=[%d, %d, %d, %d], " - "physicalFrame=[%d, %d, %d, %d], " - "deviceSize=[%d, %d]", - viewportTypeToString(type), displayId, - uniqueId.c_str(), - physicalPort ? StringPrintf("%" PRIu8, *physicalPort).c_str() : "", - orientation, - logicalLeft, logicalTop, - logicalRight, logicalBottom, - physicalLeft, physicalTop, - physicalRight, physicalBottom, - deviceWidth, deviceHeight); + "logicalFrame=[%d, %d, %d, %d], " + "physicalFrame=[%d, %d, %d, %d], " + "deviceSize=[%d, %d], " + "isActive=[%d]", + viewportTypeToString(type), displayId, uniqueId.c_str(), + physicalPort ? StringPrintf("%" PRIu8, *physicalPort).c_str() + : "", + orientation, logicalLeft, logicalTop, logicalRight, logicalBottom, + physicalLeft, physicalTop, physicalRight, physicalBottom, deviceWidth, + deviceHeight, isActive); } }; -- cgit v1.2.3-59-g8ed1b From ffaa2b163055c644dec7c2c681ed8b608e48f6b9 Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Tue, 26 May 2020 21:43:02 -0700 Subject: Add basic ANR test To reduce the delta of the upcoming ANR refactor, add a basic ANR test here. This will also help highlight the difference in behaviour from the current code to the new code. To cause an ANR today, the socket needs to be blocked, which means that we need to send ~ 50 events to the unresponsive window. These workarounds will be removed when the ANRs are refactored. Bug: 143459140 Test: adb shell -t /data/nativetest64/inputflinger_tests/inputflinger_tests --gtest_filter="*InputDispatcherSingleWindowAnr*" --gtest_repeat=1000 --gtest_break_on_failure Change-Id: I0a1b28c2785d03d8870691641e0f7c6b1ca3b85e Merged-In: I0a1b28c2785d03d8870691641e0f7c6b1ca3b85e --- include/input/InputApplication.h | 5 + include/input/InputWindow.h | 5 + services/inputflinger/dispatcher/Entry.cpp | 6 + services/inputflinger/dispatcher/Entry.h | 2 + .../inputflinger/dispatcher/InputDispatcher.cpp | 81 +++---- services/inputflinger/dispatcher/InputDispatcher.h | 6 +- .../inputflinger/tests/InputDispatcher_test.cpp | 240 ++++++++++++++++++--- 7 files changed, 274 insertions(+), 71 deletions(-) (limited to 'include') diff --git a/include/input/InputApplication.h b/include/input/InputApplication.h index 7f04611309..86de394a31 100644 --- a/include/input/InputApplication.h +++ b/include/input/InputApplication.h @@ -61,6 +61,11 @@ public: return mInfo.token ? mInfo.dispatchingTimeout : defaultValue; } + inline std::chrono::nanoseconds getDispatchingTimeout( + std::chrono::nanoseconds defaultValue) const { + return mInfo.token ? std::chrono::nanoseconds(mInfo.dispatchingTimeout) : defaultValue; + } + inline sp getApplicationToken() const { return mInfo.token; } diff --git a/include/input/InputWindow.h b/include/input/InputWindow.h index c5e56fd91f..2dac5b62a7 100644 --- a/include/input/InputWindow.h +++ b/include/input/InputWindow.h @@ -222,6 +222,11 @@ public: return mInfo.token ? mInfo.dispatchingTimeout : defaultValue; } + inline std::chrono::nanoseconds getDispatchingTimeout( + std::chrono::nanoseconds defaultValue) const { + return mInfo.token ? std::chrono::nanoseconds(mInfo.dispatchingTimeout) : defaultValue; + } + /** * Requests that the state of this object be updated to reflect * the most current available information about the application. diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp index a1eb0079df..21c8ae165d 100644 --- a/services/inputflinger/dispatcher/Entry.cpp +++ b/services/inputflinger/dispatcher/Entry.cpp @@ -102,6 +102,12 @@ EventEntry::~EventEntry() { releaseInjectionState(); } +std::string EventEntry::getDescription() const { + std::string result; + appendDescription(result); + return result; +} + void EventEntry::release() { refCount -= 1; if (refCount == 0) { diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h index ab481bd411..a135409365 100644 --- a/services/inputflinger/dispatcher/Entry.h +++ b/services/inputflinger/dispatcher/Entry.h @@ -83,6 +83,8 @@ struct EventEntry { virtual void appendDescription(std::string& msg) const = 0; + std::string getDescription() const; + protected: EventEntry(int32_t id, Type type, nsecs_t eventTime, uint32_t policyFlags); virtual ~EventEntry(); diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index a21d1ebd2d..e6e3347ae9 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -78,7 +78,7 @@ namespace android::inputdispatcher { // Default input dispatching timeout if there is no focused application or paused window // from which to determine an appropriate dispatching timeout. -constexpr nsecs_t DEFAULT_INPUT_DISPATCHING_TIMEOUT = 5000 * 1000000LL; // 5 sec +constexpr std::chrono::nanoseconds DEFAULT_INPUT_DISPATCHING_TIMEOUT = 5s; // Amount of time to allow for all pending events to be processed when an app switch // key is on the way. This is used to preempt input dispatch and drop input events @@ -1295,11 +1295,9 @@ int32_t InputDispatcher::handleTargetsNotReadyLocked( } } else { if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) { - if (DEBUG_FOCUS) { - ALOGD("Waiting for application to become ready for input: %s. Reason: %s", - getApplicationWindowLabel(applicationHandle, windowHandle).c_str(), reason); - } - nsecs_t timeout; + ALOGI("Waiting for application to become ready for input: %s. Reason: %s", + getApplicationWindowLabel(applicationHandle, windowHandle).c_str(), reason); + std::chrono::nanoseconds timeout; if (windowHandle != nullptr) { timeout = windowHandle->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT); } else if (applicationHandle != nullptr) { @@ -1311,7 +1309,7 @@ int32_t InputDispatcher::handleTargetsNotReadyLocked( mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY; mInputTargetWaitStartTime = currentTime; - mInputTargetWaitTimeoutTime = currentTime + timeout; + mInputTargetWaitTimeoutTime = currentTime + timeout.count(); mInputTargetWaitTimeoutExpired = false; mInputTargetWaitApplicationToken.clear(); @@ -1353,10 +1351,10 @@ void InputDispatcher::removeWindowByTokenLocked(const sp& token) { } void InputDispatcher::resumeAfterTargetsNotReadyTimeoutLocked( - nsecs_t newTimeout, const sp& inputConnectionToken) { - if (newTimeout > 0) { + nsecs_t timeoutExtension, const sp& inputConnectionToken) { + if (timeoutExtension > 0) { // Extend the timeout. - mInputTargetWaitTimeoutTime = now() + newTimeout; + mInputTargetWaitTimeoutTime = now() + timeoutExtension; } else { // Give up. mInputTargetWaitTimeoutExpired = true; @@ -4048,11 +4046,12 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { const int32_t displayId = it.first; const sp& applicationHandle = it.second; dump += StringPrintf(INDENT2 "displayId=%" PRId32 - ", name='%s', dispatchingTimeout=%0.3fms\n", + ", name='%s', dispatchingTimeout=%" PRId64 "ms\n", displayId, applicationHandle->getName().c_str(), - applicationHandle->getDispatchingTimeout( - DEFAULT_INPUT_DISPATCHING_TIMEOUT) / - 1000000.0); + ns2ms(applicationHandle + ->getDispatchingTimeout( + DEFAULT_INPUT_DISPATCHING_TIMEOUT) + .count())); } } else { dump += StringPrintf(INDENT "FocusedApplications: \n"); @@ -4132,9 +4131,10 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { windowInfo->windowXScale, windowInfo->windowYScale); dumpRegion(dump, windowInfo->touchableRegion); dump += StringPrintf(", inputFeatures=0x%08x", windowInfo->inputFeatures); - dump += StringPrintf(", ownerPid=%d, ownerUid=%d, dispatchingTimeout=%0.3fms\n", + dump += StringPrintf(", ownerPid=%d, ownerUid=%d, dispatchingTimeout=%" PRId64 + "ms\n", windowInfo->ownerPid, windowInfo->ownerUid, - windowInfo->dispatchingTimeout / 1000000.0); + ns2ms(windowInfo->dispatchingTimeout)); } } else { dump += INDENT2 "Windows: \n"; @@ -4167,7 +4167,7 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { for (EventEntry* entry : mRecentQueue) { dump += INDENT2; entry->appendDescription(dump); - dump += StringPrintf(", age=%0.1fms\n", (currentTime - entry->eventTime) * 0.000001f); + dump += StringPrintf(", age=%" PRId64 "ms\n", ns2ms(currentTime - entry->eventTime)); } } else { dump += INDENT "RecentQueue: \n"; @@ -4178,8 +4178,8 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { dump += INDENT "PendingEvent:\n"; dump += INDENT2; mPendingEvent->appendDescription(dump); - dump += StringPrintf(", age=%0.1fms\n", - (currentTime - mPendingEvent->eventTime) * 0.000001f); + dump += StringPrintf(", age=%" PRId64 "ms\n", + ns2ms(currentTime - mPendingEvent->eventTime)); } else { dump += INDENT "PendingEvent: \n"; } @@ -4190,7 +4190,7 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { for (EventEntry* entry : mInboundQueue) { dump += INDENT2; entry->appendDescription(dump); - dump += StringPrintf(", age=%0.1fms\n", (currentTime - entry->eventTime) * 0.000001f); + dump += StringPrintf(", age=%" PRId64 "ms\n", ns2ms(currentTime - entry->eventTime)); } } else { dump += INDENT "InboundQueue: \n"; @@ -4225,9 +4225,10 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { for (DispatchEntry* entry : connection->outboundQueue) { dump.append(INDENT4); entry->eventEntry->appendDescription(dump); - dump += StringPrintf(", targetFlags=0x%08x, resolvedAction=%d, age=%0.1fms\n", + dump += StringPrintf(", targetFlags=0x%08x, resolvedAction=%d, age=%" PRId64 + "ms\n", entry->targetFlags, entry->resolvedAction, - (currentTime - entry->eventEntry->eventTime) * 0.000001f); + ns2ms(currentTime - entry->eventEntry->eventTime)); } } else { dump += INDENT3 "OutboundQueue: \n"; @@ -4240,10 +4241,10 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { dump += INDENT4; entry->eventEntry->appendDescription(dump); dump += StringPrintf(", targetFlags=0x%08x, resolvedAction=%d, " - "age=%0.1fms, wait=%0.1fms\n", + "age=%" PRId64 "ms, wait=%" PRId64 "ms\n", entry->targetFlags, entry->resolvedAction, - (currentTime - entry->eventEntry->eventTime) * 0.000001f, - (currentTime - entry->deliveryTime) * 0.000001f); + ns2ms(currentTime - entry->eventEntry->eventTime), + ns2ms(currentTime - entry->deliveryTime)); } } else { dump += INDENT3 "WaitQueue: \n"; @@ -4254,16 +4255,16 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { } if (isAppSwitchPendingLocked()) { - dump += StringPrintf(INDENT "AppSwitch: pending, due in %0.1fms\n", - (mAppSwitchDueTime - now()) / 1000000.0); + dump += StringPrintf(INDENT "AppSwitch: pending, due in %" PRId64 "ms\n", + ns2ms(mAppSwitchDueTime - now())); } else { dump += INDENT "AppSwitch: not pending\n"; } dump += INDENT "Configuration:\n"; - dump += StringPrintf(INDENT2 "KeyRepeatDelay: %0.1fms\n", mConfig.keyRepeatDelay * 0.000001f); - dump += StringPrintf(INDENT2 "KeyRepeatTimeout: %0.1fms\n", - mConfig.keyRepeatTimeout * 0.000001f); + dump += StringPrintf(INDENT2 "KeyRepeatDelay: %" PRId64 "ms\n", ns2ms(mConfig.keyRepeatDelay)); + dump += StringPrintf(INDENT2 "KeyRepeatTimeout: %" PRId64 "ms\n", + ns2ms(mConfig.keyRepeatTimeout)); } void InputDispatcher::dumpMonitors(std::string& dump, const std::vector& monitors) { @@ -4365,8 +4366,7 @@ status_t InputDispatcher::unregisterInputChannelLocked(const sp& i return BAD_VALUE; } - [[maybe_unused]] const bool removed = removeByValue(mConnectionsByFd, connection); - ALOG_ASSERT(removed); + removeConnectionLocked(connection); mInputChannelsByToken.erase(inputChannel->getConnectionToken()); if (connection->monitor) { @@ -4468,7 +4468,7 @@ std::optional InputDispatcher::findGestureMonitorDisplayByTokenLocked( return std::nullopt; } -sp InputDispatcher::getConnectionLocked(const sp& inputConnectionToken) { +sp InputDispatcher::getConnectionLocked(const sp& inputConnectionToken) const { if (inputConnectionToken == nullptr) { return nullptr; } @@ -4483,6 +4483,10 @@ sp InputDispatcher::getConnectionLocked(const sp& inputConn return nullptr; } +void InputDispatcher::removeConnectionLocked(const sp& connection) { + removeByValue(mConnectionsByFd, connection); +} + void InputDispatcher::onDispatchCycleFinishedLocked(nsecs_t currentTime, const sp& connection, uint32_t seq, bool handled) { @@ -4587,12 +4591,12 @@ void InputDispatcher::doNotifyAnrLockedInterruptible(CommandEntry* commandEntry) commandEntry->inputChannel ? commandEntry->inputChannel->getConnectionToken() : nullptr; mLock.unlock(); - nsecs_t newTimeout = + const nsecs_t timeoutExtension = mPolicy->notifyAnr(commandEntry->inputApplicationHandle, token, commandEntry->reason); mLock.lock(); - resumeAfterTargetsNotReadyTimeoutLocked(newTimeout, token); + resumeAfterTargetsNotReadyTimeoutLocked(timeoutExtension, token); } void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible( @@ -4647,11 +4651,8 @@ void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(CommandEntry* c const nsecs_t eventDuration = finishTime - dispatchEntry->deliveryTime; if (eventDuration > SLOW_EVENT_PROCESSING_WARNING_TIMEOUT) { - std::string msg = - StringPrintf("Window '%s' spent %0.1fms processing the last input event: ", - connection->getWindowName().c_str(), eventDuration * 0.000001f); - dispatchEntry->eventEntry->appendDescription(msg); - ALOGI("%s", msg.c_str()); + ALOGI("%s spent %" PRId64 "ms processing %s", connection->getWindowName().c_str(), + ns2ms(eventDuration), dispatchEntry->eventEntry->getDescription().c_str()); } reportDispatchStatistics(std::chrono::nanoseconds(eventDuration), *connection, handled); diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index 89b5089e49..ff7be87609 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -197,6 +197,11 @@ private: // All registered connections mapped by channel file descriptor. std::unordered_map> mConnectionsByFd GUARDED_BY(mLock); + sp getConnectionLocked(const sp& inputConnectionToken) const + REQUIRES(mLock); + + void removeConnectionLocked(const sp& connection) REQUIRES(mLock); + struct IBinderHash { std::size_t operator()(const sp& b) const { return std::hash{}(b.get()); @@ -209,7 +214,6 @@ private: std::optional findGestureMonitorDisplayByTokenLocked(const sp& token) REQUIRES(mLock); - sp getConnectionLocked(const sp& inputConnectionToken) REQUIRES(mLock); // Input channels that will receive a copy of all input events sent to the provided display. std::unordered_map> mGlobalMonitorsByDisplay GUARDED_BY(mLock); diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index f33cc65c2a..13e835427f 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -17,12 +17,14 @@ #include "../dispatcher/InputDispatcher.h" #include +#include #include #include #include #include #include +#include #include #include @@ -119,6 +121,33 @@ public: << "Expected onPointerDownOutsideFocus to not have been called"; } + // This function must be called soon after the expected ANR timer starts, + // because we are also checking how much time has passed. + void assertNotifyAnrWasCalled(std::chrono::nanoseconds timeout, + const sp& expectedApplication, + const sp& expectedToken) { + const std::chrono::time_point start = std::chrono::steady_clock::now(); + std::unique_lock lock(mLock); + std::chrono::duration timeToWait = timeout + 100ms; // provide some slack + android::base::ScopedLockAssertion assumeLocked(mLock); + + // If there is an ANR, Dispatcher won't be idle because there are still events + // in the waitQueue that we need to check on. So we can't wait for dispatcher to be idle + // before checking if ANR was called. + // Since dispatcher is not guaranteed to call notifyAnr right away, we need to provide + // it some time to act. 100ms seems reasonable. + mNotifyAnr.wait_for(lock, timeToWait, + [this]() REQUIRES(mLock) { return mNotifyAnrWasCalled; }); + const std::chrono::duration waited = std::chrono::steady_clock::now() - start; + ASSERT_TRUE(mNotifyAnrWasCalled); + // Ensure that the ANR didn't get raised too early. We can't be too strict here because + // the dispatcher started counting before this function was called + ASSERT_TRUE(timeout - 100ms < waited); // check (waited < timeout + 100ms) done by wait_for + mNotifyAnrWasCalled = false; + ASSERT_EQ(expectedApplication, mLastAnrApplication); + ASSERT_EQ(expectedToken, mLastAnrWindowToken); + } + void setKeyRepeatConfiguration(nsecs_t timeout, nsecs_t delay) { mConfig.keyRepeatTimeout = timeout; mConfig.keyRepeatDelay = delay; @@ -131,14 +160,26 @@ private: sp mOnPointerDownToken GUARDED_BY(mLock); std::optional mLastNotifySwitch GUARDED_BY(mLock); + // ANR handling + bool mNotifyAnrWasCalled GUARDED_BY(mLock) = false; + sp mLastAnrApplication GUARDED_BY(mLock); + sp mLastAnrWindowToken GUARDED_BY(mLock); + std::condition_variable mNotifyAnr; + std::chrono::nanoseconds mAnrTimeout = 0ms; + virtual void notifyConfigurationChanged(nsecs_t when) override { std::scoped_lock lock(mLock); mConfigurationChangedTime = when; } - virtual nsecs_t notifyAnr(const sp&, const sp&, - const std::string&) override { - return 0; + virtual nsecs_t notifyAnr(const sp& application, + const sp& windowToken, const std::string&) override { + std::scoped_lock lock(mLock); + mLastAnrApplication = application; + mLastAnrWindowToken = windowToken; + mNotifyAnrWasCalled = true; + mNotifyAnr.notify_all(); + return mAnrTimeout.count(); } virtual void notifyInputChannelBroken(const sp&) override {} @@ -309,6 +350,20 @@ protected: mFakePolicy.clear(); mDispatcher.clear(); } + + /** + * Used for debugging when writing the test + */ + void dumpDispatcherState() { + std::string dump; + mDispatcher->dump(dump); + std::stringstream ss(dump); + std::string to; + + while (std::getline(ss, to, '\n')) { + ALOGE("%s", to.c_str()); + } + } }; @@ -502,13 +557,20 @@ static constexpr std::chrono::nanoseconds DISPATCHING_TIMEOUT = 5s; class FakeApplicationHandle : public InputApplicationHandle { public: - FakeApplicationHandle() {} + FakeApplicationHandle() { + mInfo.name = "Fake Application"; + mInfo.token = new BBinder(); + mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT.count(); + } virtual ~FakeApplicationHandle() {} virtual bool updateInfo() override { - mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT.count(); return true; } + + void setDispatchingTimeout(std::chrono::nanoseconds timeout) { + mInfo.dispatchingTimeout = timeout.count(); + } }; class FakeInputReceiver { @@ -519,6 +581,20 @@ public: } InputEvent* consume() { + InputEvent* event; + std::optional consumeSeq = receiveEvent(&event); + if (!consumeSeq) { + return nullptr; + } + finishEvent(*consumeSeq); + return event; + } + + /** + * Receive an event without acknowledging it. + * Return the sequence number that could later be used to send finished signal. + */ + std::optional receiveEvent(InputEvent** outEvent = nullptr) { uint32_t consumeSeq; InputEvent* event; @@ -535,23 +611,29 @@ public: if (status == WOULD_BLOCK) { // Just means there's no event available. - return nullptr; + return std::nullopt; } if (status != OK) { ADD_FAILURE() << mName.c_str() << ": consumer consume should return OK."; - return nullptr; + return std::nullopt; } if (event == nullptr) { ADD_FAILURE() << "Consumed correctly, but received NULL event from consumer"; - return nullptr; + return std::nullopt; } - - status = mConsumer->sendFinishedSignal(consumeSeq, true); - if (status != OK) { - ADD_FAILURE() << mName.c_str() << ": consumer sendFinishedSignal should return OK."; + if (outEvent != nullptr) { + *outEvent = event; } - return event; + return consumeSeq; + } + + /** + * To be used together with "receiveEvent" to complete the consumption of an event. + */ + void finishEvent(uint32_t consumeSeq) { + const status_t status = mConsumer->sendFinishedSignal(consumeSeq, true); + ASSERT_EQ(OK, status) << mName.c_str() << ": consumer sendFinishedSignal should return OK."; } void consumeEvent(int32_t expectedEventType, int32_t expectedAction, int32_t expectedDisplayId, @@ -668,6 +750,10 @@ public: void setFocus(bool hasFocus) { mInfo.hasFocus = hasFocus; } + void setDispatchingTimeout(std::chrono::nanoseconds timeout) { + mInfo.dispatchingTimeout = timeout.count(); + } + void setFrame(const Rect& frame) { mInfo.frameLeft = frame.left; mInfo.frameTop = frame.top; @@ -740,6 +826,19 @@ public: expectedFlags); } + std::optional receiveEvent() { + if (mInputReceiver == nullptr) { + ADD_FAILURE() << "Invalid receive event on window with no receiver"; + return std::nullopt; + } + return mInputReceiver->receiveEvent(); + } + + void finishEvent(uint32_t sequenceNum) { + ASSERT_NE(mInputReceiver, nullptr) << "Invalid receive event on window with no receiver"; + mInputReceiver->finishEvent(sequenceNum); + } + InputEvent* consume() { if (mInputReceiver == nullptr) { return nullptr; @@ -765,16 +864,15 @@ private: std::atomic FakeWindowHandle::sId{1}; -static int32_t injectKeyDown(const sp& dispatcher, - int32_t displayId = ADISPLAY_ID_NONE) { +static int32_t injectKey(const sp& dispatcher, int32_t action, int32_t repeatCount, + int32_t displayId = ADISPLAY_ID_NONE) { KeyEvent event; nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); // Define a valid key down event. event.initialize(InputEvent::nextId(), DEVICE_ID, AINPUT_SOURCE_KEYBOARD, displayId, - INVALID_HMAC, AKEY_EVENT_ACTION_DOWN, /* flags */ 0, AKEYCODE_A, KEY_A, - AMETA_NONE, - /* repeatCount */ 0, currentTime, currentTime); + INVALID_HMAC, action, /* flags */ 0, AKEYCODE_A, KEY_A, AMETA_NONE, + repeatCount, currentTime, currentTime); // Inject event until dispatch out. return dispatcher->injectInputEvent( @@ -783,10 +881,16 @@ static int32_t injectKeyDown(const sp& dispatcher, INJECT_EVENT_TIMEOUT, POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER); } -static int32_t injectMotionEvent(const sp& dispatcher, int32_t action, - int32_t source, int32_t displayId, int32_t x, int32_t y, - int32_t xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION, - int32_t yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION) { +static int32_t injectKeyDown(const sp& dispatcher, + int32_t displayId = ADISPLAY_ID_NONE) { + return injectKey(dispatcher, AKEY_EVENT_ACTION_DOWN, /* repeatCount */ 0, displayId); +} + +static int32_t injectMotionEvent( + const sp& dispatcher, int32_t action, int32_t source, int32_t displayId, + const PointF& position, + const PointF& cursorPosition = {AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION}) { MotionEvent event; PointerProperties pointerProperties[1]; PointerCoords pointerCoords[1]; @@ -796,8 +900,8 @@ static int32_t injectMotionEvent(const sp& dispatcher, int32_t pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; pointerCoords[0].clear(); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, position.x); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, position.y); nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); // Define a valid motion down event. @@ -806,7 +910,7 @@ static int32_t injectMotionEvent(const sp& dispatcher, int32_t /* flags */ 0, /* edgeFlags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE, /* xScale */ 1, /* yScale */ 1, /* xOffset */ 0, /* yOffset */ 0, - /* xPrecision */ 0, /* yPrecision */ 0, xCursorPosition, yCursorPosition, + /* xPrecision */ 0, /* yPrecision */ 0, cursorPosition.x, cursorPosition.y, currentTime, currentTime, /*pointerCount*/ 1, pointerProperties, pointerCoords); @@ -819,14 +923,12 @@ static int32_t injectMotionEvent(const sp& dispatcher, int32_t static int32_t injectMotionDown(const sp& dispatcher, int32_t source, int32_t displayId, const PointF& location = {100, 200}) { - return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_DOWN, source, displayId, location.x, - location.y); + return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_DOWN, source, displayId, location); } static int32_t injectMotionUp(const sp& dispatcher, int32_t source, int32_t displayId, const PointF& location = {100, 200}) { - return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_UP, source, displayId, location.x, - location.y); + return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_UP, source, displayId, location); } static NotifyKeyArgs generateKeyArgs(int32_t action, int32_t displayId = ADISPLAY_ID_NONE) { @@ -1051,7 +1153,7 @@ TEST_F(InputDispatcherTest, DispatchMouseEventsUnderCursor) { // left window. This event should be dispatched to the left window. ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE, - ADISPLAY_ID_DEFAULT, 610, 400, 599, 400)); + ADISPLAY_ID_DEFAULT, {610, 400}, {599, 400})); windowLeft->consumeMotionDown(ADISPLAY_ID_DEFAULT); windowRight->assertNoEvents(); } @@ -2185,4 +2287,82 @@ TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleWindowsFirstTouchWithSc consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_MOVE, expectedPoints); } +class InputDispatcherSingleWindowAnr : public InputDispatcherTest { + virtual void SetUp() override { + InputDispatcherTest::SetUp(); + + mApplication = new FakeApplicationHandle(); + mApplication->setDispatchingTimeout(20ms); + mWindow = + new FakeWindowHandle(mApplication, mDispatcher, "TestWindow", ADISPLAY_ID_DEFAULT); + mWindow->setFrame(Rect(0, 0, 30, 30)); + mWindow->setDispatchingTimeout(10ms); + mWindow->setFocus(true); + // Adding FLAG_NOT_TOUCH_MODAL to ensure taps outside this window are not sent to this + // window. + mWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL); + + // Set focused application. + mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApplication); + + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}}); + mWindow->consumeFocusEvent(true); + } + + virtual void TearDown() override { + InputDispatcherTest::TearDown(); + mWindow.clear(); + } + +protected: + sp mApplication; + sp mWindow; + static constexpr PointF WINDOW_LOCATION = {20, 20}; + + void tapOnWindow() { + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + WINDOW_LOCATION)); + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, + injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + WINDOW_LOCATION)); + } +}; + +// Send an event to the app and have the app not respond right away. +// Make sure that ANR is raised +TEST_F(InputDispatcherSingleWindowAnr, OnPointerDown_BasicAnr) { + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + WINDOW_LOCATION)); + + // Also, overwhelm the socket to make sure ANR starts + for (size_t i = 0; i < 100; i++) { + injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, {WINDOW_LOCATION.x, WINDOW_LOCATION.y + i}); + } + + std::optional sequenceNum = mWindow->receiveEvent(); // ACTION_DOWN + ASSERT_TRUE(sequenceNum); + const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT); + mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, mWindow->getToken()); + ASSERT_TRUE(mDispatcher->waitForIdle()); +} + +// Send a key to the app and have the app not respond right away. +TEST_F(InputDispatcherSingleWindowAnr, OnKeyDown_BasicAnr) { + // Inject a key, and don't respond - expect that ANR is called. + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher)); + std::optional sequenceNum = mWindow->receiveEvent(); + ASSERT_TRUE(sequenceNum); + + // Start ANR process by sending a 2nd key, which would trigger the check for whether + // waitQueue is empty + injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, /* repeatCount */ 1); + + const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT); + mFakePolicy->assertNotifyAnrWasCalled(timeout, mApplication, mWindow->getToken()); + ASSERT_TRUE(mDispatcher->waitForIdle()); +} + } // namespace android::inputdispatcher -- cgit v1.2.3-59-g8ed1b From 7be0e2def2d9cff2436511696838fcf0734b4da1 Mon Sep 17 00:00:00 2001 From: Elliott Hughes Date: Tue, 2 Jun 2020 13:05:04 -0700 Subject: API level 30 cleanup. Make sure that we have the __INTRODUCED_IN()s and -- more importantly -- the API levels recorded in the documentation. Bug: https://github.com/android/ndk/issues/1271 Test: treehugger Change-Id: I74752f19f165fd5c56b2dfd783298c2da1a926d6 --- include/android/bitmap.h | 8 ++++++++ include/android/choreographer.h | 10 ++++++++-- include/android/imagedecoder.h | 38 ++++++++++++++++++++++++++++++++++++++ include/android/thermal.h | 30 +++++++++++++++++++----------- 4 files changed, 73 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/android/bitmap.h b/include/android/bitmap.h index 2631b144af..f19539913e 100644 --- a/include/android/bitmap.h +++ b/include/android/bitmap.h @@ -125,6 +125,8 @@ int AndroidBitmap_getInfo(JNIEnv* env, jobject jbitmap, * Note that {@link ADataSpace} only exposes a few values. This may return * {@link ADATASPACE_UNKNOWN}, even for Named ColorSpaces, if they have no * corresponding ADataSpace. + * + * Available since API level 30. */ int32_t AndroidBitmap_getDataSpace(JNIEnv* env, jobject jbitmap) __INTRODUCED_IN(30); @@ -189,6 +191,8 @@ enum AndroidBitmapCompressFormat { /** * User-defined function for writing the output of compression. * + * Available since API level 30. + * * @param userContext Pointer to user-defined data passed to * {@link AndroidBitmap_compress}. * @param data Compressed data of |size| bytes to write. @@ -202,6 +206,8 @@ typedef bool (*AndroidBitmap_CompressWriteFunc)(void* userContext, /** * Compress |pixels| as described by |info|. * + * Available since API level 30. + * * @param info Description of the pixels to compress. * @param dataspace {@link ADataSpace} describing the color space of the * pixels. @@ -234,6 +240,8 @@ typedef struct AHardwareBuffer AHardwareBuffer; * * Client must not modify it while a Bitmap is wrapping it. * + * Available since API level 30. + * * @param bitmap Handle to an android.graphics.Bitmap. * @param outBuffer On success, is set to a pointer to the * {@link AHardwareBuffer} associated with bitmap. This acquires diff --git a/include/android/choreographer.h b/include/android/choreographer.h index c1c4a72cd3..bdf11e42ca 100644 --- a/include/android/choreographer.h +++ b/include/android/choreographer.h @@ -129,9 +129,12 @@ void AChoreographer_postFrameCallbackDelayed64(AChoreographer* choreographer, * * This api is thread-safe. Any thread is allowed to register a new refresh * rate callback for the choreographer instance. + * + * Available since API level 30. */ void AChoreographer_registerRefreshRateCallback(AChoreographer* choreographer, - AChoreographer_refreshRateCallback, void* data); + AChoreographer_refreshRateCallback, void* data) + __INTRODUCED_IN(30); /** * Unregisters a callback to be run when the display refresh rate changes, along @@ -144,9 +147,12 @@ void AChoreographer_registerRefreshRateCallback(AChoreographer* choreographer, * callback and associated data pointer are unregistered, then there is a * guarantee that when the unregistration completes that that callback will not * be run with the data pointer passed. + * + * Available since API level 30. */ void AChoreographer_unregisterRefreshRateCallback(AChoreographer* choreographer, - AChoreographer_refreshRateCallback, void* data); + AChoreographer_refreshRateCallback, void* data) + __INTRODUCED_IN(30); #endif /* __ANDROID_API__ >= 30 */ __END_DECLS diff --git a/include/android/imagedecoder.h b/include/android/imagedecoder.h index 3a87da0fee..d7e6e4118f 100644 --- a/include/android/imagedecoder.h +++ b/include/android/imagedecoder.h @@ -133,6 +133,8 @@ typedef struct AImageDecoder AImageDecoder; /** * Create a new {@link AImageDecoder} from an {@link AAsset}. * + * Available since API level 30. + * * @param asset {@link AAsset} containing encoded image data. Client is still * responsible for calling {@link AAsset_close} on it, which may be * done after deleting the returned {@link AImageDecoder}. @@ -162,6 +164,8 @@ int AImageDecoder_createFromAAsset(struct AAsset* asset, AImageDecoder** outDeco /** * Create a new {@link AImageDecoder} from a file descriptor. * + * Available since API level 30. + * * @param fd Seekable, readable, open file descriptor for encoded data. * Client is still responsible for closing it, which may be done * after deleting the returned {@link AImageDecoder}. @@ -190,6 +194,8 @@ int AImageDecoder_createFromFd(int fd, AImageDecoder** outDecoder) __INTRODUCED_ /** * Create a new AImageDecoder from a buffer. * + * Available since API level 30. + * * @param buffer Pointer to encoded data. Must be valid for the entire time * the {@link AImageDecoder} is used. * @param length Byte length of buffer. @@ -217,12 +223,16 @@ int AImageDecoder_createFromBuffer(const void* buffer, size_t length, /** * Delete the AImageDecoder. + * + * Available since API level 30. */ void AImageDecoder_delete(AImageDecoder* decoder) __INTRODUCED_IN(30); /** * Choose the desired output format. * + * Available since API level 30. + * * @param format {@link AndroidBitmapFormat} to use for the output. * @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success or a value * indicating the reason for the failure. On failure, the @@ -247,6 +257,8 @@ int AImageDecoder_setAndroidBitmapFormat(AImageDecoder*, * Pass true to this method to leave them unpremultiplied. This has no effect on an * opaque image. * + * Available since API level 30. + * * @param unpremultipliedRequired Pass true to leave the pixels unpremultiplied. * @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success or a value * indicating the reason for the failure. @@ -267,6 +279,8 @@ int AImageDecoder_setUnpremultipliedRequired(AImageDecoder*, * Ignored by {@link ANDROID_BITMAP_FORMAT_A_8}, which does not support * an {@link ADataSpace}. * + * Available since API level 30. + * * @param dataspace The {@link ADataSpace} to decode into. An ADataSpace * specifies how to interpret the colors. By default, * AImageDecoder will decode into the ADataSpace specified by @@ -292,6 +306,8 @@ int AImageDecoder_setDataSpace(AImageDecoder*, int32_t dataspace) __INTRODUCED_I * specified by width and height, and the output image will be the size of the * crop rect. * + * Available since API level 30. + * * @param width Width of the output (prior to cropping). * This will affect future calls to * {@link AImageDecoder_getMinimumStride}, which will now return @@ -319,6 +335,8 @@ int AImageDecoder_setTargetSize(AImageDecoder*, int32_t width, int32_t height) _ * others. This computes the most efficient target size to use to reach a * particular sampleSize. * + * Available since API level 30. + * * @param sampleSize A subsampling rate of the original image. Must be greater * than or equal to 1. A sampleSize of 2 means to skip every * other pixel/line, resulting in a width and height that are @@ -344,6 +362,8 @@ int AImageDecoder_computeSampledSize(const AImageDecoder*, int sampleSize, * the specified {@link ARect}. Clients will only need to allocate enough memory * for the cropped ARect. * + * Available since API level 30. + * * @param crop Rectangle describing a crop of the decode. It must be contained inside of * the (possibly scaled, by {@link AImageDecoder_setTargetSize}) * image dimensions. This will affect future calls to @@ -376,6 +396,8 @@ typedef struct AImageDecoderHeaderInfo AImageDecoderHeaderInfo; * * This is owned by the {@link AImageDecoder} and will be destroyed when the * AImageDecoder is destroyed via {@link AImageDecoder_delete}. + * + * Available since API level 30. */ const AImageDecoderHeaderInfo* AImageDecoder_getHeaderInfo( const AImageDecoder*) __INTRODUCED_IN(30); @@ -385,6 +407,8 @@ const AImageDecoderHeaderInfo* AImageDecoder_getHeaderInfo( * pixel width of the output, unless {@link AImageDecoder_setTargetSize} is * used to choose a different size or {@link AImageDecoder_setCrop} is used to * set a crop rect. + * + * Available since API level 30. */ int32_t AImageDecoderHeaderInfo_getWidth(const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30); @@ -393,12 +417,16 @@ int32_t AImageDecoderHeaderInfo_getWidth(const AImageDecoderHeaderInfo*) __INTRO * pixel height of the output, unless {@link AImageDecoder_setTargetSize} is * used to choose a different size or {@link AImageDecoder_setCrop} is used to * set a crop rect. + * + * Available since API level 30. */ int32_t AImageDecoderHeaderInfo_getHeight(const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30); /** * Report the mimeType of the encoded image. * + * Available since API level 30. + * * @return a string literal describing the mime type. */ const char* AImageDecoderHeaderInfo_getMimeType( @@ -409,6 +437,8 @@ const char* AImageDecoderHeaderInfo_getMimeType( * by default. {@link AImageDecoder} will try to choose one that is sensible * for the image and the system. Note that this does not indicate the * encoded format of the image. + * + * Available since API level 30. */ int32_t AImageDecoderHeaderInfo_getAndroidBitmapFormat( const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30); @@ -419,6 +449,8 @@ int32_t AImageDecoderHeaderInfo_getAndroidBitmapFormat( * {@link ANDROID_BITMAP_FLAGS_ALPHA_OPAQUE}. If the image may contain alpha, * this returns {@link ANDROID_BITMAP_FLAGS_ALPHA_PREMUL}, because * {@link AImageDecoder_decodeImage} will premultiply pixels by default. + * + * Available since API level 30. */ int AImageDecoderHeaderInfo_getAlphaFlags( const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30); @@ -429,6 +461,8 @@ int AImageDecoderHeaderInfo_getAlphaFlags( * By default, {@link AImageDecoder_decodeImage} will not do any color * conversion. * + * Available since API level 30. + * * @return The {@link ADataSpace} representing the way the colors * are encoded (or {@link ADATASPACE_UNKNOWN} if there is not a * corresponding ADataSpace). This specifies how to interpret the colors @@ -452,12 +486,16 @@ int32_t AImageDecoderHeaderInfo_getDataSpace( * * If the output is scaled (via {@link AImageDecoder_setTargetSize}) and/or * cropped (via {@link AImageDecoder_setCrop}), this takes those into account. + * + * Available since API level 30. */ size_t AImageDecoder_getMinimumStride(AImageDecoder*) __INTRODUCED_IN(30); /** * Decode the image into pixels, using the settings of the {@link AImageDecoder}. * + * Available since API level 30. + * * @param decoder Opaque object representing the decoder. * @param pixels On success, will be filled with the result * of the decode. Must be large enough to hold |size| bytes. diff --git a/include/android/thermal.h b/include/android/thermal.h index 3247fa167b..83582d6791 100644 --- a/include/android/thermal.h +++ b/include/android/thermal.h @@ -60,8 +60,6 @@ extern "C" { #endif -#if __ANDROID_API__ >= 30 - enum AThermalStatus { /** Error in thermal status. */ ATHERMAL_STATUS_ERROR = -1, @@ -111,36 +109,45 @@ typedef struct AThermalManager AThermalManager; */ typedef void (*AThermal_StatusCallback)(void *data, AThermalStatus status); +#if __ANDROID_API__ >= 30 + /** * Acquire an instance of the thermal manager. This must be freed using * {@link AThermal_releaseManager}. * + * Available since API level 30. + * * @return manager instance on success, nullptr on failure. - */ -AThermalManager* AThermal_acquireManager(); + */ +AThermalManager* AThermal_acquireManager() __INTRODUCED_IN(30); /** * Release the thermal manager pointer acquired via * {@link AThermal_acquireManager}. * - * @param manager The manager to be released. + * Available since API level 30. * + * @param manager The manager to be released. */ -void AThermal_releaseManager(AThermalManager *manager); +void AThermal_releaseManager(AThermalManager *manager) __INTRODUCED_IN(30); /** * Gets the current thermal status. * + * Available since API level 30. + * * @param manager The manager instance to use to query the thermal status. * Acquired via {@link AThermal_acquireManager}. * * @return current thermal status, ATHERMAL_STATUS_ERROR on failure. -*/ -AThermalStatus AThermal_getCurrentThermalStatus(AThermalManager *manager); + */ +AThermalStatus AThermal_getCurrentThermalStatus(AThermalManager *manager) __INTRODUCED_IN(30); /** * Register the thermal status listener for thermal status change. * + * Available since API level 30. + * * @param manager The manager instance to use to register. * Acquired via {@link AThermal_acquireManager}. * @param callback The callback function to be called when thermal status updated. @@ -152,11 +159,13 @@ AThermalStatus AThermal_getCurrentThermalStatus(AThermalManager *manager); * EPIPE if communication with the system service has failed. */ int AThermal_registerThermalStatusListener(AThermalManager *manager, - AThermal_StatusCallback callback, void *data); + AThermal_StatusCallback callback, void *data) __INTRODUCED_IN(30); /** * Unregister the thermal status listener previously resgistered. * + * Available since API level 30. + * * @param manager The manager instance to use to unregister. * Acquired via {@link AThermal_acquireManager}. * @param callback The callback function to be called when thermal status updated. @@ -168,8 +177,7 @@ int AThermal_registerThermalStatusListener(AThermalManager *manager, * EPIPE if communication with the system service has failed. */ int AThermal_unregisterThermalStatusListener(AThermalManager *manager, - AThermal_StatusCallback callback, void *data); - + AThermal_StatusCallback callback, void *data) __INTRODUCED_IN(30); #endif // __ANDROID_API__ >= 30 -- cgit v1.2.3-59-g8ed1b From 7a7321260b862b44db1b56e89f484fb7144bb921 Mon Sep 17 00:00:00 2001 From: Nick Cook Date: Wed, 3 Jun 2020 22:55:05 +0000 Subject: docs: Fix ref doc links. See generated output in cl/314618386. Bug: 148938276 Test: m ndk Change-Id: I7374793e72e1f836c2ba1eeeebe09ccdcf58ce4b --- include/android/configuration.h | 122 ++++++++++++++++++++-------------------- 1 file changed, 61 insertions(+), 61 deletions(-) (limited to 'include') diff --git a/include/android/configuration.h b/include/android/configuration.h index 05f43407fb..ccf3e59066 100644 --- a/include/android/configuration.h +++ b/include/android/configuration.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 The Android Open Source Project + * Copyright (C) 2010 The Android Open Source Project. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -58,13 +58,13 @@ enum { ACONFIGURATION_ORIENTATION_ANY = 0x0000, /** * Orientation: value corresponding to the - * port + * port * resource qualifier. */ ACONFIGURATION_ORIENTATION_PORT = 0x0001, /** * Orientation: value corresponding to the - * land + * land * resource qualifier. */ ACONFIGURATION_ORIENTATION_LAND = 0x0002, @@ -75,7 +75,7 @@ enum { ACONFIGURATION_TOUCHSCREEN_ANY = 0x0000, /** * Touchscreen: value corresponding to the - * notouch + * notouch * resource qualifier. */ ACONFIGURATION_TOUCHSCREEN_NOTOUCH = 0x0001, @@ -83,7 +83,7 @@ enum { ACONFIGURATION_TOUCHSCREEN_STYLUS = 0x0002, /** * Touchscreen: value corresponding to the - * finger + * finger * resource qualifier. */ ACONFIGURATION_TOUCHSCREEN_FINGER = 0x0003, @@ -92,43 +92,43 @@ enum { ACONFIGURATION_DENSITY_DEFAULT = 0, /** * Density: value corresponding to the - * ldpi + * ldpi * resource qualifier. */ ACONFIGURATION_DENSITY_LOW = 120, /** * Density: value corresponding to the - * mdpi + * mdpi * resource qualifier. */ ACONFIGURATION_DENSITY_MEDIUM = 160, /** * Density: value corresponding to the - * tvdpi + * tvdpi * resource qualifier. */ ACONFIGURATION_DENSITY_TV = 213, /** * Density: value corresponding to the - * hdpi + * hdpi * resource qualifier. */ ACONFIGURATION_DENSITY_HIGH = 240, /** * Density: value corresponding to the - * xhdpi + * xhdpi * resource qualifier. */ ACONFIGURATION_DENSITY_XHIGH = 320, /** * Density: value corresponding to the - * xxhdpi + * xxhdpi * resource qualifier. */ ACONFIGURATION_DENSITY_XXHIGH = 480, /** * Density: value corresponding to the - * xxxhdpi + * xxxhdpi * resource qualifier. */ ACONFIGURATION_DENSITY_XXXHIGH = 640, @@ -141,19 +141,19 @@ enum { ACONFIGURATION_KEYBOARD_ANY = 0x0000, /** * Keyboard: value corresponding to the - * nokeys + * nokeys * resource qualifier. */ ACONFIGURATION_KEYBOARD_NOKEYS = 0x0001, /** * Keyboard: value corresponding to the - * qwerty + * qwerty * resource qualifier. */ ACONFIGURATION_KEYBOARD_QWERTY = 0x0002, /** * Keyboard: value corresponding to the - * 12key + * 12key * resource qualifier. */ ACONFIGURATION_KEYBOARD_12KEY = 0x0003, @@ -162,25 +162,25 @@ enum { ACONFIGURATION_NAVIGATION_ANY = 0x0000, /** * Navigation: value corresponding to the - * nonav + * nonav * resource qualifier. */ ACONFIGURATION_NAVIGATION_NONAV = 0x0001, /** * Navigation: value corresponding to the - * dpad + * dpad * resource qualifier. */ ACONFIGURATION_NAVIGATION_DPAD = 0x0002, /** * Navigation: value corresponding to the - * trackball + * trackball * resource qualifier. */ ACONFIGURATION_NAVIGATION_TRACKBALL = 0x0003, /** * Navigation: value corresponding to the - * wheel + * wheel * resource qualifier. */ ACONFIGURATION_NAVIGATION_WHEEL = 0x0004, @@ -189,19 +189,19 @@ enum { ACONFIGURATION_KEYSHIDDEN_ANY = 0x0000, /** * Keyboard availability: value corresponding to the - * keysexposed + * keysexposed * resource qualifier. */ ACONFIGURATION_KEYSHIDDEN_NO = 0x0001, /** * Keyboard availability: value corresponding to the - * keyshidden + * keyshidden * resource qualifier. */ ACONFIGURATION_KEYSHIDDEN_YES = 0x0002, /** * Keyboard availability: value corresponding to the - * keyssoft + * keyssoft * resource qualifier. */ ACONFIGURATION_KEYSHIDDEN_SOFT = 0x0003, @@ -210,13 +210,13 @@ enum { ACONFIGURATION_NAVHIDDEN_ANY = 0x0000, /** * Navigation availability: value corresponding to the - * navexposed + * navexposed * resource qualifier. */ ACONFIGURATION_NAVHIDDEN_NO = 0x0001, /** * Navigation availability: value corresponding to the - * navhidden + * navhidden * resource qualifier. */ ACONFIGURATION_NAVHIDDEN_YES = 0x0002, @@ -226,28 +226,28 @@ enum { /** * Screen size: value indicating the screen is at least * approximately 320x426 dp units, corresponding to the - * small + * small * resource qualifier. */ ACONFIGURATION_SCREENSIZE_SMALL = 0x01, /** * Screen size: value indicating the screen is at least * approximately 320x470 dp units, corresponding to the - * normal + * normal * resource qualifier. */ ACONFIGURATION_SCREENSIZE_NORMAL = 0x02, /** * Screen size: value indicating the screen is at least * approximately 480x640 dp units, corresponding to the - * large + * large * resource qualifier. */ ACONFIGURATION_SCREENSIZE_LARGE = 0x03, /** * Screen size: value indicating the screen is at least * approximately 720x960 dp units, corresponding to the - * xlarge + * xlarge * resource qualifier. */ ACONFIGURATION_SCREENSIZE_XLARGE = 0x04, @@ -256,13 +256,13 @@ enum { ACONFIGURATION_SCREENLONG_ANY = 0x00, /** * Screen layout: value that corresponds to the - * notlong + * notlong * resource qualifier. */ ACONFIGURATION_SCREENLONG_NO = 0x1, /** * Screen layout: value that corresponds to the - * long + * long * resource qualifier. */ ACONFIGURATION_SCREENLONG_YES = 0x2, @@ -275,13 +275,13 @@ enum { ACONFIGURATION_WIDE_COLOR_GAMUT_ANY = 0x00, /** * Wide color gamut: value that corresponds to - * no + * no * nowidecg resource qualifier specified. */ ACONFIGURATION_WIDE_COLOR_GAMUT_NO = 0x1, /** * Wide color gamut: value that corresponds to - * + * * widecg resource qualifier specified. */ ACONFIGURATION_WIDE_COLOR_GAMUT_YES = 0x2, @@ -290,13 +290,13 @@ enum { ACONFIGURATION_HDR_ANY = 0x00, /** * HDR: value that corresponds to - * + * * lowdr resource qualifier specified. */ ACONFIGURATION_HDR_NO = 0x1, /** * HDR: value that corresponds to - * + * * highdr resource qualifier specified. */ ACONFIGURATION_HDR_YES = 0x2, @@ -305,38 +305,38 @@ enum { ACONFIGURATION_UI_MODE_TYPE_ANY = 0x00, /** * UI mode: value that corresponds to - * no + * no * UI mode type resource qualifier specified. */ ACONFIGURATION_UI_MODE_TYPE_NORMAL = 0x01, /** * UI mode: value that corresponds to - * desk resource qualifier specified. + * desk resource qualifier specified. */ ACONFIGURATION_UI_MODE_TYPE_DESK = 0x02, /** * UI mode: value that corresponds to - * car resource qualifier specified. + * car resource qualifier specified. */ ACONFIGURATION_UI_MODE_TYPE_CAR = 0x03, /** * UI mode: value that corresponds to - * television resource qualifier specified. + * television resource qualifier specified. */ ACONFIGURATION_UI_MODE_TYPE_TELEVISION = 0x04, /** * UI mode: value that corresponds to - * appliance resource qualifier specified. + * appliance resource qualifier specified. */ ACONFIGURATION_UI_MODE_TYPE_APPLIANCE = 0x05, /** * UI mode: value that corresponds to - * watch resource qualifier specified. + * watch resource qualifier specified. */ ACONFIGURATION_UI_MODE_TYPE_WATCH = 0x06, /** * UI mode: value that corresponds to - * vr resource qualifier specified. + * vr resource qualifier specified. */ ACONFIGURATION_UI_MODE_TYPE_VR_HEADSET = 0x07, @@ -344,12 +344,12 @@ enum { ACONFIGURATION_UI_MODE_NIGHT_ANY = 0x00, /** * UI night mode: value that corresponds to - * notnight resource qualifier specified. + * notnight resource qualifier specified. */ ACONFIGURATION_UI_MODE_NIGHT_NO = 0x1, /** * UI night mode: value that corresponds to - * night resource qualifier specified. + * night resource qualifier specified. */ ACONFIGURATION_UI_MODE_NIGHT_YES = 0x2, @@ -366,78 +366,78 @@ enum { ACONFIGURATION_LAYOUTDIR_ANY = 0x00, /** * Layout direction: value that corresponds to - * ldltr resource qualifier specified. + * ldltr resource qualifier specified. */ ACONFIGURATION_LAYOUTDIR_LTR = 0x01, /** * Layout direction: value that corresponds to - * ldrtl resource qualifier specified. + * ldrtl resource qualifier specified. */ ACONFIGURATION_LAYOUTDIR_RTL = 0x02, /** * Bit mask for - * mcc + * mcc * configuration. */ ACONFIGURATION_MCC = 0x0001, /** * Bit mask for - * mnc + * mnc * configuration. */ ACONFIGURATION_MNC = 0x0002, /** * Bit mask for - * locale + * locale * configuration. */ ACONFIGURATION_LOCALE = 0x0004, /** * Bit mask for - * touchscreen + * touchscreen * configuration. */ ACONFIGURATION_TOUCHSCREEN = 0x0008, /** * Bit mask for - * keyboard + * keyboard * configuration. */ ACONFIGURATION_KEYBOARD = 0x0010, /** * Bit mask for - * keyboardHidden + * keyboardHidden * configuration. */ ACONFIGURATION_KEYBOARD_HIDDEN = 0x0020, /** * Bit mask for - * navigation + * navigation * configuration. */ ACONFIGURATION_NAVIGATION = 0x0040, /** * Bit mask for - * orientation + * orientation * configuration. */ ACONFIGURATION_ORIENTATION = 0x0080, /** * Bit mask for - * density + * density * configuration. */ ACONFIGURATION_DENSITY = 0x0100, /** * Bit mask for - * screen size + * screen size * configuration. */ ACONFIGURATION_SCREEN_SIZE = 0x0200, /** * Bit mask for - * platform version + * platform version * configuration. */ ACONFIGURATION_VERSION = 0x0400, @@ -447,27 +447,27 @@ enum { ACONFIGURATION_SCREEN_LAYOUT = 0x0800, /** * Bit mask for - * ui mode + * ui mode * configuration. */ ACONFIGURATION_UI_MODE = 0x1000, /** * Bit mask for - * smallest screen width + * smallest screen width * configuration. */ ACONFIGURATION_SMALLEST_SCREEN_SIZE = 0x2000, /** * Bit mask for - * layout direction + * layout direction * configuration. */ ACONFIGURATION_LAYOUTDIR = 0x4000, ACONFIGURATION_SCREEN_ROUND = 0x8000, /** * Bit mask for - * wide color gamut - * and HDR configurations. + * wide color gamut + * and HDR configurations. */ ACONFIGURATION_COLOR_MODE = 0x10000, /** -- cgit v1.2.3-59-g8ed1b From e4623041f91c3fac633fc0f1803bc3a7c08c6eb9 Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Wed, 25 Mar 2020 16:16:40 -0700 Subject: Report ANR when waited for longer than the timeout Previously, when the touched window was unresponsive, we skipped adding the gesture monitors to the touch targets. That means, when an app has ANR, gesture nav stops working, and the phone feels frozen, until the ANR dialog comes up. Another failure mode was a stuck pending event. If there is no focused window, and we have a pending key event (or any focused event), we will be waiting for a focused window to appear. That would still prevent gesture monitors from receiving further touch events. In this solution, we do not add unresponsive windows to the list of targets, but we still proceed with handling the event, meaning that the app won't get a new DOWN when it is unresponsive, but the event would still go to the gesture monitors. That change, in isolation, would also break ANR for the case when the app loses focus. To maintain the ANR functionality, we extend the ANR detection mechanism to all connections. Now, every connection is eligible to receive an ANR, even if it's a gesture monitor. We expect everything in the system to be responsive within reasonable timeouts. We also change the handling of extended ANR timeouts coming from policy. Today, the behaviour is as follows: 1. If the policy says "wait longer", then we do nothing and just keep waiting 2. If the policy says "abort", then we send the cancel events and remove the window from the window list. The "abort" approach seems incorrect, because the policy will probably not register the existing inputchannel/connection with a new window. If the user does click "close app" when the ANR dialog appears, we will anyways receive "window removed" event via setInputWindows, and will clean up the connection that way. So we don't really need to do anything other than sending "cancel events" to the window. The policy now for sending events to unresponsive windows becomes: 1. If the unresponsive window is touched, the new touch stream does not go to the window. It will go to all other places, though. 2. If the unresponsive window receives a focused event, the event still gets queued for the unresponsive window to handle. For improved ANR performance, the ANR detection is now done by introducing a helper data structure, a multiset of the timeout times. Whenever we send an event to a connection, we will calculate the time that this event will cause a timeout. We will then add this time to the multiset of times. When we check for ANR inside dispatchOnce, we will only access the smallest (soonest) timeout inside the multiset. If the current time is before this smallest timeout time, then everything is normal, and we move on. This would cost O(1). If the time is past the timeout time, it means a connection is unresponsive. In this case, we take the closest in time unresponsive entry. That entry already has the connection token, for convenience. We then raise an ANR on that connection. The entries are removed from the multiset in several cases: 1. When we receive a 'finished' signal on an entry for a specific connection 2. When the connection becomes unresponsive and we raise ANR. In that case, no need to keep checking on the same connection. Once case 1. applies to that connection, entries from that connection will again become eligible for being added to the multiset. 3. When we reset and drop everything. 4. When we cannot find a connection for an entry in the multiset, we will drop all entries from that connection. If we report ANR for an app, we do not report the second ANR until the waitQueue becomes healthy first and then becomes clogged again. If we have a focused application, but no window has focus, then nothing will happen for pointer events. They will keep going to the touched window as normal. When we receive the first focused event, however, we will start a timer. If there is no focused window added by that time, we will send an ANR for that application. This logic should be moved into WM later, because from the input perspective, it is legitimate to have an application without a focused window. This would also allow WM to remove the "setFocusedApplication" call. Bug: 143459140 Test: use the test app from the bug. The app sleeps for 10 seconds when the button is clicked. Click the button, and try to use gesture nav several times. Observe that gesture nav continues to work even after app has ANR. Observe that ANR is reported even after interacting with gesture nav. Test: Click on the app multiple times (to clog up the queue), and then wait for a long time, even after the ANR dialog shows up. Then click "wait" to not close the app. Then click again on the app. Observe that the anr dialog appears after a while. This indicates that at some point, the app processed all events, and then became eligible for anr again. Test: adb shell -t /data/nativetest64/inputflinger_tests/inputflinger_tests Test: create an app that sets "FLAG_NOT_FOCUSABLE" on its only window. Launch the app and interact with it by touch. Notice that the app does not ANR. Then, send the back key to the app (using the back gesture). Notice that in 5 seconds, we receive an ANR for this app. While the BACK key is queued up for this app, the gesture nav continues to work and the notification shade can still be pulled down. Change-Id: I2c0fd1957cda833f5fbe26368cfcaa6fea6eddaf Merged-In: I2c0fd1957cda833f5fbe26368cfcaa6fea6eddaf --- include/input/Input.h | 4 + libs/input/Input.cpp | 31 + services/inputflinger/dispatcher/Android.bp | 1 + services/inputflinger/dispatcher/AnrTracker.cpp | 73 ++ services/inputflinger/dispatcher/AnrTracker.h | 60 ++ services/inputflinger/dispatcher/Connection.cpp | 3 +- services/inputflinger/dispatcher/Connection.h | 7 +- services/inputflinger/dispatcher/Entry.cpp | 36 +- services/inputflinger/dispatcher/Entry.h | 4 + .../inputflinger/dispatcher/InputDispatcher.cpp | 665 ++++++++++-------- services/inputflinger/dispatcher/InputDispatcher.h | 86 ++- services/inputflinger/tests/Android.bp | 1 + services/inputflinger/tests/AnrTracker_test.cpp | 167 +++++ .../inputflinger/tests/InputDispatcher_test.cpp | 750 +++++++++++++++++++-- 14 files changed, 1499 insertions(+), 389 deletions(-) create mode 100644 services/inputflinger/dispatcher/AnrTracker.cpp create mode 100644 services/inputflinger/dispatcher/AnrTracker.h create mode 100644 services/inputflinger/tests/AnrTracker_test.cpp (limited to 'include') diff --git a/include/input/Input.h b/include/input/Input.h index 9e47318203..54b4e5a737 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -462,6 +462,8 @@ public: nsecs_t eventTime); void initialize(const KeyEvent& from); + static const char* actionToString(int32_t action); + protected: int32_t mAction; int32_t mFlags; @@ -725,6 +727,8 @@ public: static const char* getLabel(int32_t axis); static int32_t getAxisFromLabel(const char* label); + static const char* actionToString(int32_t action); + protected: int32_t mAction; int32_t mActionButton; diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index c2437673df..31aa685391 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -169,6 +169,18 @@ void KeyEvent::initialize(const KeyEvent& from) { mEventTime = from.mEventTime; } +const char* KeyEvent::actionToString(int32_t action) { + // Convert KeyEvent action to string + switch (action) { + case AKEY_EVENT_ACTION_DOWN: + return "DOWN"; + case AKEY_EVENT_ACTION_UP: + return "UP"; + case AKEY_EVENT_ACTION_MULTIPLE: + return "MULTIPLE"; + } + return "UNKNOWN"; +} // --- PointerCoords --- @@ -678,6 +690,25 @@ int32_t MotionEvent::getAxisFromLabel(const char* label) { return getAxisByLabel(label); } +const char* MotionEvent::actionToString(int32_t action) { + // Convert MotionEvent action to string + switch (action & AMOTION_EVENT_ACTION_MASK) { + case AMOTION_EVENT_ACTION_DOWN: + return "DOWN"; + case AMOTION_EVENT_ACTION_MOVE: + return "MOVE"; + case AMOTION_EVENT_ACTION_UP: + return "UP"; + case AMOTION_EVENT_ACTION_CANCEL: + return "CANCEL"; + case AMOTION_EVENT_ACTION_POINTER_DOWN: + return "POINTER_DOWN"; + case AMOTION_EVENT_ACTION_POINTER_UP: + return "POINTER_UP"; + } + return "UNKNOWN"; +} + // --- FocusEvent --- void FocusEvent::initialize(int32_t id, bool hasFocus, bool inTouchMode) { diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp index b242eec465..d29d8dfda3 100644 --- a/services/inputflinger/dispatcher/Android.bp +++ b/services/inputflinger/dispatcher/Android.bp @@ -22,6 +22,7 @@ cc_library_headers { filegroup { name: "libinputdispatcher_sources", srcs: [ + "AnrTracker.cpp", "Connection.cpp", "Entry.cpp", "InjectionState.cpp", diff --git a/services/inputflinger/dispatcher/AnrTracker.cpp b/services/inputflinger/dispatcher/AnrTracker.cpp new file mode 100644 index 0000000000..c3f611e7db --- /dev/null +++ b/services/inputflinger/dispatcher/AnrTracker.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "AnrTracker.h" + +namespace android::inputdispatcher { + +template +static T max(const T& a, const T& b) { + return a < b ? b : a; +} + +void AnrTracker::insert(nsecs_t timeoutTime, sp token) { + mAnrTimeouts.insert(std::make_pair(timeoutTime, std::move(token))); +} + +/** + * Erase a single entry only. If there are multiple duplicate entries + * (same time, same connection), then only remove one of them. + */ +void AnrTracker::erase(nsecs_t timeoutTime, const sp& token) { + auto pair = std::make_pair(timeoutTime, token); + auto it = mAnrTimeouts.find(pair); + if (it != mAnrTimeouts.end()) { + mAnrTimeouts.erase(it); + } +} + +void AnrTracker::eraseToken(const sp& token) { + for (auto it = mAnrTimeouts.begin(); it != mAnrTimeouts.end();) { + if (it->second == token) { + it = mAnrTimeouts.erase(it); + } else { + ++it; + } + } +} + +bool AnrTracker::empty() const { + return mAnrTimeouts.empty(); +} + +// If empty() is false, return the time at which the next connection should cause an ANR +// If empty() is true, return LONG_LONG_MAX +nsecs_t AnrTracker::firstTimeout() const { + if (mAnrTimeouts.empty()) { + return std::numeric_limits::max(); + } + return mAnrTimeouts.begin()->first; +} + +const sp& AnrTracker::firstToken() const { + return mAnrTimeouts.begin()->second; +} + +void AnrTracker::clear() { + mAnrTimeouts.clear(); +} + +} // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/AnrTracker.h b/services/inputflinger/dispatcher/AnrTracker.h new file mode 100644 index 0000000000..097dba5bea --- /dev/null +++ b/services/inputflinger/dispatcher/AnrTracker.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUT_INPUTDISPATCHER_ANRTRACKER_H +#define _UI_INPUT_INPUTDISPATCHER_ANRTRACKER_H + +#include +#include +#include + +namespace android::inputdispatcher { + +/** + * Keeps track of the times when each connection is going to ANR. + * Provides the ability to quickly find the connection that is going to cause ANR next. + */ +class AnrTracker { +public: + void insert(nsecs_t timeoutTime, sp token); + void erase(nsecs_t timeoutTime, const sp& token); + void eraseToken(const sp& token); + void clear(); + + bool empty() const; + // If empty() is false, return the time at which the next connection should cause an ANR + // If empty() is true, return LONG_LONG_MAX + nsecs_t firstTimeout() const; + // Return the token of the next connection that should cause an ANR. + // Do not call this unless empty() is false, you will encounter undefined behaviour. + const sp& firstToken() const; + +private: + // Optimization: use a multiset to keep track of the event timeouts. When an event is sent + // to the InputConsumer, we add an entry to this structure. We look at the smallest value to + // determine if any of the connections is unresponsive, and to determine when we should wake + // next for the future ANR check. + // Using a multiset helps quickly look up the next timeout due. + // + // We must use a multi-set, because it is plausible (although highly unlikely) to have entries + // from the same connection and same timestamp, but different sequence numbers. + // We are not tracking sequence numbers, and just allow duplicates to exist. + std::multiset /*connectionToken*/>> mAnrTimeouts; +}; + +} // namespace android::inputdispatcher + +#endif // _UI_INPUT_INPUTDISPATCHER_ANRTRACKER_H diff --git a/services/inputflinger/dispatcher/Connection.cpp b/services/inputflinger/dispatcher/Connection.cpp index 188212bccf..f5ea563311 100644 --- a/services/inputflinger/dispatcher/Connection.cpp +++ b/services/inputflinger/dispatcher/Connection.cpp @@ -26,8 +26,7 @@ Connection::Connection(const sp& inputChannel, bool monitor, inputChannel(inputChannel), monitor(monitor), inputPublisher(inputChannel), - inputState(idGenerator), - inputPublisherBlocked(false) {} + inputState(idGenerator) {} Connection::~Connection() {} diff --git a/services/inputflinger/dispatcher/Connection.h b/services/inputflinger/dispatcher/Connection.h index bb3f2fee19..3b33f29dff 100644 --- a/services/inputflinger/dispatcher/Connection.h +++ b/services/inputflinger/dispatcher/Connection.h @@ -47,9 +47,10 @@ public: InputPublisher inputPublisher; InputState inputState; - // True if the socket is full and no further events can be published until - // the application consumes some of the input. - bool inputPublisherBlocked; + // True if this connection is responsive. + // If this connection is not responsive, avoid publishing more events to it until the + // application consumes some of the input. + bool responsive = true; // Queue of events that need to be published to the connection. std::deque outboundQueue; diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp index 21c8ae165d..fdbb1d1b55 100644 --- a/services/inputflinger/dispatcher/Entry.cpp +++ b/services/inputflinger/dispatcher/Entry.cpp @@ -28,38 +28,6 @@ using android::base::StringPrintf; namespace android::inputdispatcher { -static std::string motionActionToString(int32_t action) { - // Convert MotionEvent action to string - switch (action & AMOTION_EVENT_ACTION_MASK) { - case AMOTION_EVENT_ACTION_DOWN: - return "DOWN"; - case AMOTION_EVENT_ACTION_MOVE: - return "MOVE"; - case AMOTION_EVENT_ACTION_UP: - return "UP"; - case AMOTION_EVENT_ACTION_CANCEL: - return "CANCEL"; - case AMOTION_EVENT_ACTION_POINTER_DOWN: - return "POINTER_DOWN"; - case AMOTION_EVENT_ACTION_POINTER_UP: - return "POINTER_UP"; - } - return StringPrintf("%" PRId32, action); -} - -static std::string keyActionToString(int32_t action) { - // Convert KeyEvent action to string - switch (action) { - case AKEY_EVENT_ACTION_DOWN: - return "DOWN"; - case AKEY_EVENT_ACTION_UP: - return "UP"; - case AKEY_EVENT_ACTION_MULTIPLE: - return "MULTIPLE"; - } - return StringPrintf("%" PRId32, action); -} - VerifiedKeyEvent verifiedKeyEventFromKeyEntry(const KeyEntry& entry) { return {{VerifiedInputEvent::Type::KEY, entry.deviceId, entry.eventTime, entry.source, entry.displayId}, @@ -191,7 +159,7 @@ void KeyEntry::appendDescription(std::string& msg) const { msg += StringPrintf("(deviceId=%d, source=0x%08x, displayId=%" PRId32 ", action=%s, " "flags=0x%08x, keyCode=%d, scanCode=%d, metaState=0x%08x, " "repeatCount=%d), policyFlags=0x%08x", - deviceId, source, displayId, keyActionToString(action).c_str(), flags, + deviceId, source, displayId, KeyEvent::actionToString(action), flags, keyCode, scanCode, metaState, repeatCount, policyFlags); } @@ -253,7 +221,7 @@ void MotionEntry::appendDescription(std::string& msg) const { "buttonState=0x%08x, " "classification=%s, edgeFlags=0x%08x, xPrecision=%.1f, yPrecision=%.1f, " "xCursorPosition=%0.1f, yCursorPosition=%0.1f, pointers=[", - deviceId, source, displayId, motionActionToString(action).c_str(), + deviceId, source, displayId, MotionEvent::actionToString(action), actionButton, flags, metaState, buttonState, motionClassificationToString(classification), edgeFlags, xPrecision, yPrecision, xCursorPosition, yCursorPosition); diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h index a135409365..6b7697dde6 100644 --- a/services/inputflinger/dispatcher/Entry.h +++ b/services/inputflinger/dispatcher/Entry.h @@ -198,7 +198,11 @@ struct DispatchEntry { float globalScaleFactor; float windowXScale = 1.0f; float windowYScale = 1.0f; + // Both deliveryTime and timeoutTime are only populated when the entry is sent to the app, + // and will be undefined before that. nsecs_t deliveryTime; // time when the event was actually delivered + // An ANR will be triggered if a response for this entry is not received by timeoutTime + nsecs_t timeoutTime; // Set to the resolved ID, action and flags when the event is enqueued. int32_t resolvedEventId; diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 959eeea41e..677bf7e2ec 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -90,18 +90,17 @@ constexpr nsecs_t APP_SWITCH_TIMEOUT = 500 * 1000000LL; // 0.5sec // before considering it stale and dropping it. constexpr nsecs_t STALE_EVENT_TIMEOUT = 10000 * 1000000LL; // 10sec -// Amount of time to allow touch events to be streamed out to a connection before requiring -// that the first event be finished. This value extends the ANR timeout by the specified -// amount. For example, if streaming is allowed to get ahead by one second relative to the -// queue of waiting unfinished events, then ANRs will similarly be delayed by one second. -constexpr nsecs_t STREAM_AHEAD_EVENT_TIMEOUT = 500 * 1000000LL; // 0.5sec - // Log a warning when an event takes longer than this to process, even if an ANR does not occur. constexpr nsecs_t SLOW_EVENT_PROCESSING_WARNING_TIMEOUT = 2000 * 1000000LL; // 2sec // Log a warning when an interception call takes longer than this to process. constexpr std::chrono::milliseconds SLOW_INTERCEPTION_THRESHOLD = 50ms; +// Additional key latency in case a connection is still processing some motion events. +// This will help with the case when a user touched a button that opens a new window, +// and gives us the chance to dispatch the key to this new window. +constexpr std::chrono::nanoseconds KEY_WAITING_FOR_EVENTS_TIMEOUT = 500ms; + // Number of recent events to keep for debugging purposes. constexpr size_t RECENT_QUEUE_MAX_SIZE = 10; @@ -409,8 +408,7 @@ InputDispatcher::InputDispatcher(const sp& polic // To avoid leaking stack in case that call never comes, and for tests, // initialize it here anyways. mInTouchMode(true), - mFocusedDisplayId(ADISPLAY_ID_DEFAULT), - mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE) { + mFocusedDisplayId(ADISPLAY_ID_DEFAULT) { mLooper = new Looper(false); mReporter = createInputReporter(); @@ -470,6 +468,11 @@ void InputDispatcher::dispatchOnce() { nextWakeupTime = LONG_LONG_MIN; } + // If we are still waiting for ack on some events, + // we might have to wake up earlier to check if an app is anr'ing. + const nsecs_t nextAnrCheck = processAnrsLocked(); + nextWakeupTime = std::min(nextWakeupTime, nextAnrCheck); + // We are about to enter an infinitely long sleep, because we have no commands or // pending or queued events if (nextWakeupTime == LONG_LONG_MAX) { @@ -483,6 +486,55 @@ void InputDispatcher::dispatchOnce() { mLooper->pollOnce(timeoutMillis); } +/** + * Check if any of the connections' wait queues have events that are too old. + * If we waited for events to be ack'ed for more than the window timeout, raise an ANR. + * Return the time at which we should wake up next. + */ +nsecs_t InputDispatcher::processAnrsLocked() { + const nsecs_t currentTime = now(); + nsecs_t nextAnrCheck = LONG_LONG_MAX; + // Check if we are waiting for a focused window to appear. Raise ANR if waited too long + if (mNoFocusedWindowTimeoutTime.has_value() && mAwaitedFocusedApplication != nullptr) { + if (currentTime >= *mNoFocusedWindowTimeoutTime) { + onAnrLocked(mAwaitedFocusedApplication); + mAwaitedFocusedApplication.clear(); + return LONG_LONG_MIN; + } else { + // Keep waiting + const nsecs_t millisRemaining = ns2ms(*mNoFocusedWindowTimeoutTime - currentTime); + ALOGW("Still no focused window. Will drop the event in %" PRId64 "ms", millisRemaining); + nextAnrCheck = *mNoFocusedWindowTimeoutTime; + } + } + + // Check if any connection ANRs are due + nextAnrCheck = std::min(nextAnrCheck, mAnrTracker.firstTimeout()); + if (currentTime < nextAnrCheck) { // most likely scenario + return nextAnrCheck; // everything is normal. Let's check again at nextAnrCheck + } + + // If we reached here, we have an unresponsive connection. + sp connection = getConnectionLocked(mAnrTracker.firstToken()); + if (connection == nullptr) { + ALOGE("Could not find connection for entry %" PRId64, mAnrTracker.firstTimeout()); + return nextAnrCheck; + } + connection->responsive = false; + // Stop waking up for this unresponsive connection + mAnrTracker.eraseToken(connection->inputChannel->getConnectionToken()); + onAnrLocked(connection); + return LONG_LONG_MIN; +} + +nsecs_t InputDispatcher::getDispatchingTimeoutLocked(const sp& token) { + sp window = getWindowHandleLocked(token); + if (window != nullptr) { + return window->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT).count(); + } + return DEFAULT_INPUT_DISPATCHING_TIMEOUT.count(); +} + void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { nsecs_t currentTime = now(); @@ -546,9 +598,6 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { if (mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER) { pokeUserActivityLocked(*mPendingEvent); } - - // Get ready to dispatch the event. - resetAnrTimeoutsLocked(); } // Now we have an event to dispatch. @@ -642,11 +691,14 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { * Return false otherwise (the default behaviour) */ bool InputDispatcher::shouldPruneInboundQueueLocked(const MotionEntry& motionEntry) { - bool isPointerDownEvent = motionEntry.action == AMOTION_EVENT_ACTION_DOWN && + const bool isPointerDownEvent = motionEntry.action == AMOTION_EVENT_ACTION_DOWN && (motionEntry.source & AINPUT_SOURCE_CLASS_POINTER); - if (isPointerDownEvent && - mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY && - mInputTargetWaitApplicationToken != nullptr) { + + // Optimize case where the current application is unresponsive and the user + // decides to touch a window in a different application. + // If the application takes too long to catch up then we drop all events preceding + // the touch into the other window. + if (isPointerDownEvent && mAwaitedFocusedApplication != nullptr) { int32_t displayId = motionEntry.displayId; int32_t x = static_cast( motionEntry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X)); @@ -655,12 +707,41 @@ bool InputDispatcher::shouldPruneInboundQueueLocked(const MotionEntry& motionEnt sp touchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y, nullptr); if (touchedWindowHandle != nullptr && - touchedWindowHandle->getApplicationToken() != mInputTargetWaitApplicationToken) { + touchedWindowHandle->getApplicationToken() != + mAwaitedFocusedApplication->getApplicationToken()) { // User touched a different application than the one we are waiting on. - // Flag the event, and start pruning the input queue. - ALOGI("Pruning input queue because user touched a different application"); + ALOGI("Pruning input queue because user touched a different application while waiting " + "for %s", + mAwaitedFocusedApplication->getName().c_str()); return true; } + + // Alternatively, maybe there's a gesture monitor that could handle this event + std::vector gestureMonitors = + findTouchedGestureMonitorsLocked(displayId, {}); + for (TouchedMonitor& gestureMonitor : gestureMonitors) { + sp connection = + getConnectionLocked(gestureMonitor.monitor.inputChannel->getConnectionToken()); + if (connection->responsive) { + // This monitor could take more input. Drop all events preceding this + // event, so that gesture monitor could get a chance to receive the stream + ALOGW("Pruning the input queue because %s is unresponsive, but we have a " + "responsive gesture monitor that may handle the event", + mAwaitedFocusedApplication->getName().c_str()); + return true; + } + } + } + + // Prevent getting stuck: if we have a pending key event, and some motion events that have not + // yet been processed by some connections, the dispatcher will wait for these motion + // events to be processed before dispatching the key event. This is because these motion events + // may cause a new window to be launched, which the user might expect to receive focus. + // To prevent waiting forever for such events, just send the key to the currently focused window + if (isPointerDownEvent && mKeyIsWaitingForEventsTimeout) { + ALOGD("Received a new pointer down event, stop waiting for events to process and " + "just send the pending key event to the focused window."); + mKeyIsWaitingForEventsTimeout = now(); } return false; } @@ -694,10 +775,6 @@ bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) { } case EventEntry::Type::MOTION: { - // Optimize case where the current application is unresponsive and the user - // decides to touch a window in a different application. - // If the application takes too long to catch up then we drop all events preceding - // the touch into the other window. if (shouldPruneInboundQueueLocked(static_cast(*entry))) { mNextUnblockedEvent = entry; needWake = true; @@ -912,7 +989,6 @@ void InputDispatcher::drainInboundQueueLocked() { void InputDispatcher::releasePendingEventLocked() { if (mPendingEvent) { - resetAnrTimeoutsLocked(); releaseInboundEventLocked(mPendingEvent); mPendingEvent = nullptr; } @@ -1294,109 +1370,29 @@ void InputDispatcher::dispatchEventLocked(nsecs_t currentTime, EventEntry* event } } -int32_t InputDispatcher::handleTargetsNotReadyLocked( - nsecs_t currentTime, const EventEntry& entry, - const sp& applicationHandle, - const sp& windowHandle, nsecs_t* nextWakeupTime, const char* reason) { - if (applicationHandle == nullptr && windowHandle == nullptr) { - if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY) { - if (DEBUG_FOCUS) { - ALOGD("Waiting for system to become ready for input. Reason: %s", reason); - } - mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY; - mInputTargetWaitStartTime = currentTime; - mInputTargetWaitTimeoutTime = LONG_LONG_MAX; - mInputTargetWaitTimeoutExpired = false; - mInputTargetWaitApplicationToken.clear(); - } - } else { - if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) { - ALOGI("Waiting for application to become ready for input: %s. Reason: %s", - getApplicationWindowLabel(applicationHandle, windowHandle).c_str(), reason); - std::chrono::nanoseconds timeout; - if (windowHandle != nullptr) { - timeout = windowHandle->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT); - } else if (applicationHandle != nullptr) { - timeout = - applicationHandle->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT); - } else { - timeout = DEFAULT_INPUT_DISPATCHING_TIMEOUT; - } - - mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY; - mInputTargetWaitStartTime = currentTime; - mInputTargetWaitTimeoutTime = currentTime + timeout.count(); - mInputTargetWaitTimeoutExpired = false; - mInputTargetWaitApplicationToken.clear(); - - if (windowHandle != nullptr) { - mInputTargetWaitApplicationToken = windowHandle->getApplicationToken(); - } - if (mInputTargetWaitApplicationToken == nullptr && applicationHandle != nullptr) { - mInputTargetWaitApplicationToken = applicationHandle->getApplicationToken(); - } - } - } - - if (mInputTargetWaitTimeoutExpired) { - return INPUT_EVENT_INJECTION_TIMED_OUT; - } - - if (currentTime >= mInputTargetWaitTimeoutTime) { - onAnrLocked(currentTime, applicationHandle, windowHandle, entry.eventTime, - mInputTargetWaitStartTime, reason); - - // Force poll loop to wake up immediately on next iteration once we get the - // ANR response back from the policy. - *nextWakeupTime = LONG_LONG_MIN; - return INPUT_EVENT_INJECTION_PENDING; - } else { - // Force poll loop to wake up when timeout is due. - if (mInputTargetWaitTimeoutTime < *nextWakeupTime) { - *nextWakeupTime = mInputTargetWaitTimeoutTime; - } - return INPUT_EVENT_INJECTION_PENDING; - } -} - -void InputDispatcher::removeWindowByTokenLocked(const sp& token) { - for (std::pair& pair : mTouchStatesByDisplay) { - TouchState& state = pair.second; - state.removeWindowByToken(token); - } -} - -void InputDispatcher::resumeAfterTargetsNotReadyTimeoutLocked( - nsecs_t timeoutExtension, const sp& inputConnectionToken) { - if (timeoutExtension > 0) { - // Extend the timeout. - mInputTargetWaitTimeoutTime = now() + timeoutExtension; - } else { - // Give up. - mInputTargetWaitTimeoutExpired = true; - - // Input state will not be realistic. Mark it out of sync. - sp connection = getConnectionLocked(inputConnectionToken); - if (connection != nullptr) { - removeWindowByTokenLocked(inputConnectionToken); - - if (connection->status == Connection::STATUS_NORMAL) { - CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS, - "application not responding"); - synthesizeCancelationEventsForConnectionLocked(connection, options); - } - } +void InputDispatcher::cancelEventsForAnrLocked(const sp& connection) { + // We will not be breaking any connections here, even if the policy wants us to abort dispatch. + // If the policy decides to close the app, we will get a channel removal event via + // unregisterInputChannel, and will clean up the connection that way. We are already not + // sending new pointers to the connection when it blocked, but focused events will continue to + // pile up. + ALOGW("Canceling events for %s because it is unresponsive", + connection->inputChannel->getName().c_str()); + if (connection->status == Connection::STATUS_NORMAL) { + CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS, + "application not responding"); + synthesizeCancelationEventsForConnectionLocked(connection, options); } } -void InputDispatcher::resetAnrTimeoutsLocked() { +void InputDispatcher::resetNoFocusedWindowTimeoutLocked() { if (DEBUG_FOCUS) { ALOGD("Resetting ANR timeouts."); } // Reset input target wait timeout. - mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_NONE; - mInputTargetWaitApplicationToken.clear(); + mNoFocusedWindowTimeoutTime = std::nullopt; + mAwaitedFocusedApplication.clear(); } /** @@ -1427,6 +1423,36 @@ int32_t InputDispatcher::getTargetDisplayId(const EventEntry& entry) { return displayId == ADISPLAY_ID_NONE ? mFocusedDisplayId : displayId; } +bool InputDispatcher::shouldWaitToSendKeyLocked(nsecs_t currentTime, + const char* focusedWindowName) { + if (mAnrTracker.empty()) { + // already processed all events that we waited for + mKeyIsWaitingForEventsTimeout = std::nullopt; + return false; + } + + if (!mKeyIsWaitingForEventsTimeout.has_value()) { + // Start the timer + ALOGD("Waiting to send key to %s because there are unprocessed events that may cause " + "focus to change", + focusedWindowName); + mKeyIsWaitingForEventsTimeout = currentTime + KEY_WAITING_FOR_EVENTS_TIMEOUT.count(); + return true; + } + + // We still have pending events, and already started the timer + if (currentTime < *mKeyIsWaitingForEventsTimeout) { + return true; // Still waiting + } + + // Waited too long, and some connection still hasn't processed all motions + // Just send the key to the focused window + ALOGW("Dispatching key to %s even though there are other unprocessed events", + focusedWindowName); + mKeyIsWaitingForEventsTimeout = std::nullopt; + return false; +} + int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime, const EventEntry& entry, std::vector& inputTargets, @@ -1441,31 +1467,70 @@ int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime, // If there is no currently focused window and no focused application // then drop the event. - if (focusedWindowHandle == nullptr) { - if (focusedApplicationHandle != nullptr) { - return handleTargetsNotReadyLocked(currentTime, entry, focusedApplicationHandle, - nullptr, nextWakeupTime, - "Waiting because no window has focus but there is " - "a focused application that may eventually add a " - "window when it finishes starting up."); - } - - ALOGI("Dropping event because there is no focused window or focused application in display " - "%" PRId32 ".", - displayId); + if (focusedWindowHandle == nullptr && focusedApplicationHandle == nullptr) { + ALOGI("Dropping %s event because there is no focused window or focused application in " + "display %" PRId32 ".", + EventEntry::typeToString(entry.type), displayId); return INPUT_EVENT_INJECTION_FAILED; } + // Compatibility behavior: raise ANR if there is a focused application, but no focused window. + // Only start counting when we have a focused event to dispatch. The ANR is canceled if we + // start interacting with another application via touch (app switch). This code can be removed + // if the "no focused window ANR" is moved to the policy. Input doesn't know whether + // an app is expected to have a focused window. + if (focusedWindowHandle == nullptr && focusedApplicationHandle != nullptr) { + if (!mNoFocusedWindowTimeoutTime.has_value()) { + // We just discovered that there's no focused window. Start the ANR timer + const nsecs_t timeout = focusedApplicationHandle->getDispatchingTimeout( + DEFAULT_INPUT_DISPATCHING_TIMEOUT.count()); + mNoFocusedWindowTimeoutTime = currentTime + timeout; + mAwaitedFocusedApplication = focusedApplicationHandle; + ALOGW("Waiting because no window has focus but %s may eventually add a " + "window when it finishes starting up. Will wait for %" PRId64 "ms", + mAwaitedFocusedApplication->getName().c_str(), ns2ms(timeout)); + *nextWakeupTime = *mNoFocusedWindowTimeoutTime; + return INPUT_EVENT_INJECTION_PENDING; + } else if (currentTime > *mNoFocusedWindowTimeoutTime) { + // Already raised ANR. Drop the event + ALOGE("Dropping %s event because there is no focused window", + EventEntry::typeToString(entry.type)); + return INPUT_EVENT_INJECTION_FAILED; + } else { + // Still waiting for the focused window + return INPUT_EVENT_INJECTION_PENDING; + } + } + + // we have a valid, non-null focused window + resetNoFocusedWindowTimeoutLocked(); + // Check permissions. if (!checkInjectionPermission(focusedWindowHandle, entry.injectionState)) { return INPUT_EVENT_INJECTION_PERMISSION_DENIED; } - // Check whether the window is ready for more input. - reason = checkWindowReadyForMoreInputLocked(currentTime, focusedWindowHandle, entry, "focused"); - if (!reason.empty()) { - return handleTargetsNotReadyLocked(currentTime, entry, focusedApplicationHandle, - focusedWindowHandle, nextWakeupTime, reason.c_str()); + if (focusedWindowHandle->getInfo()->paused) { + ALOGI("Waiting because %s is paused", focusedWindowHandle->getName().c_str()); + return INPUT_EVENT_INJECTION_PENDING; + } + + // If the event is a key event, then we must wait for all previous events to + // complete before delivering it because previous events may have the + // side-effect of transferring focus to a different window and we want to + // ensure that the following keys are sent to the new window. + // + // Suppose the user touches a button in a window then immediately presses "A". + // If the button causes a pop-up window to appear then we want to ensure that + // the "A" key is delivered to the new pop-up window. This is because users + // often anticipate pending UI changes when typing on a keyboard. + // To obtain this behavior, we must serialize key events with respect to all + // prior input events. + if (entry.type == EventEntry::Type::KEY) { + if (shouldWaitToSendKeyLocked(currentTime, focusedWindowHandle->getName().c_str())) { + *nextWakeupTime = *mKeyIsWaitingForEventsTimeout; + return INPUT_EVENT_INJECTION_PENDING; + } } // Success! Output targets. @@ -1477,6 +1542,32 @@ int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime, return INPUT_EVENT_INJECTION_SUCCEEDED; } +/** + * Given a list of monitors, remove the ones we cannot find a connection for, and the ones + * that are currently unresponsive. + */ +std::vector InputDispatcher::selectResponsiveMonitorsLocked( + const std::vector& monitors) const { + std::vector responsiveMonitors; + std::copy_if(monitors.begin(), monitors.end(), std::back_inserter(responsiveMonitors), + [this](const TouchedMonitor& monitor) REQUIRES(mLock) { + sp connection = getConnectionLocked( + monitor.monitor.inputChannel->getConnectionToken()); + if (connection == nullptr) { + ALOGE("Could not find connection for monitor %s", + monitor.monitor.inputChannel->getName().c_str()); + return false; + } + if (!connection->responsive) { + ALOGW("Unresponsive monitor %s will not get the new gesture", + connection->inputChannel->getName().c_str()); + return false; + } + return true; + }); + return responsiveMonitors; +} + int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, const MotionEntry& entry, std::vector& inputTargets, @@ -1592,6 +1683,29 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, newTouchedWindowHandle = tempTouchState.getFirstForegroundWindowHandle(); } + if (newTouchedWindowHandle != nullptr && newTouchedWindowHandle->getInfo()->paused) { + ALOGI("Not sending touch event to %s because it is paused", + newTouchedWindowHandle->getName().c_str()); + newTouchedWindowHandle = nullptr; + } + + if (newTouchedWindowHandle != nullptr) { + sp connection = getConnectionLocked(newTouchedWindowHandle->getToken()); + if (connection == nullptr) { + ALOGI("Could not find connection for %s", + newTouchedWindowHandle->getName().c_str()); + newTouchedWindowHandle = nullptr; + } else if (!connection->responsive) { + // don't send the new touch to an unresponsive window + ALOGW("Unresponsive window %s will not get the new gesture at %" PRIu64, + newTouchedWindowHandle->getName().c_str(), entry.eventTime); + newTouchedWindowHandle = nullptr; + } + } + + // Also don't send the new touch event to unresponsive gesture monitors + newGestureMonitors = selectResponsiveMonitorsLocked(newGestureMonitors); + if (newTouchedWindowHandle == nullptr && newGestureMonitors.empty()) { ALOGI("Dropping event because there is no touchable window or gesture monitor at " "(%d, %d) in display %" PRId32 ".", @@ -1758,21 +1872,6 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, } } - // Ensure all touched foreground windows are ready for new input. - for (const TouchedWindow& touchedWindow : tempTouchState.windows) { - if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) { - // Check whether the window is ready for more input. - std::string reason = - checkWindowReadyForMoreInputLocked(currentTime, touchedWindow.windowHandle, - entry, "touched"); - if (!reason.empty()) { - return handleTargetsNotReadyLocked(currentTime, entry, nullptr, - touchedWindow.windowHandle, nextWakeupTime, - reason.c_str()); - } - } - } - // If this is the first pointer going down and the touched window has a wallpaper // then also add the touched wallpaper windows so they are locked in for the duration // of the touch gesture. @@ -2050,92 +2149,6 @@ bool InputDispatcher::isWindowObscuredLocked(const sp& window return false; } -std::string InputDispatcher::checkWindowReadyForMoreInputLocked( - nsecs_t currentTime, const sp& windowHandle, - const EventEntry& eventEntry, const char* targetType) { - // If the window is paused then keep waiting. - if (windowHandle->getInfo()->paused) { - return StringPrintf("Waiting because the %s window is paused.", targetType); - } - - // If the window's connection is not registered then keep waiting. - sp connection = getConnectionLocked(windowHandle->getToken()); - if (connection == nullptr) { - return StringPrintf("Waiting because the %s window's input channel is not " - "registered with the input dispatcher. The window may be in the " - "process of being removed.", - targetType); - } - - // If the connection is dead then keep waiting. - if (connection->status != Connection::STATUS_NORMAL) { - return StringPrintf("Waiting because the %s window's input connection is %s." - "The window may be in the process of being removed.", - targetType, connection->getStatusLabel()); - } - - // If the connection is backed up then keep waiting. - if (connection->inputPublisherBlocked) { - return StringPrintf("Waiting because the %s window's input channel is full. " - "Outbound queue length: %zu. Wait queue length: %zu.", - targetType, connection->outboundQueue.size(), - connection->waitQueue.size()); - } - - // Ensure that the dispatch queues aren't too far backed up for this event. - if (eventEntry.type == EventEntry::Type::KEY) { - // If the event is a key event, then we must wait for all previous events to - // complete before delivering it because previous events may have the - // side-effect of transferring focus to a different window and we want to - // ensure that the following keys are sent to the new window. - // - // Suppose the user touches a button in a window then immediately presses "A". - // If the button causes a pop-up window to appear then we want to ensure that - // the "A" key is delivered to the new pop-up window. This is because users - // often anticipate pending UI changes when typing on a keyboard. - // To obtain this behavior, we must serialize key events with respect to all - // prior input events. - if (!connection->outboundQueue.empty() || !connection->waitQueue.empty()) { - return StringPrintf("Waiting to send key event because the %s window has not " - "finished processing all of the input events that were previously " - "delivered to it. Outbound queue length: %zu. Wait queue length: " - "%zu.", - targetType, connection->outboundQueue.size(), - connection->waitQueue.size()); - } - } else { - // Touch events can always be sent to a window immediately because the user intended - // to touch whatever was visible at the time. Even if focus changes or a new - // window appears moments later, the touch event was meant to be delivered to - // whatever window happened to be on screen at the time. - // - // Generic motion events, such as trackball or joystick events are a little trickier. - // Like key events, generic motion events are delivered to the focused window. - // Unlike key events, generic motion events don't tend to transfer focus to other - // windows and it is not important for them to be serialized. So we prefer to deliver - // generic motion events as soon as possible to improve efficiency and reduce lag - // through batching. - // - // The one case where we pause input event delivery is when the wait queue is piling - // up with lots of events because the application is not responding. - // This condition ensures that ANRs are detected reliably. - if (!connection->waitQueue.empty() && - currentTime >= - connection->waitQueue.front()->deliveryTime + STREAM_AHEAD_EVENT_TIMEOUT) { - return StringPrintf("Waiting to send non-key event because the %s window has not " - "finished processing certain input events that were delivered to " - "it over " - "%0.1fms ago. Wait queue length: %zu. Wait queue head age: " - "%0.1fms.", - targetType, STREAM_AHEAD_EVENT_TIMEOUT * 0.000001f, - connection->waitQueue.size(), - (currentTime - connection->waitQueue.front()->deliveryTime) * - 0.000001f); - } - } - return ""; -} - std::string InputDispatcher::getApplicationWindowLabel( const sp& applicationHandle, const sp& windowHandle) { @@ -2535,6 +2548,9 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, while (connection->status == Connection::STATUS_NORMAL && !connection->outboundQueue.empty()) { DispatchEntry* dispatchEntry = connection->outboundQueue.front(); dispatchEntry->deliveryTime = currentTime; + const nsecs_t timeout = + getDispatchingTimeoutLocked(connection->inputChannel->getConnectionToken()); + dispatchEntry->timeoutTime = currentTime + timeout; // Publish the event. status_t status; @@ -2654,7 +2670,6 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, "waiting for the application to catch up", connection->getInputChannelName().c_str()); #endif - connection->inputPublisherBlocked = true; } } else { ALOGE("channel '%s' ~ Could not publish event due to an unexpected error, " @@ -2671,6 +2686,10 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, dispatchEntry)); traceOutboundQueueLength(connection); connection->waitQueue.push_back(dispatchEntry); + if (connection->responsive) { + mAnrTracker.insert(dispatchEntry->timeoutTime, + connection->inputChannel->getConnectionToken()); + } traceWaitQueueLength(connection); } } @@ -2705,8 +2724,6 @@ void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime, connection->getInputChannelName().c_str(), seq, toString(handled)); #endif - connection->inputPublisherBlocked = false; - if (connection->status == Connection::STATUS_BROKEN || connection->status == Connection::STATUS_ZOMBIE) { return; @@ -3862,15 +3879,17 @@ void InputDispatcher::setFocusedApplication( sp oldFocusedApplicationHandle = getValueByKey(mFocusedApplicationHandlesByDisplay, displayId); + + if (oldFocusedApplicationHandle == mAwaitedFocusedApplication && + inputApplicationHandle != oldFocusedApplicationHandle) { + resetNoFocusedWindowTimeoutLocked(); + } + if (inputApplicationHandle != nullptr && inputApplicationHandle->updateInfo()) { if (oldFocusedApplicationHandle != inputApplicationHandle) { - if (oldFocusedApplicationHandle != nullptr) { - resetAnrTimeoutsLocked(); - } mFocusedApplicationHandlesByDisplay[displayId] = inputApplicationHandle; } } else if (oldFocusedApplicationHandle != nullptr) { - resetAnrTimeoutsLocked(); oldFocusedApplicationHandle.clear(); mFocusedApplicationHandlesByDisplay.erase(displayId); } @@ -3951,7 +3970,7 @@ void InputDispatcher::setInputDispatchMode(bool enabled, bool frozen) { if (mDispatchEnabled != enabled || mDispatchFrozen != frozen) { if (mDispatchFrozen && !frozen) { - resetAnrTimeoutsLocked(); + resetNoFocusedWindowTimeoutLocked(); } if (mDispatchEnabled && !enabled) { @@ -4091,8 +4110,9 @@ void InputDispatcher::resetAndDropEverythingLocked(const char* reason) { resetKeyRepeatLocked(); releasePendingEventLocked(); drainInboundQueueLocked(); - resetAnrTimeoutsLocked(); + resetNoFocusedWindowTimeoutLocked(); + mAnrTracker.clear(); mTouchStatesByDisplay.clear(); mLastHoverWindowHandle.clear(); mReplacedKeys.clear(); @@ -4289,11 +4309,10 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { for (const auto& pair : mConnectionsByFd) { const sp& connection = pair.second; dump += StringPrintf(INDENT2 "%i: channelName='%s', windowName='%s', " - "status=%s, monitor=%s, inputPublisherBlocked=%s\n", + "status=%s, monitor=%s, responsive=%s\n", pair.first, connection->getInputChannelName().c_str(), connection->getWindowName().c_str(), connection->getStatusLabel(), - toString(connection->monitor), - toString(connection->inputPublisherBlocked)); + toString(connection->monitor), toString(connection->responsive)); if (!connection->outboundQueue.empty()) { dump += StringPrintf(INDENT3 "OutboundQueue: length=%zu\n", @@ -4561,6 +4580,7 @@ sp InputDispatcher::getConnectionLocked(const sp& inputConn } void InputDispatcher::removeConnectionLocked(const sp& connection) { + mAnrTracker.eraseToken(connection->inputChannel->getConnectionToken()); removeByValue(mConnectionsByFd, connection); } @@ -4598,17 +4618,69 @@ void InputDispatcher::onFocusChangedLocked(const sp& oldFocus postCommandLocked(std::move(commandEntry)); } -void InputDispatcher::onAnrLocked(nsecs_t currentTime, - const sp& applicationHandle, - const sp& windowHandle, nsecs_t eventTime, - nsecs_t waitStartTime, const char* reason) { - float dispatchLatency = (currentTime - eventTime) * 0.000001f; - float waitDuration = (currentTime - waitStartTime) * 0.000001f; - ALOGI("Application is not responding: %s. " - "It has been %0.1fms since event, %0.1fms since wait started. Reason: %s", - getApplicationWindowLabel(applicationHandle, windowHandle).c_str(), dispatchLatency, - waitDuration, reason); +void InputDispatcher::onAnrLocked(const sp& connection) { + // Since we are allowing the policy to extend the timeout, maybe the waitQueue + // is already healthy again. Don't raise ANR in this situation + if (connection->waitQueue.empty()) { + ALOGI("Not raising ANR because the connection %s has recovered", + connection->inputChannel->getName().c_str()); + return; + } + /** + * The "oldestEntry" is the entry that was first sent to the application. That entry, however, + * may not be the one that caused the timeout to occur. One possibility is that window timeout + * has changed. This could cause newer entries to time out before the already dispatched + * entries. In that situation, the newest entries caused ANR. But in all likelihood, the app + * processes the events linearly. So providing information about the oldest entry seems to be + * most useful. + */ + DispatchEntry* oldestEntry = *connection->waitQueue.begin(); + const nsecs_t currentWait = now() - oldestEntry->deliveryTime; + std::string reason = + android::base::StringPrintf("%s is not responding. Waited %" PRId64 "ms for %s", + connection->inputChannel->getName().c_str(), + ns2ms(currentWait), + oldestEntry->eventEntry->getDescription().c_str()); + + updateLastAnrStateLocked(getWindowHandleLocked(connection->inputChannel->getConnectionToken()), + reason); + + std::unique_ptr commandEntry = + std::make_unique(&InputDispatcher::doNotifyAnrLockedInterruptible); + commandEntry->inputApplicationHandle = nullptr; + commandEntry->inputChannel = connection->inputChannel; + commandEntry->reason = std::move(reason); + postCommandLocked(std::move(commandEntry)); +} +void InputDispatcher::onAnrLocked(const sp& application) { + std::string reason = android::base::StringPrintf("%s does not have a focused window", + application->getName().c_str()); + + updateLastAnrStateLocked(application, reason); + + std::unique_ptr commandEntry = + std::make_unique(&InputDispatcher::doNotifyAnrLockedInterruptible); + commandEntry->inputApplicationHandle = application; + commandEntry->inputChannel = nullptr; + commandEntry->reason = std::move(reason); + postCommandLocked(std::move(commandEntry)); +} + +void InputDispatcher::updateLastAnrStateLocked(const sp& window, + const std::string& reason) { + const std::string windowLabel = getApplicationWindowLabel(nullptr, window); + updateLastAnrStateLocked(windowLabel, reason); +} + +void InputDispatcher::updateLastAnrStateLocked(const sp& application, + const std::string& reason) { + const std::string windowLabel = getApplicationWindowLabel(application, nullptr); + updateLastAnrStateLocked(windowLabel, reason); +} + +void InputDispatcher::updateLastAnrStateLocked(const std::string& windowLabel, + const std::string& reason) { // Capture a record of the InputDispatcher state at the time of the ANR. time_t t = time(nullptr); struct tm tm; @@ -4618,21 +4690,9 @@ void InputDispatcher::onAnrLocked(nsecs_t currentTime, mLastAnrState.clear(); mLastAnrState += INDENT "ANR:\n"; mLastAnrState += StringPrintf(INDENT2 "Time: %s\n", timestr); - mLastAnrState += - StringPrintf(INDENT2 "Window: %s\n", - getApplicationWindowLabel(applicationHandle, windowHandle).c_str()); - mLastAnrState += StringPrintf(INDENT2 "DispatchLatency: %0.1fms\n", dispatchLatency); - mLastAnrState += StringPrintf(INDENT2 "WaitDuration: %0.1fms\n", waitDuration); - mLastAnrState += StringPrintf(INDENT2 "Reason: %s\n", reason); + mLastAnrState += StringPrintf(INDENT2 "Reason: %s\n", reason.c_str()); + mLastAnrState += StringPrintf(INDENT2 "Window: %s\n", windowLabel.c_str()); dumpDispatchStateLocked(mLastAnrState); - - std::unique_ptr commandEntry = - std::make_unique(&InputDispatcher::doNotifyAnrLockedInterruptible); - commandEntry->inputApplicationHandle = applicationHandle; - commandEntry->inputChannel = - windowHandle != nullptr ? getInputChannelLocked(windowHandle->getToken()) : nullptr; - commandEntry->reason = reason; - postCommandLocked(std::move(commandEntry)); } void InputDispatcher::doNotifyConfigurationChangedLockedInterruptible(CommandEntry* commandEntry) { @@ -4673,13 +4733,50 @@ void InputDispatcher::doNotifyAnrLockedInterruptible(CommandEntry* commandEntry) mLock.lock(); - resumeAfterTargetsNotReadyTimeoutLocked(timeoutExtension, token); + if (timeoutExtension > 0) { + extendAnrTimeoutsLocked(commandEntry->inputApplicationHandle, token, timeoutExtension); + } else { + // stop waking up for events in this connection, it is already not responding + sp connection = getConnectionLocked(token); + if (connection == nullptr) { + return; + } + cancelEventsForAnrLocked(connection); + } +} + +void InputDispatcher::extendAnrTimeoutsLocked(const sp& application, + const sp& connectionToken, + nsecs_t timeoutExtension) { + sp connection = getConnectionLocked(connectionToken); + if (connection == nullptr) { + if (mNoFocusedWindowTimeoutTime.has_value() && application != nullptr) { + // Maybe ANR happened because there's no focused window? + mNoFocusedWindowTimeoutTime = now() + timeoutExtension; + mAwaitedFocusedApplication = application; + } else { + // It's also possible that the connection already disappeared. No action necessary. + } + return; + } + + ALOGI("Raised ANR, but the policy wants to keep waiting on %s for %" PRId64 "ms longer", + connection->inputChannel->getName().c_str(), ns2ms(timeoutExtension)); + + connection->responsive = true; + const nsecs_t newTimeout = now() + timeoutExtension; + for (DispatchEntry* entry : connection->waitQueue) { + if (newTimeout >= entry->timeoutTime) { + // Already removed old entries when connection was marked unresponsive + entry->timeoutTime = newTimeout; + mAnrTracker.insert(entry->timeoutTime, connectionToken); + } + } } void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible( CommandEntry* commandEntry) { KeyEntry* entry = commandEntry->keyEntry; - KeyEvent event = createKeyEvent(*entry); mLock.unlock(); @@ -4713,6 +4810,20 @@ void InputDispatcher::doOnPointerDownOutsideFocusLockedInterruptible(CommandEntr mLock.lock(); } +/** + * Connection is responsive if it has no events in the waitQueue that are older than the + * current time. + */ +static bool isConnectionResponsive(const Connection& connection) { + const nsecs_t currentTime = now(); + for (const DispatchEntry* entry : connection.waitQueue) { + if (entry->timeoutTime < currentTime) { + return false; + } + } + return true; +} + void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(CommandEntry* commandEntry) { sp connection = commandEntry->connection; const nsecs_t finishTime = commandEntry->eventTime; @@ -4725,7 +4836,6 @@ void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(CommandEntry* c return; } DispatchEntry* dispatchEntry = *dispatchEntryIt; - const nsecs_t eventDuration = finishTime - dispatchEntry->deliveryTime; if (eventDuration > SLOW_EVENT_PROCESSING_WARNING_TIMEOUT) { ALOGI("%s spent %" PRId64 "ms processing %s", connection->getWindowName().c_str(), @@ -4754,6 +4864,11 @@ void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(CommandEntry* c if (dispatchEntryIt != connection->waitQueue.end()) { dispatchEntry = *dispatchEntryIt; connection->waitQueue.erase(dispatchEntryIt); + mAnrTracker.erase(dispatchEntry->timeoutTime, + connection->inputChannel->getConnectionToken()); + if (!connection->responsive) { + connection->responsive = isConnectionResponsive(*connection); + } traceWaitQueueLength(connection); if (restartEvent && connection->status == Connection::STATUS_NORMAL) { connection->outboundQueue.push_front(dispatchEntry); diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index 1980435b5a..7c2028ab29 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -17,6 +17,7 @@ #ifndef _UI_INPUT_DISPATCHER_H #define _UI_INPUT_DISPATCHER_H +#include "AnrTracker.h" #include "CancelationOptions.h" #include "Entry.h" #include "InjectionState.h" @@ -216,7 +217,6 @@ private: std::optional findGestureMonitorDisplayByTokenLocked(const sp& token) REQUIRES(mLock); - // Input channels that will receive a copy of all input events sent to the provided display. std::unordered_map> mGlobalMonitorsByDisplay GUARDED_BY(mLock); @@ -274,6 +274,9 @@ private: bool runCommandsLockedInterruptible() REQUIRES(mLock); void postCommandLocked(std::unique_ptr commandEntry) REQUIRES(mLock); + nsecs_t processAnrsLocked() REQUIRES(mLock); + nsecs_t getDispatchingTimeoutLocked(const sp& token) REQUIRES(mLock); + // Input filter processing. bool shouldSendKeyToInputFilterLocked(const NotifyKeyArgs* args) REQUIRES(mLock); bool shouldSendMotionToInputFilterLocked(const NotifyMotionArgs* args) REQUIRES(mLock); @@ -344,38 +347,53 @@ private: void logOutboundKeyDetails(const char* prefix, const KeyEntry& entry); void logOutboundMotionDetails(const char* prefix, const MotionEntry& entry); - // Keeping track of ANR timeouts. - enum InputTargetWaitCause { - INPUT_TARGET_WAIT_CAUSE_NONE, - INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY, - INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY, - }; - - InputTargetWaitCause mInputTargetWaitCause GUARDED_BY(mLock); - nsecs_t mInputTargetWaitStartTime GUARDED_BY(mLock); - nsecs_t mInputTargetWaitTimeoutTime GUARDED_BY(mLock); - bool mInputTargetWaitTimeoutExpired GUARDED_BY(mLock); - sp mInputTargetWaitApplicationToken GUARDED_BY(mLock); + /** + * This field is set if there is no focused window, and we have an event that requires + * a focused window to be dispatched (for example, a KeyEvent). + * When this happens, we will wait until *mNoFocusedWindowTimeoutTime before + * dropping the event and raising an ANR for that application. + * This is useful if an application is slow to add a focused window. + */ + std::optional mNoFocusedWindowTimeoutTime GUARDED_BY(mLock); bool shouldPruneInboundQueueLocked(const MotionEntry& motionEntry) REQUIRES(mLock); - // Contains the last window which received a hover event. - sp mLastHoverWindowHandle GUARDED_BY(mLock); + /** + * Time to stop waiting for the events to be processed while trying to dispatch a key. + * When this time expires, we just send the pending key event to the currently focused window, + * without waiting on other events to be processed first. + */ + std::optional mKeyIsWaitingForEventsTimeout GUARDED_BY(mLock); + bool shouldWaitToSendKeyLocked(nsecs_t currentTime, const char* focusedWindowName) + REQUIRES(mLock); - // Finding targets for input events. - int32_t handleTargetsNotReadyLocked(nsecs_t currentTime, const EventEntry& entry, - const sp& applicationHandle, - const sp& windowHandle, - nsecs_t* nextWakeupTime, const char* reason) + /** + * The focused application at the time when no focused window was present. + * Used to raise an ANR when we have no focused window. + */ + sp mAwaitedFocusedApplication GUARDED_BY(mLock); + + // Optimization: AnrTracker is used to quickly find which connection is due for a timeout next. + // AnrTracker must be kept in-sync with all responsive connection.waitQueues. + // If a connection is not responsive, then the entries should not be added to the AnrTracker. + // Once a connection becomes unresponsive, its entries are removed from AnrTracker to + // prevent unneeded wakeups. + AnrTracker mAnrTracker GUARDED_BY(mLock); + void extendAnrTimeoutsLocked(const sp& application, + const sp& connectionToken, nsecs_t timeoutExtension) REQUIRES(mLock); - void removeWindowByTokenLocked(const sp& token) REQUIRES(mLock); + // Contains the last window which received a hover event. + sp mLastHoverWindowHandle GUARDED_BY(mLock); - void resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout, - const sp& inputConnectionToken) - REQUIRES(mLock); + void cancelEventsForAnrLocked(const sp& connection) REQUIRES(mLock); nsecs_t getTimeSpentWaitingForApplicationLocked(nsecs_t currentTime) REQUIRES(mLock); - void resetAnrTimeoutsLocked() REQUIRES(mLock); + // If a focused application changes, we should stop counting down the "no focused window" time, + // because we will have no way of knowing when the previous application actually added a window. + // This also means that we will miss cases like pulling down notification shade when the + // focused application does not have a focused window (no ANR will be raised if notification + // shade is pulled down while we are counting down the timeout). + void resetNoFocusedWindowTimeoutLocked() REQUIRES(mLock); int32_t getTargetDisplayId(const EventEntry& entry); int32_t findFocusedWindowTargetsLocked(nsecs_t currentTime, const EventEntry& entry, @@ -388,6 +406,8 @@ private: std::vector findTouchedGestureMonitorsLocked( int32_t displayId, const std::vector>& portalWindows) const REQUIRES(mLock); + std::vector selectResponsiveMonitorsLocked( + const std::vector& gestureMonitors) const REQUIRES(mLock); void addWindowTargetLocked(const sp& windowHandle, int32_t targetFlags, BitSet32 pointerIds, std::vector& inputTargets) @@ -406,11 +426,6 @@ private: std::string getApplicationWindowLabel(const sp& applicationHandle, const sp& windowHandle); - std::string checkWindowReadyForMoreInputLocked(nsecs_t currentTime, - const sp& windowHandle, - const EventEntry& eventEntry, - const char* targetType) REQUIRES(mLock); - // Manage the dispatch cycle for a single connection. // These methods are deliberately not Interruptible because doing all of the work // with the mutex held makes it easier to ensure that connection invariants are maintained. @@ -480,9 +495,14 @@ private: REQUIRES(mLock); void onFocusChangedLocked(const sp& oldFocus, const sp& newFocus) REQUIRES(mLock); - void onAnrLocked(nsecs_t currentTime, const sp& applicationHandle, - const sp& windowHandle, nsecs_t eventTime, - nsecs_t waitStartTime, const char* reason) REQUIRES(mLock); + void onAnrLocked(const sp& connection) REQUIRES(mLock); + void onAnrLocked(const sp& application) REQUIRES(mLock); + void updateLastAnrStateLocked(const sp& window, const std::string& reason) + REQUIRES(mLock); + void updateLastAnrStateLocked(const sp& application, + const std::string& reason) REQUIRES(mLock); + void updateLastAnrStateLocked(const std::string& windowLabel, const std::string& reason) + REQUIRES(mLock); // Outbound policy interactions. void doNotifyConfigurationChangedLockedInterruptible(CommandEntry* commandEntry) diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp index 73d22727f0..a0d2f4f172 100644 --- a/services/inputflinger/tests/Android.bp +++ b/services/inputflinger/tests/Android.bp @@ -27,6 +27,7 @@ cc_test { "libinputflinger_defaults", ], srcs: [ + "AnrTracker_test.cpp", "BlockingQueue_test.cpp", "EventHub_test.cpp", "TestInputListener.cpp", diff --git a/services/inputflinger/tests/AnrTracker_test.cpp b/services/inputflinger/tests/AnrTracker_test.cpp new file mode 100644 index 0000000000..b561da107d --- /dev/null +++ b/services/inputflinger/tests/AnrTracker_test.cpp @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "../AnrTracker.h" + +#include +#include + +namespace android { + +namespace inputdispatcher { + +// --- AnrTrackerTest --- + +/** + * Add a single entry and ensure it's returned as first, even if the token isn't valid + */ +TEST(AnrTrackerTest, SingleEntry_First) { + AnrTracker tracker; + + tracker.insert(1, nullptr); + + ASSERT_EQ(1, tracker.firstTimeout()); + ASSERT_EQ(tracker.firstToken(), nullptr); +} + +TEST(AnrTrackerTest, MultipleEntries_RemoveToken) { + AnrTracker tracker; + + sp token1 = new BBinder(); + sp token2 = new BBinder(); + + tracker.insert(1, token1); + tracker.insert(2, token2); + tracker.insert(3, token1); + tracker.insert(4, token2); + tracker.insert(5, token1); + + tracker.eraseToken(token1); + + ASSERT_EQ(2, tracker.firstTimeout()); +} + +TEST(AnrTrackerTest, AddAndRemove_Empty) { + AnrTracker tracker; + + ASSERT_TRUE(tracker.empty()); + + tracker.insert(1, nullptr); + ASSERT_FALSE(tracker.empty()); + + tracker.erase(1, nullptr); + ASSERT_TRUE(tracker.empty()); +} + +TEST(AnrTrackerTest, Clear) { + AnrTracker tracker; + + tracker.insert(1, nullptr); + tracker.clear(); + ASSERT_TRUE(tracker.empty()); +} + +TEST(AnrTrackerTest, SingleToken_MaintainsOrder) { + AnrTracker tracker; + + ASSERT_TRUE(tracker.empty()); + + tracker.insert(2, nullptr); + tracker.insert(5, nullptr); + tracker.insert(0, nullptr); + + ASSERT_EQ(0, tracker.firstTimeout()); + ASSERT_EQ(nullptr, tracker.firstToken()); +} + +TEST(AnrTrackerTest, MultipleTokens_MaintainsOrder) { + AnrTracker tracker; + + sp token1 = new BBinder(); + sp token2 = new BBinder(); + + tracker.insert(2, token1); + tracker.insert(5, token2); + tracker.insert(0, token2); + + ASSERT_EQ(0, tracker.firstTimeout()); + ASSERT_EQ(token2, tracker.firstToken()); +} + +TEST(AnrTrackerTest, MultipleTokens_IdenticalTimes) { + AnrTracker tracker; + + sp token1 = new BBinder(); + sp token2 = new BBinder(); + + tracker.insert(2, token1); + tracker.insert(2, token2); + tracker.insert(10, token2); + + ASSERT_EQ(2, tracker.firstTimeout()); + // Doesn't matter which token is returned - both are valid results + ASSERT_TRUE(token1 == tracker.firstToken() || token2 == tracker.firstToken()); +} + +TEST(AnrTrackerTest, MultipleTokens_IdenticalTimesRemove) { + AnrTracker tracker; + + sp token1 = new BBinder(); + sp token2 = new BBinder(); + + tracker.insert(2, token1); + tracker.insert(2, token2); + tracker.insert(10, token2); + + tracker.erase(2, token2); + + ASSERT_EQ(2, tracker.firstTimeout()); + ASSERT_EQ(token1, tracker.firstToken()); +} + +TEST(AnrTrackerTest, Empty_DoesntCrash) { + AnrTracker tracker; + + ASSERT_TRUE(tracker.empty()); + + ASSERT_EQ(LONG_LONG_MAX, tracker.firstTimeout()); + // Can't call firstToken() if tracker.empty() +} + +TEST(AnrTrackerTest, RemoveInvalidItem_DoesntCrash) { + AnrTracker tracker; + + tracker.insert(1, nullptr); + + // Remove with non-matching timestamp + tracker.erase(2, nullptr); + ASSERT_EQ(1, tracker.firstTimeout()); + ASSERT_EQ(nullptr, tracker.firstToken()); + + // Remove with non-matching token + tracker.erase(1, new BBinder()); + ASSERT_EQ(1, tracker.firstTimeout()); + ASSERT_EQ(nullptr, tracker.firstToken()); + + // Remove with both non-matching + tracker.erase(2, new BBinder()); + ASSERT_EQ(1, tracker.firstTimeout()); + ASSERT_EQ(nullptr, tracker.firstToken()); +} + +} // namespace inputdispatcher + +} // namespace android diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 13e835427f..1a133dc01c 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -126,6 +126,14 @@ public: void assertNotifyAnrWasCalled(std::chrono::nanoseconds timeout, const sp& expectedApplication, const sp& expectedToken) { + std::pair, sp> anrData; + ASSERT_NO_FATAL_FAILURE(anrData = getNotifyAnrData(timeout)); + ASSERT_EQ(expectedApplication, anrData.first); + ASSERT_EQ(expectedToken, anrData.second); + } + + std::pair, sp> getNotifyAnrData( + std::chrono::nanoseconds timeout) { const std::chrono::time_point start = std::chrono::steady_clock::now(); std::unique_lock lock(mLock); std::chrono::duration timeToWait = timeout + 100ms; // provide some slack @@ -136,16 +144,33 @@ public: // before checking if ANR was called. // Since dispatcher is not guaranteed to call notifyAnr right away, we need to provide // it some time to act. 100ms seems reasonable. - mNotifyAnr.wait_for(lock, timeToWait, - [this]() REQUIRES(mLock) { return mNotifyAnrWasCalled; }); + mNotifyAnr.wait_for(lock, timeToWait, [this]() REQUIRES(mLock) { + return !mAnrApplications.empty() && !mAnrWindowTokens.empty(); + }); const std::chrono::duration waited = std::chrono::steady_clock::now() - start; - ASSERT_TRUE(mNotifyAnrWasCalled); + if (mAnrApplications.empty() || mAnrWindowTokens.empty()) { + ADD_FAILURE() << "Did not receive ANR callback"; + } // Ensure that the ANR didn't get raised too early. We can't be too strict here because // the dispatcher started counting before this function was called - ASSERT_TRUE(timeout - 100ms < waited); // check (waited < timeout + 100ms) done by wait_for - mNotifyAnrWasCalled = false; - ASSERT_EQ(expectedApplication, mLastAnrApplication); - ASSERT_EQ(expectedToken, mLastAnrWindowToken); + if (std::chrono::abs(timeout - waited) > 100ms) { + ADD_FAILURE() << "ANR was raised too early or too late. Expected " + << std::chrono::duration_cast(timeout).count() + << "ms, but waited " + << std::chrono::duration_cast(waited).count() + << "ms instead"; + } + std::pair, sp> result = + std::make_pair(mAnrApplications.front(), mAnrWindowTokens.front()); + mAnrApplications.pop(); + mAnrWindowTokens.pop(); + return result; + } + + void assertNotifyAnrWasNotCalled() { + std::scoped_lock lock(mLock); + ASSERT_TRUE(mAnrApplications.empty()); + ASSERT_TRUE(mAnrWindowTokens.empty()); } void setKeyRepeatConfiguration(nsecs_t timeout, nsecs_t delay) { @@ -153,6 +178,8 @@ public: mConfig.keyRepeatDelay = delay; } + void setAnrTimeout(std::chrono::nanoseconds timeout) { mAnrTimeout = timeout; } + private: std::mutex mLock; std::unique_ptr mFilteredEvent GUARDED_BY(mLock); @@ -161,9 +188,8 @@ private: std::optional mLastNotifySwitch GUARDED_BY(mLock); // ANR handling - bool mNotifyAnrWasCalled GUARDED_BY(mLock) = false; - sp mLastAnrApplication GUARDED_BY(mLock); - sp mLastAnrWindowToken GUARDED_BY(mLock); + std::queue> mAnrApplications GUARDED_BY(mLock); + std::queue> mAnrWindowTokens GUARDED_BY(mLock); std::condition_variable mNotifyAnr; std::chrono::nanoseconds mAnrTimeout = 0ms; @@ -175,9 +201,8 @@ private: virtual nsecs_t notifyAnr(const sp& application, const sp& windowToken, const std::string&) override { std::scoped_lock lock(mLock); - mLastAnrApplication = application; - mLastAnrWindowToken = windowToken; - mNotifyAnrWasCalled = true; + mAnrApplications.push(application); + mAnrWindowTokens.push(windowToken); mNotifyAnr.notify_all(); return mAnrTimeout.count(); } @@ -643,7 +668,7 @@ public: ASSERT_NE(nullptr, event) << mName.c_str() << ": consumer should have returned non-NULL event."; ASSERT_EQ(expectedEventType, event->getType()) - << mName.c_str() << "expected " << inputEventTypeToString(expectedEventType) + << mName.c_str() << " expected " << inputEventTypeToString(expectedEventType) << " event, got " << inputEventTypeToString(event->getType()) << " event"; EXPECT_EQ(expectedDisplayId, event->getDisplayId()); @@ -688,9 +713,24 @@ public: void assertNoEvents() { InputEvent* event = consume(); - ASSERT_EQ(nullptr, event) - << mName.c_str() - << ": should not have received any events, so consume() should return NULL"; + if (event == nullptr) { + return; + } + if (event->getType() == AINPUT_EVENT_TYPE_KEY) { + KeyEvent& keyEvent = static_cast(*event); + ADD_FAILURE() << "Received key event " + << KeyEvent::actionToString(keyEvent.getAction()); + } else if (event->getType() == AINPUT_EVENT_TYPE_MOTION) { + MotionEvent& motionEvent = static_cast(*event); + ADD_FAILURE() << "Received motion event " + << MotionEvent::actionToString(motionEvent.getAction()); + } else if (event->getType() == AINPUT_EVENT_TYPE_FOCUS) { + FocusEvent& focusEvent = static_cast(*event); + ADD_FAILURE() << "Received focus event, hasFocus = " + << (focusEvent.getHasFocus() ? "true" : "false"); + } + FAIL() << mName.c_str() + << ": should not have received any events, so consume() should return NULL"; } sp getToken() { return mConsumer->getChannel()->getConnectionToken(); } @@ -754,6 +794,8 @@ public: mInfo.dispatchingTimeout = timeout.count(); } + void setPaused(bool paused) { mInfo.paused = paused; } + void setFrame(const Rect& frame) { mInfo.frameLeft = frame.left; mInfo.frameTop = frame.top; @@ -775,6 +817,10 @@ public: expectedFlags); } + void consumeKeyUp(int32_t expectedDisplayId, int32_t expectedFlags = 0) { + consumeEvent(AINPUT_EVENT_TYPE_KEY, AKEY_EVENT_ACTION_UP, expectedDisplayId, expectedFlags); + } + void consumeMotionCancel(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT, int32_t expectedFlags = 0) { consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_CANCEL, expectedDisplayId, @@ -826,12 +872,12 @@ public: expectedFlags); } - std::optional receiveEvent() { + std::optional receiveEvent(InputEvent** outEvent = nullptr) { if (mInputReceiver == nullptr) { ADD_FAILURE() << "Invalid receive event on window with no receiver"; return std::nullopt; } - return mInputReceiver->receiveEvent(); + return mInputReceiver->receiveEvent(outEvent); } void finishEvent(uint32_t sequenceNum) { @@ -865,7 +911,9 @@ private: std::atomic FakeWindowHandle::sId{1}; static int32_t injectKey(const sp& dispatcher, int32_t action, int32_t repeatCount, - int32_t displayId = ADISPLAY_ID_NONE) { + int32_t displayId = ADISPLAY_ID_NONE, + int32_t syncMode = INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, + std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT) { KeyEvent event; nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); @@ -875,10 +923,9 @@ static int32_t injectKey(const sp& dispatcher, int32_t action, repeatCount, currentTime, currentTime); // Inject event until dispatch out. - return dispatcher->injectInputEvent( - &event, - INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, - INJECT_EVENT_TIMEOUT, POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER); + return dispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, syncMode, + injectionTimeout, + POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER); } static int32_t injectKeyDown(const sp& dispatcher, @@ -886,11 +933,19 @@ static int32_t injectKeyDown(const sp& dispatcher, return injectKey(dispatcher, AKEY_EVENT_ACTION_DOWN, /* repeatCount */ 0, displayId); } +static int32_t injectKeyUp(const sp& dispatcher, + int32_t displayId = ADISPLAY_ID_NONE) { + return injectKey(dispatcher, AKEY_EVENT_ACTION_UP, /* repeatCount */ 0, displayId); +} + static int32_t injectMotionEvent( const sp& dispatcher, int32_t action, int32_t source, int32_t displayId, const PointF& position, const PointF& cursorPosition = {AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION}) { + AMOTION_EVENT_INVALID_CURSOR_POSITION}, + std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT, + int32_t injectionMode = INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, + nsecs_t eventTime = systemTime(SYSTEM_TIME_MONOTONIC)) { MotionEvent event; PointerProperties pointerProperties[1]; PointerCoords pointerCoords[1]; @@ -903,7 +958,6 @@ static int32_t injectMotionEvent( pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, position.x); pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, position.y); - nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); // Define a valid motion down event. event.initialize(InputEvent::nextId(), DEVICE_ID, source, displayId, INVALID_HMAC, action, /* actionButton */ 0, @@ -911,14 +965,13 @@ static int32_t injectMotionEvent( /* edgeFlags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE, /* xScale */ 1, /* yScale */ 1, /* xOffset */ 0, /* yOffset */ 0, /* xPrecision */ 0, /* yPrecision */ 0, cursorPosition.x, cursorPosition.y, - currentTime, currentTime, + eventTime, eventTime, /*pointerCount*/ 1, pointerProperties, pointerCoords); // Inject event until dispatch out. - return dispatcher->injectInputEvent( - &event, - INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, - INJECT_EVENT_TIMEOUT, POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER); + return dispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, injectionMode, + injectionTimeout, + POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER); } static int32_t injectMotionDown(const sp& dispatcher, int32_t source, @@ -1429,6 +1482,10 @@ public: expectedDisplayId, expectedFlags); } + std::optional receiveEvent() { return mInputReceiver->receiveEvent(); } + + void finishEvent(uint32_t consumeSeq) { return mInputReceiver->finishEvent(consumeSeq); } + void consumeMotionDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) { mInputReceiver->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_DOWN, expectedDisplayId, expectedFlags); @@ -1507,6 +1564,21 @@ TEST_F(InputDispatcherTest, GestureMonitor_CanPilferAfterWindowIsRemovedMidStrea monitor.consumeMotionUp(ADISPLAY_ID_DEFAULT); } +TEST_F(InputDispatcherTest, UnresponsiveGestureMonitor_GetsAnr) { + FakeMonitorReceiver monitor = + FakeMonitorReceiver(mDispatcher, "Gesture monitor", ADISPLAY_ID_DEFAULT, + true /*isGestureMonitor*/); + + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT)); + std::optional consumeSeq = monitor.receiveEvent(); + ASSERT_TRUE(consumeSeq); + + mFakePolicy->assertNotifyAnrWasCalled(DISPATCHING_TIMEOUT, nullptr, monitor.getToken()); + monitor.finishEvent(*consumeSeq); + ASSERT_TRUE(mDispatcher->waitForIdle()); +} + TEST_F(InputDispatcherTest, TestMoveEvent) { sp application = new FakeApplicationHandle(); sp window = @@ -2329,23 +2401,40 @@ protected: } }; +// Send a tap and respond, which should not cause an ANR. +TEST_F(InputDispatcherSingleWindowAnr, WhenTouchIsConsumed_NoAnr) { + tapOnWindow(); + mWindow->consumeMotionDown(); + mWindow->consumeMotionUp(); + ASSERT_TRUE(mDispatcher->waitForIdle()); + mFakePolicy->assertNotifyAnrWasNotCalled(); +} + +// Send a regular key and respond, which should not cause an ANR. +TEST_F(InputDispatcherSingleWindowAnr, WhenKeyIsConsumed_NoAnr) { + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher)); + mWindow->consumeKeyDown(ADISPLAY_ID_NONE); + ASSERT_TRUE(mDispatcher->waitForIdle()); + mFakePolicy->assertNotifyAnrWasNotCalled(); +} + // Send an event to the app and have the app not respond right away. -// Make sure that ANR is raised +// When ANR is raised, policy will tell the dispatcher to cancel the events for that window. +// So InputDispatcher will enqueue ACTION_CANCEL event as well. TEST_F(InputDispatcherSingleWindowAnr, OnPointerDown_BasicAnr) { ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, WINDOW_LOCATION)); - // Also, overwhelm the socket to make sure ANR starts - for (size_t i = 0; i < 100; i++) { - injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, - ADISPLAY_ID_DEFAULT, {WINDOW_LOCATION.x, WINDOW_LOCATION.y + i}); - } - std::optional sequenceNum = mWindow->receiveEvent(); // ACTION_DOWN ASSERT_TRUE(sequenceNum); const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT); mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, mWindow->getToken()); + + // The remaining lines are not really needed for the test, but kept as a sanity check + mWindow->finishEvent(*sequenceNum); + mWindow->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_CANCEL, + ADISPLAY_ID_DEFAULT, 0 /*flags*/); ASSERT_TRUE(mDispatcher->waitForIdle()); } @@ -2355,14 +2444,591 @@ TEST_F(InputDispatcherSingleWindowAnr, OnKeyDown_BasicAnr) { ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher)); std::optional sequenceNum = mWindow->receiveEvent(); ASSERT_TRUE(sequenceNum); + const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT); + mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, mWindow->getToken()); + ASSERT_TRUE(mDispatcher->waitForIdle()); +} + +// We have a focused application, but no focused window +TEST_F(InputDispatcherSingleWindowAnr, FocusedApplication_NoFocusedWindow) { + mWindow->setFocus(false); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}}); + mWindow->consumeFocusEvent(false); + + // taps on the window work as normal + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + WINDOW_LOCATION)); + ASSERT_NO_FATAL_FAILURE(mWindow->consumeMotionDown()); + mDispatcher->waitForIdle(); + mFakePolicy->assertNotifyAnrWasNotCalled(); + + // Once a focused event arrives, we get an ANR for this application + // We specify the injection timeout to be smaller than the application timeout, to ensure that + // injection times out (instead of failing). + const int32_t result = + injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */, ADISPLAY_ID_DEFAULT, + INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, 10ms); + ASSERT_EQ(INPUT_EVENT_INJECTION_TIMED_OUT, result); + const std::chrono::duration timeout = mApplication->getDispatchingTimeout(DISPATCHING_TIMEOUT); + mFakePolicy->assertNotifyAnrWasCalled(timeout, mApplication, nullptr /*windowToken*/); + ASSERT_TRUE(mDispatcher->waitForIdle()); +} + +// We have a focused application, but no focused window +// If the policy wants to keep waiting on the focused window to be added, make sure +// that this timeout extension is honored and ANR is raised again. +TEST_F(InputDispatcherSingleWindowAnr, NoFocusedWindow_ExtendsAnr) { + mWindow->setFocus(false); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}}); + mWindow->consumeFocusEvent(false); + const std::chrono::duration timeout = 5ms; + mFakePolicy->setAnrTimeout(timeout); + + // Once a focused event arrives, we get an ANR for this application + // We specify the injection timeout to be smaller than the application timeout, to ensure that + // injection times out (instead of failing). + const int32_t result = + injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */, ADISPLAY_ID_DEFAULT, + INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, 10ms); + ASSERT_EQ(INPUT_EVENT_INJECTION_TIMED_OUT, result); + const std::chrono::duration appTimeout = + mApplication->getDispatchingTimeout(DISPATCHING_TIMEOUT); + mFakePolicy->assertNotifyAnrWasCalled(appTimeout, mApplication, nullptr /*windowToken*/); + + // After the extended time has passed, ANR should be raised again + mFakePolicy->assertNotifyAnrWasCalled(timeout, mApplication, nullptr /*windowToken*/); + + // If we stop extending the timeout, dispatcher should go to idle. + // Another ANR may be raised during this time + mFakePolicy->setAnrTimeout(0ms); + ASSERT_TRUE(mDispatcher->waitForIdle()); +} + +// We have a focused application, but no focused window +TEST_F(InputDispatcherSingleWindowAnr, NoFocusedWindow_DropsFocusedEvents) { + mWindow->setFocus(false); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}}); + mWindow->consumeFocusEvent(false); + + // Once a focused event arrives, we get an ANR for this application + const int32_t result = + injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */, ADISPLAY_ID_DEFAULT, + INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, 10ms); + ASSERT_EQ(INPUT_EVENT_INJECTION_TIMED_OUT, result); - // Start ANR process by sending a 2nd key, which would trigger the check for whether - // waitQueue is empty - injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, /* repeatCount */ 1); + const std::chrono::duration timeout = mApplication->getDispatchingTimeout(DISPATCHING_TIMEOUT); + mFakePolicy->assertNotifyAnrWasCalled(timeout, mApplication, nullptr /*windowToken*/); + // Future focused events get dropped right away + ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, injectKeyDown(mDispatcher)); + ASSERT_TRUE(mDispatcher->waitForIdle()); + mWindow->assertNoEvents(); +} + +/** + * Ensure that the implementation is valid. Since we are using multiset to keep track of the + * ANR timeouts, we are allowing entries with identical timestamps in the same connection. + * If we process 1 of the events, but ANR on the second event with the same timestamp, + * the ANR mechanism should still work. + * + * In this test, we are injecting DOWN and UP events with the same timestamps, and acknowledging the + * DOWN event, while not responding on the second one. + */ +TEST_F(InputDispatcherSingleWindowAnr, Anr_HandlesEventsWithIdenticalTimestamps) { + nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); + injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, WINDOW_LOCATION, + {AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION}, + 500ms, INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, currentTime); + + // Now send ACTION_UP, with identical timestamp + injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, WINDOW_LOCATION, + {AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION}, + 500ms, INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, currentTime); + + // We have now sent down and up. Let's consume first event and then ANR on the second. + mWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT); + const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT); + mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, mWindow->getToken()); +} + +// If an app is not responding to a key event, gesture monitors should continue to receive +// new motion events +TEST_F(InputDispatcherSingleWindowAnr, GestureMonitors_ReceiveEventsDuringAppAnrOnKey) { + FakeMonitorReceiver monitor = + FakeMonitorReceiver(mDispatcher, "Gesture monitor", ADISPLAY_ID_DEFAULT, + true /*isGestureMonitor*/); + + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT)); + mWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT); + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyUp(mDispatcher, ADISPLAY_ID_DEFAULT)); + + // Stuck on the ACTION_UP + const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT); + mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr, mWindow->getToken()); + + // New tap will go to the gesture monitor, but not to the window + tapOnWindow(); + monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT); + monitor.consumeMotionUp(ADISPLAY_ID_DEFAULT); + + mWindow->consumeKeyUp(ADISPLAY_ID_DEFAULT); // still the previous motion + mDispatcher->waitForIdle(); + mWindow->assertNoEvents(); + monitor.assertNoEvents(); +} + +// If an app is not responding to a motion event, gesture monitors should continue to receive +// new motion events +TEST_F(InputDispatcherSingleWindowAnr, GestureMonitors_ReceiveEventsDuringAppAnrOnMotion) { + FakeMonitorReceiver monitor = + FakeMonitorReceiver(mDispatcher, "Gesture monitor", ADISPLAY_ID_DEFAULT, + true /*isGestureMonitor*/); + + tapOnWindow(); + monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT); + monitor.consumeMotionUp(ADISPLAY_ID_DEFAULT); + + mWindow->consumeMotionDown(); + // Stuck on the ACTION_UP const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT); - mFakePolicy->assertNotifyAnrWasCalled(timeout, mApplication, mWindow->getToken()); + mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr, mWindow->getToken()); + + // New tap will go to the gesture monitor, but not to the window + tapOnWindow(); + monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT); + monitor.consumeMotionUp(ADISPLAY_ID_DEFAULT); + + mWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT); // still the previous motion + mDispatcher->waitForIdle(); + mWindow->assertNoEvents(); + monitor.assertNoEvents(); +} + +// If a window is unresponsive, then you get anr. if the window later catches up and starts to +// process events, you don't get an anr. When the window later becomes unresponsive again, you +// get an ANR again. +// 1. tap -> block on ACTION_UP -> receive ANR +// 2. consume all pending events (= queue becomes healthy again) +// 3. tap again -> block on ACTION_UP again -> receive ANR second time +TEST_F(InputDispatcherSingleWindowAnr, SameWindow_CanReceiveAnrTwice) { + tapOnWindow(); + + mWindow->consumeMotionDown(); + // Block on ACTION_UP + const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT); + mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, mWindow->getToken()); + mWindow->consumeMotionUp(); // Now the connection should be healthy again + mDispatcher->waitForIdle(); + mWindow->assertNoEvents(); + + tapOnWindow(); + mWindow->consumeMotionDown(); + mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, mWindow->getToken()); + mWindow->consumeMotionUp(); + + mDispatcher->waitForIdle(); + mWindow->assertNoEvents(); +} + +// If the policy tells us to raise ANR again after some time, ensure that the timeout extension +// is honored +TEST_F(InputDispatcherSingleWindowAnr, Policy_CanExtendTimeout) { + const std::chrono::duration timeout = 5ms; + mFakePolicy->setAnrTimeout(timeout); + + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + WINDOW_LOCATION)); + + const std::chrono::duration windowTimeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT); + mFakePolicy->assertNotifyAnrWasCalled(windowTimeout, nullptr /*application*/, + mWindow->getToken()); + + // Since the policy wanted to extend ANR, make sure it is called again after the extension + mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, mWindow->getToken()); + mFakePolicy->setAnrTimeout(0ms); + std::this_thread::sleep_for(windowTimeout); + // We are not checking if ANR has been called, because it may have been called again by the + // time we set the timeout to 0 + + // When the policy finally says stop, we should get ACTION_CANCEL + mWindow->consumeMotionDown(); + mWindow->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_CANCEL, + ADISPLAY_ID_DEFAULT, 0 /*flags*/); + mWindow->assertNoEvents(); +} + +/** + * If a window is processing a motion event, and then a key event comes in, the key event should + * not to to the focused window until the motion is processed. + * + * Warning!!! + * This test depends on the value of android::inputdispatcher::KEY_WAITING_FOR_MOTION_TIMEOUT + * and the injection timeout that we specify when injecting the key. + * We must have the injection timeout (10ms) be smaller than + * KEY_WAITING_FOR_MOTION_TIMEOUT (currently 500ms). + * + * If that value changes, this test should also change. + */ +TEST_F(InputDispatcherSingleWindowAnr, Key_StaysPendingWhileMotionIsProcessed) { + mWindow->setDispatchingTimeout(2s); // Set a long ANR timeout to prevent it from triggering + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}}); + + tapOnWindow(); + std::optional downSequenceNum = mWindow->receiveEvent(); + ASSERT_TRUE(downSequenceNum); + std::optional upSequenceNum = mWindow->receiveEvent(); + ASSERT_TRUE(upSequenceNum); + // Don't finish the events yet, and send a key + // Injection will "succeed" because we will eventually give up and send the key to the focused + // window even if motions are still being processed. But because the injection timeout is short, + // we will receive INJECTION_TIMED_OUT as the result. + + int32_t result = + injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */, ADISPLAY_ID_DEFAULT, + INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, 10ms); + ASSERT_EQ(INPUT_EVENT_INJECTION_TIMED_OUT, result); + // Key will not be sent to the window, yet, because the window is still processing events + // and the key remains pending, waiting for the touch events to be processed + std::optional keySequenceNum = mWindow->receiveEvent(); + ASSERT_FALSE(keySequenceNum); + + std::this_thread::sleep_for(500ms); + // if we wait long enough though, dispatcher will give up, and still send the key + // to the focused window, even though we have not yet finished the motion event + mWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT); + mWindow->finishEvent(*downSequenceNum); + mWindow->finishEvent(*upSequenceNum); +} + +/** + * If a window is processing a motion event, and then a key event comes in, the key event should + * not go to the focused window until the motion is processed. + * If then a new motion comes in, then the pending key event should be going to the currently + * focused window right away. + */ +TEST_F(InputDispatcherSingleWindowAnr, + PendingKey_IsDroppedWhileMotionIsProcessedAndNewTouchComesIn) { + mWindow->setDispatchingTimeout(2s); // Set a long ANR timeout to prevent it from triggering + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}}); + + tapOnWindow(); + std::optional downSequenceNum = mWindow->receiveEvent(); + ASSERT_TRUE(downSequenceNum); + std::optional upSequenceNum = mWindow->receiveEvent(); + ASSERT_TRUE(upSequenceNum); + // Don't finish the events yet, and send a key + // Injection is async, so it will succeed + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, + injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */, + ADISPLAY_ID_DEFAULT, INPUT_EVENT_INJECTION_SYNC_NONE)); + // At this point, key is still pending, and should not be sent to the application yet. + std::optional keySequenceNum = mWindow->receiveEvent(); + ASSERT_FALSE(keySequenceNum); + + // Now tap down again. It should cause the pending key to go to the focused window right away. + tapOnWindow(); + mWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT); // it doesn't matter that we haven't ack'd + // the other events yet. We can finish events in any order. + mWindow->finishEvent(*downSequenceNum); // first tap's ACTION_DOWN + mWindow->finishEvent(*upSequenceNum); // first tap's ACTION_UP + mWindow->consumeMotionDown(); + mWindow->consumeMotionUp(); + mWindow->assertNoEvents(); +} + +class InputDispatcherMultiWindowAnr : public InputDispatcherTest { + virtual void SetUp() override { + InputDispatcherTest::SetUp(); + + mApplication = new FakeApplicationHandle(); + mApplication->setDispatchingTimeout(10ms); + mUnfocusedWindow = + new FakeWindowHandle(mApplication, mDispatcher, "Unfocused", ADISPLAY_ID_DEFAULT); + mUnfocusedWindow->setFrame(Rect(0, 0, 30, 30)); + // Adding FLAG_NOT_TOUCH_MODAL to ensure taps outside this window are not sent to this + // window. + // Adding FLAG_WATCH_OUTSIDE_TOUCH to receive ACTION_OUTSIDE when another window is tapped + mUnfocusedWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL | + InputWindowInfo::FLAG_WATCH_OUTSIDE_TOUCH | + InputWindowInfo::FLAG_SPLIT_TOUCH); + + mFocusedWindow = + new FakeWindowHandle(mApplication, mDispatcher, "Focused", ADISPLAY_ID_DEFAULT); + mFocusedWindow->setDispatchingTimeout(10ms); + mFocusedWindow->setFrame(Rect(50, 50, 100, 100)); + mFocusedWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL | + InputWindowInfo::FLAG_SPLIT_TOUCH); + + // Set focused application. + mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApplication); + mFocusedWindow->setFocus(true); + + // Expect one focus window exist in display. + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mUnfocusedWindow, mFocusedWindow}}}); + mFocusedWindow->consumeFocusEvent(true); + } + + virtual void TearDown() override { + InputDispatcherTest::TearDown(); + + mUnfocusedWindow.clear(); + mFocusedWindow.clear(); + } + +protected: + sp mApplication; + sp mUnfocusedWindow; + sp mFocusedWindow; + static constexpr PointF UNFOCUSED_WINDOW_LOCATION = {20, 20}; + static constexpr PointF FOCUSED_WINDOW_LOCATION = {75, 75}; + static constexpr PointF LOCATION_OUTSIDE_ALL_WINDOWS = {40, 40}; + + void tapOnFocusedWindow() { tap(FOCUSED_WINDOW_LOCATION); } + + void tapOnUnfocusedWindow() { tap(UNFOCUSED_WINDOW_LOCATION); } + +private: + void tap(const PointF& location) { + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + location)); + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, + injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + location)); + } +}; + +// If we have 2 windows that are both unresponsive, the one with the shortest timeout +// should be ANR'd first. +TEST_F(InputDispatcherMultiWindowAnr, TwoWindows_BothUnresponsive) { + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + FOCUSED_WINDOW_LOCATION)) + << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED"; + mFocusedWindow->consumeMotionDown(); + mUnfocusedWindow->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_OUTSIDE, + ADISPLAY_ID_DEFAULT, 0 /*flags*/); + // We consumed all events, so no ANR + ASSERT_TRUE(mDispatcher->waitForIdle()); + mFakePolicy->assertNotifyAnrWasNotCalled(); + + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + FOCUSED_WINDOW_LOCATION)); + std::optional unfocusedSequenceNum = mUnfocusedWindow->receiveEvent(); + ASSERT_TRUE(unfocusedSequenceNum); + std::optional focusedSequenceNum = mFocusedWindow->receiveEvent(); + ASSERT_TRUE(focusedSequenceNum); + + const std::chrono::duration timeout = + mFocusedWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT); + mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, + mFocusedWindow->getToken()); + + mFocusedWindow->finishEvent(*focusedSequenceNum); + mUnfocusedWindow->finishEvent(*unfocusedSequenceNum); + ASSERT_TRUE(mDispatcher->waitForIdle()); +} + +// If we have 2 windows with identical timeouts that are both unresponsive, +// it doesn't matter which order they should have ANR. +// But we should receive ANR for both. +TEST_F(InputDispatcherMultiWindowAnr, TwoWindows_BothUnresponsiveWithSameTimeout) { + // Set the timeout for unfocused window to match the focused window + mUnfocusedWindow->setDispatchingTimeout(10ms); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mUnfocusedWindow, mFocusedWindow}}}); + + tapOnFocusedWindow(); + // we should have ACTION_DOWN/ACTION_UP on focused window and ACTION_OUTSIDE on unfocused window + std::pair, sp> anrData1 = + mFakePolicy->getNotifyAnrData(10ms); + std::pair, sp> anrData2 = + mFakePolicy->getNotifyAnrData(0ms); + + // We don't know which window will ANR first. But both of them should happen eventually. + ASSERT_TRUE(mFocusedWindow->getToken() == anrData1.second || + mFocusedWindow->getToken() == anrData2.second); + ASSERT_TRUE(mUnfocusedWindow->getToken() == anrData1.second || + mUnfocusedWindow->getToken() == anrData2.second); + + ASSERT_TRUE(mDispatcher->waitForIdle()); + mFakePolicy->assertNotifyAnrWasNotCalled(); +} + +// If a window is already not responding, the second tap on the same window should be ignored. +// We should also log an error to account for the dropped event (not tested here). +// At the same time, FLAG_WATCH_OUTSIDE_TOUCH targets should not receive any events. +TEST_F(InputDispatcherMultiWindowAnr, DuringAnr_SecondTapIsIgnored) { + tapOnFocusedWindow(); + mUnfocusedWindow->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_OUTSIDE, + ADISPLAY_ID_DEFAULT, 0 /*flags*/); + // Receive the events, but don't respond + std::optional downEventSequenceNum = mFocusedWindow->receiveEvent(); // ACTION_DOWN + ASSERT_TRUE(downEventSequenceNum); + std::optional upEventSequenceNum = mFocusedWindow->receiveEvent(); // ACTION_UP + ASSERT_TRUE(upEventSequenceNum); + const std::chrono::duration timeout = + mFocusedWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT); + mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, + mFocusedWindow->getToken()); + + // Tap once again + // We cannot use "tapOnFocusedWindow" because it asserts the injection result to be success + ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + FOCUSED_WINDOW_LOCATION)); + ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, + injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + FOCUSED_WINDOW_LOCATION)); + // Unfocused window does not receive ACTION_OUTSIDE because the tapped window is not a + // valid touch target + mUnfocusedWindow->assertNoEvents(); + + // Consume the first tap + mFocusedWindow->finishEvent(*downEventSequenceNum); + mFocusedWindow->finishEvent(*upEventSequenceNum); + ASSERT_TRUE(mDispatcher->waitForIdle()); + // The second tap did not go to the focused window + mFocusedWindow->assertNoEvents(); + // should not have another ANR after the window just became healthy again + mFakePolicy->assertNotifyAnrWasNotCalled(); +} + +// If you tap outside of all windows, there will not be ANR +TEST_F(InputDispatcherMultiWindowAnr, TapOutsideAllWindows_DoesNotAnr) { + ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + LOCATION_OUTSIDE_ALL_WINDOWS)); + ASSERT_TRUE(mDispatcher->waitForIdle()); + mFakePolicy->assertNotifyAnrWasNotCalled(); +} + +// Since the focused window is paused, tapping on it should not produce any events +TEST_F(InputDispatcherMultiWindowAnr, Window_CanBePaused) { + mFocusedWindow->setPaused(true); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mUnfocusedWindow, mFocusedWindow}}}); + + ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + FOCUSED_WINDOW_LOCATION)); + + std::this_thread::sleep_for(mFocusedWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT)); + ASSERT_TRUE(mDispatcher->waitForIdle()); + // Should not ANR because the window is paused, and touches shouldn't go to it + mFakePolicy->assertNotifyAnrWasNotCalled(); + + mFocusedWindow->assertNoEvents(); + mUnfocusedWindow->assertNoEvents(); +} + +/** + * If a window is processing a motion event, and then a key event comes in, the key event should + * not to to the focused window until the motion is processed. + * If a different window becomes focused at this time, the key should go to that window instead. + * + * Warning!!! + * This test depends on the value of android::inputdispatcher::KEY_WAITING_FOR_MOTION_TIMEOUT + * and the injection timeout that we specify when injecting the key. + * We must have the injection timeout (10ms) be smaller than + * KEY_WAITING_FOR_MOTION_TIMEOUT (currently 500ms). + * + * If that value changes, this test should also change. + */ +TEST_F(InputDispatcherMultiWindowAnr, PendingKey_GoesToNewlyFocusedWindow) { + // Set a long ANR timeout to prevent it from triggering + mFocusedWindow->setDispatchingTimeout(2s); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mFocusedWindow, mUnfocusedWindow}}}); + + tapOnUnfocusedWindow(); + std::optional downSequenceNum = mUnfocusedWindow->receiveEvent(); + ASSERT_TRUE(downSequenceNum); + std::optional upSequenceNum = mUnfocusedWindow->receiveEvent(); + ASSERT_TRUE(upSequenceNum); + // Don't finish the events yet, and send a key + // Injection will succeed because we will eventually give up and send the key to the focused + // window even if motions are still being processed. + + int32_t result = + injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /*repeatCount*/, ADISPLAY_ID_DEFAULT, + INPUT_EVENT_INJECTION_SYNC_NONE, 10ms /*injectionTimeout*/); + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, result); + // Key will not be sent to the window, yet, because the window is still processing events + // and the key remains pending, waiting for the touch events to be processed + std::optional keySequenceNum = mFocusedWindow->receiveEvent(); + ASSERT_FALSE(keySequenceNum); + + // Switch the focus to the "unfocused" window that we tapped. Expect the key to go there + mFocusedWindow->setFocus(false); + mUnfocusedWindow->setFocus(true); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mFocusedWindow, mUnfocusedWindow}}}); + + // Focus events should precede the key events + mUnfocusedWindow->consumeFocusEvent(true); + mFocusedWindow->consumeFocusEvent(false); + + // Finish the tap events, which should unblock dispatcher + mUnfocusedWindow->finishEvent(*downSequenceNum); + mUnfocusedWindow->finishEvent(*upSequenceNum); + + // Now that all queues are cleared and no backlog in the connections, the key event + // can finally go to the newly focused "mUnfocusedWindow". + mUnfocusedWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT); + mFocusedWindow->assertNoEvents(); + mUnfocusedWindow->assertNoEvents(); +} + +// When the touch stream is split across 2 windows, and one of them does not respond, +// then ANR should be raised and the touch should be canceled for the unresponsive window. +// The other window should not be affected by that. +TEST_F(InputDispatcherMultiWindowAnr, SplitTouch_SingleWindowAnr) { + // Touch Window 1 + NotifyMotionArgs motionArgs = + generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, {FOCUSED_WINDOW_LOCATION}); + mDispatcher->notifyMotion(&motionArgs); + mUnfocusedWindow->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_OUTSIDE, + ADISPLAY_ID_DEFAULT, 0 /*flags*/); + + // Touch Window 2 + int32_t actionPointerDown = + AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); + + motionArgs = + generateMotionArgs(actionPointerDown, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + {FOCUSED_WINDOW_LOCATION, UNFOCUSED_WINDOW_LOCATION}); + mDispatcher->notifyMotion(&motionArgs); + + const std::chrono::duration timeout = + mFocusedWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT); + mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, + mFocusedWindow->getToken()); + + mUnfocusedWindow->consumeMotionDown(); + mFocusedWindow->consumeMotionDown(); + // Focused window may or may not receive ACTION_MOVE + // But it should definitely receive ACTION_CANCEL due to the ANR + InputEvent* event; + std::optional moveOrCancelSequenceNum = mFocusedWindow->receiveEvent(&event); + ASSERT_TRUE(moveOrCancelSequenceNum); + mFocusedWindow->finishEvent(*moveOrCancelSequenceNum); + ASSERT_NE(nullptr, event); + ASSERT_EQ(event->getType(), AINPUT_EVENT_TYPE_MOTION); + MotionEvent& motionEvent = static_cast(*event); + if (motionEvent.getAction() == AMOTION_EVENT_ACTION_MOVE) { + mFocusedWindow->consumeMotionCancel(); + } else { + ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, motionEvent.getAction()); + } + ASSERT_TRUE(mDispatcher->waitForIdle()); + mUnfocusedWindow->assertNoEvents(); + mFocusedWindow->assertNoEvents(); } } // namespace android::inputdispatcher -- cgit v1.2.3-59-g8ed1b From f1cf6a632da354cff3d8aed54913e1ee2909908e Mon Sep 17 00:00:00 2001 From: Luke Huang Date: Sat, 20 Jun 2020 13:01:33 +0800 Subject: Update the documentation of ANDROID_RESOLV_NO_CACHE_STORE Make it up-to-date. Test: N/A Bug: 150371903 Change-Id: Ia1402a18d6d466ffbb0357127d7d45cf6c722550 --- include/android/multinetwork.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/android/multinetwork.h b/include/android/multinetwork.h index 59b1deb595..c6d1c94c0a 100644 --- a/include/android/multinetwork.h +++ b/include/android/multinetwork.h @@ -126,8 +126,8 @@ enum ResNsendFlags : uint32_t { ANDROID_RESOLV_NO_RETRY = 1 << 0, /** - * Do not cache the result of the lookup. The lookup may return a result that is already - * in the cache, unless the ANDROID_RESOLV_NO_CACHE_LOOKUP flag is also specified. + * Don't lookup this request in the cache, and don't cache the result of the lookup. + * This flag implies {@link #ANDROID_RESOLV_NO_CACHE_LOOKUP}. */ ANDROID_RESOLV_NO_CACHE_STORE = 1 << 1, -- cgit v1.2.3-59-g8ed1b From 8fc3b0aa17398b8475866923008cf463b535bfb7 Mon Sep 17 00:00:00 2001 From: Nick Cook Date: Thu, 9 Jul 2020 19:29:25 +0000 Subject: docs: Clarify the lifetime of AHardwareBuffer_toHardwareBuffer in docs. See generated output in cl/320449087. Bug: 140885254 Test: m ndk Change-Id: I5b27dcedd5b0d20003b46c05c40a8c7c26dd9d28 --- include/android/hardware_buffer_jni.h | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/android/hardware_buffer_jni.h b/include/android/hardware_buffer_jni.h index 293e5ac469..ae208a6e75 100644 --- a/include/android/hardware_buffer_jni.h +++ b/include/android/hardware_buffer_jni.h @@ -39,9 +39,9 @@ __BEGIN_DECLS * Return the AHardwareBuffer wrapped by a Java HardwareBuffer object. * * This method does not acquire any additional reference to the AHardwareBuffer - * that is returned. To keep the AHardwareBuffer live after the Java - * HardwareBuffer object got garbage collected, be sure to use AHardwareBuffer_acquire() - * to acquire an additional reference. + * that is returned. To keep the AHardwareBuffer alive after the Java + * HardwareBuffer object is closed, explicitly or by the garbage collector, be + * sure to use AHardwareBuffer_acquire() to acquire an additional reference. * * Available since API level 26. */ @@ -50,7 +50,18 @@ AHardwareBuffer* AHardwareBuffer_fromHardwareBuffer(JNIEnv* env, /** * Return a new Java HardwareBuffer object that wraps the passed native - * AHardwareBuffer object. + * AHardwareBuffer object. The Java HardwareBuffer will acquire a reference to + * the internal buffer and manage its lifetime. For example: + * + *

+ * AHardwareBuffer* buffer;
+ * AHardwareBuffer_allocate(..., &buffer);  // `buffer` has reference count 1
+ * jobject java_result = AHardwareBuffer_toHardwareBuffer(buffer);  // `buffer` has reference count 2.
+ * AHardwareBuffer_release(buffer); // `buffer` has reference count 1
+ * return result;  // The underlying buffer is kept alive by `java_result` and
+ *                 // will be set to 0 when it is closed on the Java side with
+ *                 // HardwareBuffer::close().
+ * 
* * Available since API level 26. */ -- cgit v1.2.3-59-g8ed1b