From 1aaa212641edc9367f937c53b85a3a4b8d2ed153 Mon Sep 17 00:00:00 2001 From: Chris Ye Date: Tue, 7 Apr 2020 19:38:15 -0700 Subject: Add java-to-native converters for input events. Add java-to-native converters for android.view.KeyEvent, android.view.MotionEvent. Bug: 116830907 Test: atest KeyEventTest, atest MotionEventTest Change-Id: I66a2f1dbd627c03b5dc923713be74ee9c2b73e1a --- include/android/input.h | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) (limited to 'include/android/input.h') diff --git a/include/android/input.h b/include/android/input.h index dbfd61eb05..7c392348b7 100644 --- a/include/android/input.h +++ b/include/android/input.h @@ -55,6 +55,7 @@ #include #include #include +#include #if !defined(__INTRODUCED_IN) #define __INTRODUCED_IN(__api_level) /* nothing */ @@ -931,6 +932,15 @@ int32_t AInputEvent_getDeviceId(const AInputEvent* event); /** Get the input event source. */ int32_t AInputEvent_getSource(const AInputEvent* event); +/** + * Releases interface objects created by {@link AKeyEvent_fromJava()} + * and {@link AMotionEvent_fromJava()}. + * After returning, the specified AInputEvent* object becomes invalid and should no longer be used. + * The underlying Java object remains valid and does not change its state. + */ + +void AInputEvent_release(const AInputEvent* event); + /*** Accessors for key events only. ***/ /** Get the key event action. */ @@ -977,6 +987,15 @@ int64_t AKeyEvent_getDownTime(const AInputEvent* key_event); */ int64_t AKeyEvent_getEventTime(const AInputEvent* key_event); +/** + * Creates a native AInputEvent* object associated with the specified Java android.view.KeyEvent. + * The result may be used with generic and KeyEvent-specific AInputEvent_* functions. + * The object returned by this function must be disposed using {@link AInputEvent_release()}. + * User must guarantee that lifetime for object referenced by keyEvent is prolongated + * up to release of returned AInputEvent*. + */ +const AInputEvent* AKeyEvent_fromJava(JNIEnv* env, jobject keyEvent); + /*** Accessors for motion events only. ***/ /** Get the combined motion event action code and pointer index. */ @@ -1292,6 +1311,14 @@ float AMotionEvent_getHistoricalOrientation(const AInputEvent* motion_event, siz float AMotionEvent_getHistoricalAxisValue(const AInputEvent* motion_event, int32_t axis, size_t pointer_index, size_t history_index); +/** + * Creates a native AInputEvent* object associated with the specified Java android.view.MotionEvent. + * The result may be used with generic and MotionEvent-specific AInputEvent_* functions. + * The object returned by this function must be disposed using {@link AInputEvent_release()}. + * User must guarantee that object referenced by motionEvent won't be recycled and + * its lifetime is prolongated up to release of returned AInputEvent*. + */ +const AInputEvent* AMotionEvent_fromJava(JNIEnv* env, jobject motionEvent); struct AInputQueue; /** -- cgit v1.2.3-59-g8ed1b From a53d86530d59a4ec708d4090821da21f380dd07e Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Thu, 13 Aug 2020 23:59:50 -0500 Subject: Correct docs about the returned native objects The native objects returned by the function are actually copies of the Java objects. So, they do not have any relationship with the original Java objects, and their lifetimes do not depend on them. Bug: 150954805 Test: none Change-Id: I21c35bf34eb10d2ce7b4716b886840b11b0c08de --- include/android/input.h | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) (limited to 'include/android/input.h') diff --git a/include/android/input.h b/include/android/input.h index 7c392348b7..b04775b5c3 100644 --- a/include/android/input.h +++ b/include/android/input.h @@ -988,11 +988,9 @@ int64_t AKeyEvent_getDownTime(const AInputEvent* key_event); int64_t AKeyEvent_getEventTime(const AInputEvent* key_event); /** - * Creates a native AInputEvent* object associated with the specified Java android.view.KeyEvent. - * The result may be used with generic and KeyEvent-specific AInputEvent_* functions. - * The object returned by this function must be disposed using {@link AInputEvent_release()}. - * User must guarantee that lifetime for object referenced by keyEvent is prolongated - * up to release of returned AInputEvent*. + * Creates a native AInputEvent* object that is a copy of the specified Java android.view.KeyEvent. + * The result may be used with generic and KeyEvent-specific AInputEvent_* functions. The object + * returned by this function must be disposed using {@link AInputEvent_release()}. */ const AInputEvent* AKeyEvent_fromJava(JNIEnv* env, jobject keyEvent); @@ -1312,11 +1310,10 @@ float AMotionEvent_getHistoricalAxisValue(const AInputEvent* motion_event, int32_t axis, size_t pointer_index, size_t history_index); /** - * Creates a native AInputEvent* object associated with the specified Java android.view.MotionEvent. - * The result may be used with generic and MotionEvent-specific AInputEvent_* functions. - * The object returned by this function must be disposed using {@link AInputEvent_release()}. - * User must guarantee that object referenced by motionEvent won't be recycled and - * its lifetime is prolongated up to release of returned AInputEvent*. + * Creates a native AInputEvent* object that is a copy of the specified Java + * android.view.MotionEvent. The result may be used with generic and MotionEvent-specific + * AInputEvent_* functions. The object returned by this function must be disposed using + * {@link AInputEvent_release()}. */ const AInputEvent* AMotionEvent_fromJava(JNIEnv* env, jobject motionEvent); -- cgit v1.2.3-59-g8ed1b From 3f37b7b1d750190f15afd0d6c0a54e1ea76cbd40 Mon Sep 17 00:00:00 2001 From: Prabir Pradhan Date: Tue, 10 Nov 2020 16:50:18 -0800 Subject: SyncPointerCapture (3/n): Add Capture event to InputChannel This CL adds the ability to send a CAPTURE event through the InputChannel, and adds the appropriate processing logic to InputPublisher and InputConsumer. This will be used by the InputDispatcher to notify windows when they have either lost or gained Pointer Capture. Bug: 141749603 Test: atest libinput_tests Change-Id: I102833e6f0fd1e8e9c4b3c12e7a5a737eeda2377 --- include/android/input.h | 3 ++ include/input/Input.h | 24 +++++++++++ include/input/InputTransport.h | 20 ++++++++++ libs/input/Input.cpp | 32 +++++++++++++++ libs/input/InputTransport.cpp | 46 ++++++++++++++++++++++ .../input/tests/InputPublisherAndConsumer_test.cpp | 45 +++++++++++++++++++++ libs/input/tests/StructLayout_test.cpp | 4 ++ 7 files changed, 174 insertions(+) (limited to 'include/android/input.h') diff --git a/include/android/input.h b/include/android/input.h index b04775b5c3..38af89a31e 100644 --- a/include/android/input.h +++ b/include/android/input.h @@ -163,6 +163,9 @@ enum { /** Focus event */ AINPUT_EVENT_TYPE_FOCUS = 3, + + /** Capture event */ + AINPUT_EVENT_TYPE_CAPTURE = 4, }; /** diff --git a/include/input/Input.h b/include/input/Input.h index 3facfa5135..aa42db8ea8 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -773,6 +773,25 @@ protected: bool mInTouchMode; }; +/* + * Capture events. + */ +class CaptureEvent : public InputEvent { +public: + virtual ~CaptureEvent() {} + + virtual int32_t getType() const override { return AINPUT_EVENT_TYPE_CAPTURE; } + + inline bool getPointerCaptureEnabled() const { return mPointerCaptureEnabled; } + + void initialize(int32_t id, bool pointerCaptureEnabled); + + void initialize(const CaptureEvent& from); + +protected: + bool mPointerCaptureEnabled; +}; + /** * Base class for verified events. * Do not create a VerifiedInputEvent explicitly. @@ -835,6 +854,7 @@ public: virtual KeyEvent* createKeyEvent() = 0; virtual MotionEvent* createMotionEvent() = 0; virtual FocusEvent* createFocusEvent() = 0; + virtual CaptureEvent* createCaptureEvent() = 0; }; /* @@ -849,11 +869,13 @@ public: virtual KeyEvent* createKeyEvent() override { return &mKeyEvent; } virtual MotionEvent* createMotionEvent() override { return &mMotionEvent; } virtual FocusEvent* createFocusEvent() override { return &mFocusEvent; } + virtual CaptureEvent* createCaptureEvent() override { return &mCaptureEvent; } private: KeyEvent mKeyEvent; MotionEvent mMotionEvent; FocusEvent mFocusEvent; + CaptureEvent mCaptureEvent; }; /* @@ -867,6 +889,7 @@ public: virtual KeyEvent* createKeyEvent() override; virtual MotionEvent* createMotionEvent() override; virtual FocusEvent* createFocusEvent() override; + virtual CaptureEvent* createCaptureEvent() override; void recycle(InputEvent* event); @@ -876,6 +899,7 @@ private: std::queue> mKeyEventPool; std::queue> mMotionEventPool; std::queue> mFocusEventPool; + std::queue> mCaptureEventPool; }; } // namespace android diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h index ad0a14ed0b..8744ef76c1 100644 --- a/include/input/InputTransport.h +++ b/include/input/InputTransport.h @@ -67,6 +67,7 @@ struct InputMessage { MOTION, FINISHED, FOCUS, + CAPTURE, }; struct Header { @@ -166,6 +167,13 @@ struct InputMessage { inline size_t size() const { return sizeof(Focus); } } focus; + + struct Capture { + int32_t eventId; + uint32_t pointerCaptureEnabled; // actually a bool, but we maintain 8-byte alignment + + inline size_t size() const { return sizeof(Capture); } + } capture; } __attribute__((aligned(8))) body; bool isValid(size_t actualSize) const; @@ -182,6 +190,8 @@ struct InputMessage { return "FINISHED"; case Type::FOCUS: return "FOCUS"; + case Type::CAPTURE: + return "CAPTURE"; } } }; @@ -341,6 +351,15 @@ public: */ status_t publishFocusEvent(uint32_t seq, int32_t eventId, bool hasFocus, bool inTouchMode); + /* Publishes a capture 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 publishCaptureEvent(uint32_t seq, int32_t eventId, bool pointerCaptureEnabled); + /* 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. @@ -576,6 +595,7 @@ private: 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 initializeCaptureEvent(CaptureEvent* 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 0ea3889338..0a00d68556 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -89,6 +89,9 @@ const char* inputEventTypeToString(int32_t type) { case AINPUT_EVENT_TYPE_FOCUS: { return "FOCUS"; } + case AINPUT_EVENT_TYPE_CAPTURE: { + return "CAPTURE"; + } } return "UNKNOWN"; } @@ -754,6 +757,19 @@ void FocusEvent::initialize(const FocusEvent& from) { mInTouchMode = from.mInTouchMode; } +// --- CaptureEvent --- + +void CaptureEvent::initialize(int32_t id, bool pointerCaptureEnabled) { + InputEvent::initialize(id, ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID, AINPUT_SOURCE_UNKNOWN, + ADISPLAY_ID_NONE, INVALID_HMAC); + mPointerCaptureEnabled = pointerCaptureEnabled; +} + +void CaptureEvent::initialize(const CaptureEvent& from) { + InputEvent::initialize(from); + mPointerCaptureEnabled = from.mPointerCaptureEnabled; +} + // --- PooledInputEventFactory --- PooledInputEventFactory::PooledInputEventFactory(size_t maxPoolSize) : @@ -790,6 +806,15 @@ FocusEvent* PooledInputEventFactory::createFocusEvent() { return event; } +CaptureEvent* PooledInputEventFactory::createCaptureEvent() { + if (mCaptureEventPool.empty()) { + return new CaptureEvent(); + } + CaptureEvent* event = mCaptureEventPool.front().release(); + mCaptureEventPool.pop(); + return event; +} + void PooledInputEventFactory::recycle(InputEvent* event) { switch (event->getType()) { case AINPUT_EVENT_TYPE_KEY: @@ -810,6 +835,13 @@ void PooledInputEventFactory::recycle(InputEvent* event) { return; } break; + case AINPUT_EVENT_TYPE_CAPTURE: + if (mCaptureEventPool.size() < mMaxPoolSize) { + mCaptureEventPool.push( + std::unique_ptr(static_cast(event))); + return; + } + break; } delete event; } diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp index 85df405f98..acea473242 100644 --- a/libs/input/InputTransport.cpp +++ b/libs/input/InputTransport.cpp @@ -105,6 +105,8 @@ bool InputMessage::isValid(size_t actualSize) const { return true; case Type::FOCUS: return true; + case Type::CAPTURE: + return true; } } return false; @@ -120,6 +122,8 @@ size_t InputMessage::size() const { return sizeof(Header) + body.finished.size(); case Type::FOCUS: return sizeof(Header) + body.focus.size(); + case Type::CAPTURE: + return sizeof(Header) + body.capture.size(); } return sizeof(Header); } @@ -238,6 +242,11 @@ void InputMessage::getSanitizedCopy(InputMessage* msg) const { msg->body.focus.inTouchMode = body.focus.inTouchMode; break; } + case InputMessage::Type::CAPTURE: { + msg->body.capture.eventId = body.capture.eventId; + msg->body.capture.pointerCaptureEnabled = body.capture.pointerCaptureEnabled; + break; + } } } @@ -571,6 +580,23 @@ status_t InputPublisher::publishFocusEvent(uint32_t seq, int32_t eventId, bool h return mChannel->sendMessage(&msg); } +status_t InputPublisher::publishCaptureEvent(uint32_t seq, int32_t eventId, + bool pointerCaptureEnabled) { + if (ATRACE_ENABLED()) { + std::string message = + StringPrintf("publishCaptureEvent(inputChannel=%s, pointerCaptureEnabled=%s)", + mChannel->getName().c_str(), toString(pointerCaptureEnabled)); + ATRACE_NAME(message.c_str()); + } + + InputMessage msg; + msg.header.type = InputMessage::Type::CAPTURE; + msg.header.seq = seq; + msg.body.capture.eventId = eventId; + msg.body.capture.pointerCaptureEnabled = pointerCaptureEnabled ? 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()); @@ -739,6 +765,16 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consum *outEvent = focusEvent; break; } + + case InputMessage::Type::CAPTURE: { + CaptureEvent* captureEvent = factory->createCaptureEvent(); + if (!captureEvent) return NO_MEMORY; + + initializeCaptureEvent(captureEvent, &mMsg); + *outSeq = mMsg.header.seq; + *outEvent = captureEvent; + break; + } } } return OK; @@ -1171,6 +1207,10 @@ void InputConsumer::initializeFocusEvent(FocusEvent* event, const InputMessage* msg->body.focus.inTouchMode == 1); } +void InputConsumer::initializeCaptureEvent(CaptureEvent* event, const InputMessage* msg) { + event->initialize(msg->body.capture.eventId, msg->body.capture.pointerCaptureEnabled == 1); +} + void InputConsumer::initializeMotionEvent(MotionEvent* event, const InputMessage* msg) { uint32_t pointerCount = msg->body.motion.pointerCount; PointerProperties pointerProperties[pointerCount]; @@ -1274,6 +1314,12 @@ std::string InputConsumer::dump() const { toString(msg.body.focus.inTouchMode)); break; } + case InputMessage::Type::CAPTURE: { + out += android::base::StringPrintf("hasCapture=%s", + toString(msg.body.capture + .pointerCaptureEnabled)); + break; + } } out += "\n"; } diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp index 4f53dc9ec4..9da7b69b19 100644 --- a/libs/input/tests/InputPublisherAndConsumer_test.cpp +++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp @@ -51,6 +51,7 @@ protected: void PublishAndConsumeKeyEvent(); void PublishAndConsumeMotionEvent(); void PublishAndConsumeFocusEvent(); + void PublishAndConsumeCaptureEvent(); }; TEST_F(InputPublisherAndConsumerTest, GetChannel_ReturnsTheChannel) { @@ -309,6 +310,43 @@ void InputPublisherAndConsumerTest::PublishAndConsumeFocusEvent() { << "publisher receiveFinishedSignal should have set handled to consumer's reply"; } +void InputPublisherAndConsumerTest::PublishAndConsumeCaptureEvent() { + status_t status; + + constexpr uint32_t seq = 42; + int32_t eventId = InputEvent::nextId(); + constexpr bool captureEnabled = true; + + status = mPublisher->publishCaptureEvent(seq, eventId, captureEnabled); + 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_CAPTURE, event->getType()) + << "consumer should have returned a capture event"; + + const CaptureEvent* captureEvent = static_cast(event); + EXPECT_EQ(seq, consumeSeq); + EXPECT_EQ(eventId, captureEvent->getId()); + EXPECT_EQ(captureEnabled, captureEvent->getPointerCaptureEnabled()); + + 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()); } @@ -321,6 +359,10 @@ TEST_F(InputPublisherAndConsumerTest, PublishFocusEvent_EndToEnd) { ASSERT_NO_FATAL_FAILURE(PublishAndConsumeFocusEvent()); } +TEST_F(InputPublisherAndConsumerTest, PublishCaptureEvent_EndToEnd) { + ASSERT_NO_FATAL_FAILURE(PublishAndConsumeCaptureEvent()); +} + TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenSequenceNumberIsZero_ReturnsError) { status_t status; const size_t pointerCount = 1; @@ -385,6 +427,9 @@ TEST_F(InputPublisherAndConsumerTest, PublishMultipleEvents_EndToEnd) { ASSERT_NO_FATAL_FAILURE(PublishAndConsumeFocusEvent()); ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent()); ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent()); + ASSERT_NO_FATAL_FAILURE(PublishAndConsumeCaptureEvent()); + ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent()); + ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent()); } } // namespace android diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp index 3c5fb22588..4107d61eb2 100644 --- a/libs/input/tests/StructLayout_test.cpp +++ b/libs/input/tests/StructLayout_test.cpp @@ -79,6 +79,9 @@ void TestInputMessageAlignment() { CHECK_OFFSET(InputMessage::Body::Focus, hasFocus, 4); CHECK_OFFSET(InputMessage::Body::Focus, inTouchMode, 6); + CHECK_OFFSET(InputMessage::Body::Capture, eventId, 0); + CHECK_OFFSET(InputMessage::Body::Capture, pointerCaptureEnabled, 4); + CHECK_OFFSET(InputMessage::Body::Finished, handled, 4); } @@ -99,6 +102,7 @@ void TestBodySize() { 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::Capture) == 8); } // --- VerifiedInputEvent --- -- cgit v1.2.3-59-g8ed1b From f59a2f484d4765edd74c9bb3f911f66c4eb7a3f4 Mon Sep 17 00:00:00 2001 From: Chris Ye Date: Fri, 16 Oct 2020 12:55:26 -0700 Subject: Add SensorManager support in inputflinger. Add sensor device, sensor input mapper, sens event dispatcher support into inputflinger. Bug: 161634265 Test: atest inputflinger_tests Change-Id: I2dcb2c35d9dccefc4cd8d939b79cf340931a9410 --- include/android/input.h | 2 + include/input/InputDevice.h | 103 +++++ include/input/KeyCharacterMap.h | 2 + include/input/KeyLayoutMap.h | 10 + libs/input/InputDevice.cpp | 31 +- libs/input/KeyCharacterMap.cpp | 42 ++ libs/input/KeyLayoutMap.cpp | 129 ++++++- libs/input/tests/InputDevice_test.cpp | 53 ++- services/inputflinger/InputClassifier.cpp | 5 + services/inputflinger/InputClassifier.h | 1 + services/inputflinger/InputListener.cpp | 40 ++ .../benchmarks/InputDispatcher_benchmarks.cpp | 7 + services/inputflinger/dispatcher/Entry.cpp | 35 ++ services/inputflinger/dispatcher/Entry.h | 38 +- .../inputflinger/dispatcher/InputDispatcher.cpp | 141 ++++++- services/inputflinger/dispatcher/InputDispatcher.h | 6 +- services/inputflinger/dispatcher/InputState.cpp | 2 + .../dispatcher/include/InputDispatcherInterface.h | 6 + .../include/InputDispatcherPolicyInterface.h | 5 + services/inputflinger/include/InputListener.h | 27 ++ services/inputflinger/include/InputReaderBase.h | 11 + services/inputflinger/reader/Android.bp | 1 + services/inputflinger/reader/EventHub.cpp | 49 ++- services/inputflinger/reader/InputDevice.cpp | 25 ++ services/inputflinger/reader/InputReader.cpp | 35 ++ services/inputflinger/reader/include/EventHub.h | 13 + services/inputflinger/reader/include/InputDevice.h | 13 +- services/inputflinger/reader/include/InputReader.h | 8 + .../inputflinger/reader/mapper/InputMapper.cpp | 10 + services/inputflinger/reader/mapper/InputMapper.h | 5 + .../reader/mapper/SensorInputMapper.cpp | 421 +++++++++++++++++++++ .../inputflinger/reader/mapper/SensorInputMapper.h | 138 +++++++ .../inputflinger/tests/InputDispatcher_test.cpp | 6 + services/inputflinger/tests/InputReader_test.cpp | 197 ++++++++++ services/inputflinger/tests/TestInputListener.cpp | 10 + services/inputflinger/tests/TestInputListener.h | 4 + 36 files changed, 1587 insertions(+), 44 deletions(-) create mode 100644 services/inputflinger/reader/mapper/SensorInputMapper.cpp create mode 100644 services/inputflinger/reader/mapper/SensorInputMapper.h (limited to 'include/android/input.h') diff --git a/include/android/input.h b/include/android/input.h index 38af89a31e..b5d399ee09 100644 --- a/include/android/input.h +++ b/include/android/input.h @@ -856,6 +856,8 @@ enum { AINPUT_SOURCE_TOUCH_NAVIGATION = 0x00200000 | AINPUT_SOURCE_CLASS_NONE, /** joystick */ AINPUT_SOURCE_JOYSTICK = 0x01000000 | AINPUT_SOURCE_CLASS_JOYSTICK, + /** sensor */ + AINPUT_SOURCE_SENSOR = 0x02000000 | AINPUT_SOURCE_CLASS_JOYSTICK, /** rotary encoder */ AINPUT_SOURCE_ROTARY_ENCODER = 0x00400000 | AINPUT_SOURCE_CLASS_NONE, diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h index 60638caaab..23692e92d4 100644 --- a/include/input/InputDevice.h +++ b/include/input/InputDevice.h @@ -17,8 +17,10 @@ #ifndef _LIBINPUT_INPUT_DEVICE_H #define _LIBINPUT_INPUT_DEVICE_H +#include #include #include +#include #include namespace android { @@ -63,6 +65,97 @@ struct InputDeviceIdentifier { std::string getCanonicalName() const; }; +/* Types of input device sensors. Keep sync with core/java/android/hardware/Sensor.java */ +enum class InputDeviceSensorType : int32_t { + ACCELEROMETER = ASENSOR_TYPE_ACCELEROMETER, + MAGNETIC_FIELD = ASENSOR_TYPE_MAGNETIC_FIELD, + ORIENTATION = 3, + GYROSCOPE = ASENSOR_TYPE_GYROSCOPE, + LIGHT = ASENSOR_TYPE_LIGHT, + PRESSURE = ASENSOR_TYPE_PRESSURE, + TEMPERATURE = 7, + PROXIMITY = ASENSOR_TYPE_PROXIMITY, + GRAVITY = ASENSOR_TYPE_GRAVITY, + LINEAR_ACCELERATION = ASENSOR_TYPE_LINEAR_ACCELERATION, + ROTATION_VECTOR = ASENSOR_TYPE_ROTATION_VECTOR, + RELATIVE_HUMIDITY = ASENSOR_TYPE_RELATIVE_HUMIDITY, + AMBIENT_TEMPERATURE = ASENSOR_TYPE_AMBIENT_TEMPERATURE, + MAGNETIC_FIELD_UNCALIBRATED = ASENSOR_TYPE_MAGNETIC_FIELD_UNCALIBRATED, + GAME_ROTATION_VECTOR = ASENSOR_TYPE_GAME_ROTATION_VECTOR, + GYROSCOPE_UNCALIBRATED = ASENSOR_TYPE_GYROSCOPE_UNCALIBRATED, + SIGNIFICANT_MOTION = ASENSOR_TYPE_SIGNIFICANT_MOTION, +}; + +enum class InputDeviceSensorAccuracy : int32_t { + ACCURACY_NONE = 0, + ACCURACY_LOW = 1, + ACCURACY_MEDIUM = 2, + ACCURACY_HIGH = 3, +}; + +enum class InputDeviceSensorReportingMode : int32_t { + CONTINUOUS = 0, + ON_CHANGE = 1, + ONE_SHOT = 2, + SPECIAL_TRIGGER = 3, +}; + +struct InputDeviceSensorInfo { + explicit InputDeviceSensorInfo(std::string name, std::string vendor, int32_t version, + InputDeviceSensorType type, InputDeviceSensorAccuracy accuracy, + float maxRange, float resolution, float power, int32_t minDelay, + int32_t fifoReservedEventCount, int32_t fifoMaxEventCount, + std::string stringType, int32_t maxDelay, int32_t flags, + int32_t id) + : name(name), + vendor(vendor), + version(version), + type(type), + accuracy(accuracy), + maxRange(maxRange), + resolution(resolution), + power(power), + minDelay(minDelay), + fifoReservedEventCount(fifoReservedEventCount), + fifoMaxEventCount(fifoMaxEventCount), + stringType(stringType), + maxDelay(maxDelay), + flags(flags), + id(id) {} + // Name string of the sensor. + std::string name; + // Vendor string of this sensor. + std::string vendor; + // Version of the sensor's module. + int32_t version; + // Generic type of this sensor. + InputDeviceSensorType type; + // The current accuracy of sensor event. + InputDeviceSensorAccuracy accuracy; + // Maximum range of the sensor in the sensor's unit. + float maxRange; + // Resolution of the sensor in the sensor's unit. + float resolution; + // The power in mA used by this sensor while in use. + float power; + // The minimum delay allowed between two events in microsecond or zero if this sensor only + // returns a value when the data it's measuring changes. + int32_t minDelay; + // Number of events reserved for this sensor in the batch mode FIFO. + int32_t fifoReservedEventCount; + // Maximum number of events of this sensor that could be batched. + int32_t fifoMaxEventCount; + // The type of this sensor as a string. + std::string stringType; + // The delay between two sensor events corresponding to the lowest frequency that this sensor + // supports. + int32_t maxDelay; + // Sensor flags + int32_t flags; + // Sensor id, same as the input device ID it belongs to. + int32_t id; +}; + /* * Describes the characteristics and capabilities of an input device. */ @@ -104,6 +197,7 @@ public: void addMotionRange(int32_t axis, uint32_t source, float min, float max, float flat, float fuzz, float resolution); void addMotionRange(const MotionRange& range); + void addSensorInfo(const InputDeviceSensorInfo& info); inline void setKeyboardType(int32_t keyboardType) { mKeyboardType = keyboardType; } inline int32_t getKeyboardType() const { return mKeyboardType; } @@ -122,10 +216,17 @@ public: inline void setButtonUnderPad(bool hasButton) { mHasButtonUnderPad = hasButton; } inline bool hasButtonUnderPad() const { return mHasButtonUnderPad; } + inline void setHasSensor(bool hasSensor) { mHasSensor = hasSensor; } + inline bool hasSensor() const { return mHasSensor; } + inline const std::vector& getMotionRanges() const { return mMotionRanges; } + const InputDeviceSensorInfo* getSensorInfo(InputDeviceSensorType type); + + const std::vector getSensorTypes(); + private: int32_t mId; int32_t mGeneration; @@ -139,8 +240,10 @@ private: std::shared_ptr mKeyCharacterMap; bool mHasVibrator; bool mHasButtonUnderPad; + bool mHasSensor; std::vector mMotionRanges; + std::unordered_map mSensors; }; /* Types of input device configuration files. */ diff --git a/include/input/KeyCharacterMap.h b/include/input/KeyCharacterMap.h index 23f8ddf764..451ca3c6cd 100644 --- a/include/input/KeyCharacterMap.h +++ b/include/input/KeyCharacterMap.h @@ -142,6 +142,8 @@ public: void writeToParcel(Parcel* parcel) const; #endif + bool operator==(const KeyCharacterMap& other) const; + KeyCharacterMap(const KeyCharacterMap& other); virtual ~KeyCharacterMap(); diff --git a/include/input/KeyLayoutMap.h b/include/input/KeyLayoutMap.h index 872dd45c73..b2bd535cbf 100644 --- a/include/input/KeyLayoutMap.h +++ b/include/input/KeyLayoutMap.h @@ -24,6 +24,8 @@ #include #include +#include + namespace android { struct AxisInfo { @@ -76,6 +78,8 @@ public: status_t mapAxis(int32_t scanCode, AxisInfo* outAxisInfo) const; const std::string getLoadFileName() const; + // Return pair of sensor type and sensor data index, for the input device abs code + base::Result> mapSensor(int32_t absCode); virtual ~KeyLayoutMap(); @@ -89,12 +93,17 @@ private: int32_t ledCode; }; + struct Sensor { + InputDeviceSensorType sensorType; + int32_t sensorDataIndex; + }; KeyedVector mKeysByScanCode; KeyedVector mKeysByUsageCode; KeyedVector mAxes; KeyedVector mLedsByScanCode; KeyedVector mLedsByUsageCode; + std::unordered_map mSensorsByAbsCode; std::string mLoadFileName; KeyLayoutMap(); @@ -114,6 +123,7 @@ private: status_t parseKey(); status_t parseAxis(); status_t parseLed(); + status_t parseSensor(); }; }; diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp index 34eba5be7f..2ed441d8b4 100644 --- a/libs/input/InputDevice.cpp +++ b/libs/input/InputDevice.cpp @@ -23,6 +23,7 @@ #include #include #include +#include using android::base::StringPrintf; @@ -166,7 +167,9 @@ InputDeviceInfo::InputDeviceInfo(const InputDeviceInfo& other) mKeyCharacterMap(other.mKeyCharacterMap), mHasVibrator(other.mHasVibrator), mHasButtonUnderPad(other.mHasButtonUnderPad), - mMotionRanges(other.mMotionRanges) {} + mHasSensor(other.mHasSensor), + mMotionRanges(other.mMotionRanges), + mSensors(other.mSensors) {} InputDeviceInfo::~InputDeviceInfo() { } @@ -185,7 +188,9 @@ void InputDeviceInfo::initialize(int32_t id, int32_t generation, int32_t control mKeyboardType = AINPUT_KEYBOARD_TYPE_NONE; mHasVibrator = false; mHasButtonUnderPad = false; + mHasSensor = false; mMotionRanges.clear(); + mSensors.clear(); } const InputDeviceInfo::MotionRange* InputDeviceInfo::getMotionRange( @@ -214,4 +219,28 @@ void InputDeviceInfo::addMotionRange(const MotionRange& range) { mMotionRanges.push_back(range); } +void InputDeviceInfo::addSensorInfo(const InputDeviceSensorInfo& info) { + if (mSensors.find(info.type) != mSensors.end()) { + ALOGW("Sensor type %s already exists, will be replaced by new sensor added.", + NamedEnum::string(info.type).c_str()); + } + mSensors.insert_or_assign(info.type, info); +} + +const std::vector InputDeviceInfo::getSensorTypes() { + std::vector types; + for (const auto& [type, info] : mSensors) { + types.push_back(type); + } + return types; +} + +const InputDeviceSensorInfo* InputDeviceInfo::getSensorInfo(InputDeviceSensorType type) { + auto it = mSensors.find(type); + if (it == mSensors.end()) { + return nullptr; + } + return &it->second; +} + } // namespace android diff --git a/libs/input/KeyCharacterMap.cpp b/libs/input/KeyCharacterMap.cpp index f5432ad3ee..5666de6624 100644 --- a/libs/input/KeyCharacterMap.cpp +++ b/libs/input/KeyCharacterMap.cpp @@ -103,6 +103,48 @@ KeyCharacterMap::~KeyCharacterMap() { } } +bool KeyCharacterMap::operator==(const KeyCharacterMap& other) const { + if (mType != other.mType) { + return false; + } + if (mKeys.size() != other.mKeys.size() || + mKeysByScanCode.size() != other.mKeysByScanCode.size() || + mKeysByUsageCode.size() != other.mKeysByUsageCode.size()) { + return false; + } + + for (size_t i = 0; i < mKeys.size(); i++) { + if (mKeys.keyAt(i) != other.mKeys.keyAt(i)) { + return false; + } + const Key* key = mKeys.valueAt(i); + const Key* otherKey = other.mKeys.valueAt(i); + if (key->label != otherKey->label || key->number != otherKey->number) { + return false; + } + } + + for (size_t i = 0; i < mKeysByScanCode.size(); i++) { + if (mKeysByScanCode.keyAt(i) != other.mKeysByScanCode.keyAt(i)) { + return false; + } + if (mKeysByScanCode.valueAt(i) != other.mKeysByScanCode.valueAt(i)) { + return false; + } + } + + for (size_t i = 0; i < mKeysByUsageCode.size(); i++) { + if (mKeysByUsageCode.keyAt(i) != other.mKeysByUsageCode.keyAt(i)) { + return false; + } + if (mKeysByUsageCode.valueAt(i) != other.mKeysByUsageCode.valueAt(i)) { + return false; + } + } + + return true; +} + base::Result> KeyCharacterMap::load(const std::string& filename, Format format) { Tokenizer* tokenizer; diff --git a/libs/input/KeyLayoutMap.cpp b/libs/input/KeyLayoutMap.cpp index 16ce48aca1..d36e417850 100644 --- a/libs/input/KeyLayoutMap.cpp +++ b/libs/input/KeyLayoutMap.cpp @@ -20,12 +20,13 @@ #include #include -#include #include -#include +#include +#include #include -#include +#include #include +#include // Enables debug output for the parser. #define DEBUG_PARSER 0 @@ -41,6 +42,26 @@ namespace android { static const char* WHITESPACE = " \t\r"; +#define SENSOR_ENTRY(type) NamedEnum::string(type), type +static const std::unordered_map SENSOR_LIST = + {{SENSOR_ENTRY(InputDeviceSensorType::ACCELEROMETER)}, + {SENSOR_ENTRY(InputDeviceSensorType::MAGNETIC_FIELD)}, + {SENSOR_ENTRY(InputDeviceSensorType::ORIENTATION)}, + {SENSOR_ENTRY(InputDeviceSensorType::GYROSCOPE)}, + {SENSOR_ENTRY(InputDeviceSensorType::LIGHT)}, + {SENSOR_ENTRY(InputDeviceSensorType::PRESSURE)}, + {SENSOR_ENTRY(InputDeviceSensorType::TEMPERATURE)}, + {SENSOR_ENTRY(InputDeviceSensorType::PROXIMITY)}, + {SENSOR_ENTRY(InputDeviceSensorType::GRAVITY)}, + {SENSOR_ENTRY(InputDeviceSensorType::LINEAR_ACCELERATION)}, + {SENSOR_ENTRY(InputDeviceSensorType::ROTATION_VECTOR)}, + {SENSOR_ENTRY(InputDeviceSensorType::RELATIVE_HUMIDITY)}, + {SENSOR_ENTRY(InputDeviceSensorType::AMBIENT_TEMPERATURE)}, + {SENSOR_ENTRY(InputDeviceSensorType::MAGNETIC_FIELD_UNCALIBRATED)}, + {SENSOR_ENTRY(InputDeviceSensorType::GAME_ROTATION_VECTOR)}, + {SENSOR_ENTRY(InputDeviceSensorType::GYROSCOPE_UNCALIBRATED)}, + {SENSOR_ENTRY(InputDeviceSensorType::SIGNIFICANT_MOTION)}}; + // --- KeyLayoutMap --- KeyLayoutMap::KeyLayoutMap() { @@ -127,6 +148,24 @@ status_t KeyLayoutMap::mapKey(int32_t scanCode, int32_t usageCode, return NO_ERROR; } +// Return pair of sensor type and sensor data index, for the input device abs code +base::Result> KeyLayoutMap::mapSensor(int32_t absCode) { + auto it = mSensorsByAbsCode.find(absCode); + if (it == mSensorsByAbsCode.end()) { +#if DEBUG_MAPPING + ALOGD("mapSensor: absCode=%d, ~ Failed.", absCode); +#endif + return Errorf("Can't find abs code {}.", absCode); + } + const Sensor& sensor = it->second; + +#if DEBUG_MAPPING + ALOGD("mapSensor: absCode=%d, sensorType=0x%0x, sensorDataIndex=0x%x.", absCode, + NamedEnum::string(sensor.sensorType), sensor.sensorDataIndex); +#endif + return std::make_pair(sensor.sensorType, sensor.sensorDataIndex); +} + const KeyLayoutMap::Key* KeyLayoutMap::getKey(int32_t scanCode, int32_t usageCode) const { if (usageCode) { ssize_t index = mKeysByUsageCode.indexOfKey(usageCode); @@ -242,6 +281,10 @@ status_t KeyLayoutMap::Parser::parse() { mTokenizer->skipDelimiters(WHITESPACE); status_t status = parseLed(); if (status) return status; + } else if (keywordToken == "sensor") { + mTokenizer->skipDelimiters(WHITESPACE); + status_t status = parseSensor(); + if (status) return status; } else { ALOGE("%s: Expected keyword, got '%s'.", mTokenizer->getLocation().string(), keywordToken.string()); @@ -468,4 +511,84 @@ status_t KeyLayoutMap::Parser::parseLed() { map.add(code, led); return NO_ERROR; } + +static std::optional getSensorType(const char* token) { + auto it = SENSOR_LIST.find(std::string(token)); + if (it == SENSOR_LIST.end()) { + return std::nullopt; + } + return it->second; +} + +static std::optional getSensorDataIndex(String8 token) { + std::string tokenStr(token.string()); + if (tokenStr == "X") { + return 0; + } else if (tokenStr == "Y") { + return 1; + } else if (tokenStr == "Z") { + return 2; + } + return std::nullopt; +} + +// Parse sensor type and data index mapping, as below format +// sensor +// raw abs : the linux abs code of the axis +// sensor type : string name of InputDeviceSensorType +// sensor data index : the data index of sensor, out of [X, Y, Z] +// Examples: +// sensor 0x00 ACCELEROMETER X +// sensor 0x01 ACCELEROMETER Y +// sensor 0x02 ACCELEROMETER Z +// sensor 0x03 GYROSCOPE X +// sensor 0x04 GYROSCOPE Y +// sensor 0x05 GYROSCOPE Z +status_t KeyLayoutMap::Parser::parseSensor() { + String8 codeToken = mTokenizer->nextToken(WHITESPACE); + char* end; + int32_t code = int32_t(strtol(codeToken.string(), &end, 0)); + if (*end) { + ALOGE("%s: Expected sensor %s number, got '%s'.", mTokenizer->getLocation().string(), + "abs code", codeToken.string()); + return BAD_VALUE; + } + + std::unordered_map& map = mMap->mSensorsByAbsCode; + if (map.find(code) != map.end()) { + ALOGE("%s: Duplicate entry for sensor %s '%s'.", mTokenizer->getLocation().string(), + "abs code", codeToken.string()); + return BAD_VALUE; + } + + mTokenizer->skipDelimiters(WHITESPACE); + String8 sensorTypeToken = mTokenizer->nextToken(WHITESPACE); + std::optional typeOpt = getSensorType(sensorTypeToken.string()); + if (!typeOpt) { + ALOGE("%s: Expected sensor code label, got '%s'.", mTokenizer->getLocation().string(), + sensorTypeToken.string()); + return BAD_VALUE; + } + InputDeviceSensorType sensorType = typeOpt.value(); + mTokenizer->skipDelimiters(WHITESPACE); + String8 sensorDataIndexToken = mTokenizer->nextToken(WHITESPACE); + std::optional indexOpt = getSensorDataIndex(sensorDataIndexToken); + if (!indexOpt) { + ALOGE("%s: Expected sensor data index label, got '%s'.", mTokenizer->getLocation().string(), + sensorDataIndexToken.string()); + return BAD_VALUE; + } + int32_t sensorDataIndex = indexOpt.value(); + +#if DEBUG_PARSER + ALOGD("Parsed sensor: abs code=%d, sensorType=%d, sensorDataIndex=%d.", code, + NamedEnum::string(sensorType).c_str(), sensorDataIndex); +#endif + + Sensor sensor; + sensor.sensorType = sensorType; + sensor.sensorDataIndex = sensorDataIndex; + map.emplace(code, sensor); + return NO_ERROR; +} }; diff --git a/libs/input/tests/InputDevice_test.cpp b/libs/input/tests/InputDevice_test.cpp index c174ae94a6..e2cf2456ce 100644 --- a/libs/input/tests/InputDevice_test.cpp +++ b/libs/input/tests/InputDevice_test.cpp @@ -14,9 +14,12 @@ * limitations under the License. */ - +#include +#include #include #include +#include +#include namespace android { @@ -31,4 +34,52 @@ TEST(InputDeviceIdentifierTest, getCanonicalName) { ASSERT_EQ(std::string("deviceName-123_version_C_"), identifier.getCanonicalName()); } +class InputDeviceKeyMapTest : public testing::Test { +protected: + void loadKeyLayout(const char* name) { + std::string path = + getInputDeviceConfigurationFilePathByName(name, + InputDeviceConfigurationFileType:: + KEY_LAYOUT); + ASSERT_FALSE(path.empty()); + base::Result> ret = KeyLayoutMap::load(path); + ASSERT_TRUE(ret) << "Cannot load KeyLayout at " << path; + mKeyMap.keyLayoutMap = std::move(*ret); + mKeyMap.keyLayoutFile = path; + } + + void loadKeyCharacterMap(const char* name) { + InputDeviceIdentifier identifier; + identifier.name = name; + std::string path = + getInputDeviceConfigurationFilePathByName(identifier.getCanonicalName(), + InputDeviceConfigurationFileType:: + KEY_CHARACTER_MAP); + ASSERT_FALSE(path.empty()) << "KeyCharacterMap for " << name << " not found"; + base::Result> ret = + KeyCharacterMap::load(path, KeyCharacterMap::Format::BASE); + ASSERT_TRUE(ret) << "Cannot load KeyCharacterMap at " << path; + mKeyMap.keyCharacterMap = *ret; + mKeyMap.keyCharacterMapFile = path; + } + + virtual void SetUp() override { + loadKeyLayout("Generic"); + loadKeyCharacterMap("Generic"); + } + + virtual void TearDown() override {} + + KeyMap mKeyMap; +}; + +TEST_F(InputDeviceKeyMapTest, keyCharacterMapParcelingTest) { + Parcel parcel; + mKeyMap.keyCharacterMap->writeToParcel(&parcel); + parcel.setDataPosition(0); + std::shared_ptr map = KeyCharacterMap::readFromParcel(&parcel); + // Verify the key character map is the same as original + ASSERT_EQ(*map, *mKeyMap.keyCharacterMap); +} + } // namespace android \ No newline at end of file diff --git a/services/inputflinger/InputClassifier.cpp b/services/inputflinger/InputClassifier.cpp index eafb5ab340..f5f0400995 100644 --- a/services/inputflinger/InputClassifier.cpp +++ b/services/inputflinger/InputClassifier.cpp @@ -391,6 +391,11 @@ void InputClassifier::notifyMotion(const NotifyMotionArgs* args) { mListener->notifyMotion(&newArgs); } +void InputClassifier::notifySensor(const NotifySensorArgs* args) { + // pass through + mListener->notifySensor(args); +} + void InputClassifier::notifySwitch(const NotifySwitchArgs* args) { // pass through mListener->notifySwitch(args); diff --git a/services/inputflinger/InputClassifier.h b/services/inputflinger/InputClassifier.h index 6965940e17..bf10920e61 100644 --- a/services/inputflinger/InputClassifier.h +++ b/services/inputflinger/InputClassifier.h @@ -229,6 +229,7 @@ public: virtual void notifyKey(const NotifyKeyArgs* args) override; virtual void notifyMotion(const NotifyMotionArgs* args) override; virtual void notifySwitch(const NotifySwitchArgs* args) override; + virtual void notifySensor(const NotifySensorArgs* args) override; virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) override; void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) override; diff --git a/services/inputflinger/InputListener.cpp b/services/inputflinger/InputListener.cpp index 49a813e82d..4be49b120f 100644 --- a/services/inputflinger/InputListener.cpp +++ b/services/inputflinger/InputListener.cpp @@ -211,6 +211,41 @@ void NotifySwitchArgs::notify(const sp& listener) const listener->notifySwitch(this); } +// --- NotifySensorArgs --- + +NotifySensorArgs::NotifySensorArgs(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source, + InputDeviceSensorType sensorType, + InputDeviceSensorAccuracy accuracy, bool accuracyChanged, + nsecs_t hwTimestamp, std::vector values) + : NotifyArgs(id, eventTime), + deviceId(deviceId), + source(source), + sensorType(sensorType), + accuracy(accuracy), + accuracyChanged(accuracyChanged), + hwTimestamp(hwTimestamp), + values(std::move(values)) {} + +NotifySensorArgs::NotifySensorArgs(const NotifySensorArgs& other) + : NotifyArgs(other.id, other.eventTime), + deviceId(other.deviceId), + source(other.source), + sensorType(other.sensorType), + accuracy(other.accuracy), + accuracyChanged(other.accuracyChanged), + hwTimestamp(other.hwTimestamp), + values(other.values) {} + +bool NotifySensorArgs::operator==(const NotifySensorArgs rhs) const { + return id == rhs.id && eventTime == rhs.eventTime && sensorType == rhs.sensorType && + accuracy == rhs.accuracy && accuracyChanged == rhs.accuracyChanged && + hwTimestamp == rhs.hwTimestamp && values == rhs.values; +} + +void NotifySensorArgs::notify(const sp& listener) const { + listener->notifySensor(this); +} + // --- NotifyDeviceResetArgs --- NotifyDeviceResetArgs::NotifyDeviceResetArgs(int32_t id, nsecs_t eventTime, int32_t deviceId) @@ -286,6 +321,11 @@ void QueuedInputListener::notifySwitch(const NotifySwitchArgs* args) { mArgsQueue.push_back(new NotifySwitchArgs(*args)); } +void QueuedInputListener::notifySensor(const NotifySensorArgs* args) { + traceEvent(__func__, args->id); + mArgsQueue.push_back(new NotifySensorArgs(*args)); +} + void QueuedInputListener::notifyDeviceReset(const NotifyDeviceResetArgs* args) { traceEvent(__func__, args->id); mArgsQueue.push_back(new NotifyDeviceResetArgs(*args)); diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp index 7c572b4682..4e55872bc6 100644 --- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp +++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp @@ -66,6 +66,13 @@ private: void notifyFocusChanged(const sp&, const sp&) override {} + void notifySensorEvent(int32_t deviceId, InputDeviceSensorType sensorType, + InputDeviceSensorAccuracy accuracy, nsecs_t timestamp, + const std::vector& values) override {} + + void notifySensorAccuracy(int32_t deviceId, InputDeviceSensorType sensorType, + InputDeviceSensorAccuracy accuracy) override {} + void notifyUntrustedTouch(const std::string& obscuringPackage) override {} void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) override { diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp index d8a6548856..f0976b9030 100644 --- a/services/inputflinger/dispatcher/Entry.cpp +++ b/services/inputflinger/dispatcher/Entry.cpp @@ -239,6 +239,41 @@ std::string MotionEntry::getDescription() const { return msg; } +// --- SensorEntry --- + +SensorEntry::SensorEntry(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source, + uint32_t policyFlags, nsecs_t hwTimestamp, + InputDeviceSensorType sensorType, InputDeviceSensorAccuracy accuracy, + bool accuracyChanged, std::vector values) + : EventEntry(id, Type::SENSOR, eventTime, policyFlags), + deviceId(deviceId), + source(source), + sensorType(sensorType), + accuracy(accuracy), + accuracyChanged(accuracyChanged), + hwTimestamp(hwTimestamp), + values(std::move(values)) {} + +SensorEntry::~SensorEntry() {} + +std::string SensorEntry::getDescription() const { + std::string msg; + msg += StringPrintf("SensorEntry(deviceId=%d, source=0x%08x, sensorType=0x%08x, " + "accuracy=0x%08x, hwTimestamp=%" PRId64, + deviceId, source, sensorType, accuracy, hwTimestamp); + + if (!GetBoolProperty("ro.debuggable", false)) { + for (size_t i = 0; i < values.size(); i++) { + if (i > 0) { + msg += ", "; + } + msg += StringPrintf("(%.3f)", values[i]); + } + } + msg += StringPrintf(", policyFlags=0x%08x", policyFlags); + return msg; +} + // --- DispatchEntry --- volatile int32_t DispatchEntry::sNextSeqAtomic; diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h index fba5514625..3a860f05e0 100644 --- a/services/inputflinger/dispatcher/Entry.h +++ b/services/inputflinger/dispatcher/Entry.h @@ -36,26 +36,10 @@ struct EventEntry { FOCUS, KEY, MOTION, + SENSOR, POINTER_CAPTURE_CHANGED, }; - static const char* typeToString(Type type) { - switch (type) { - case Type::CONFIGURATION_CHANGED: - return "CONFIGURATION_CHANGED"; - case Type::DEVICE_RESET: - return "DEVICE_RESET"; - case Type::FOCUS: - return "FOCUS"; - case Type::KEY: - return "KEY"; - case Type::MOTION: - return "MOTION"; - case Type::POINTER_CAPTURE_CHANGED: - return "POINTER_CAPTURE_CHANGED"; - } - } - int32_t id; Type type; nsecs_t eventTime; @@ -191,6 +175,25 @@ struct MotionEntry : EventEntry { virtual ~MotionEntry(); }; +struct SensorEntry : EventEntry { + int32_t deviceId; + uint32_t source; + InputDeviceSensorType sensorType; + InputDeviceSensorAccuracy accuracy; + bool accuracyChanged; + nsecs_t hwTimestamp; + + std::vector values; + + SensorEntry(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source, + uint32_t policyFlags, nsecs_t hwTimestamp, InputDeviceSensorType sensorType, + InputDeviceSensorAccuracy accuracy, bool accuracyChanged, + std::vector values); + std::string getDescription() const override; + + virtual ~SensorEntry(); +}; + // Tracks the progress of dispatching a particular event to a particular connection. struct DispatchEntry { const uint32_t seq; // unique sequence number, never 0 @@ -257,6 +260,7 @@ struct CommandEntry { sp connection; nsecs_t eventTime; std::shared_ptr keyEntry; + std::shared_ptr sensorEntry; std::shared_ptr inputApplicationHandle; std::string reason; int32_t userActivityEventType; diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 6c44a54550..3af33726fd 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -47,8 +47,6 @@ static constexpr bool DEBUG_TOUCH_OCCLUSION = true; // Log debug messages about hover events. #define DEBUG_HOVER 0 -#include "InputDispatcher.h" - #include #include #include @@ -73,6 +71,7 @@ static constexpr bool DEBUG_TOUCH_OCCLUSION = true; #include #include "Connection.h" +#include "InputDispatcher.h" #define INDENT " " #define INDENT2 " " @@ -758,6 +757,23 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { done = dispatchMotionLocked(currentTime, motionEntry, &dropReason, nextWakeupTime); break; } + + case EventEntry::Type::SENSOR: { + std::shared_ptr sensorEntry = + std::static_pointer_cast(mPendingEvent); + if (dropReason == DropReason::NOT_DROPPED && isAppSwitchDue) { + dropReason = DropReason::APP_SWITCH; + } + // Sensor timestamps use SYSTEM_TIME_BOOTTIME time base, so we can't use + // 'currentTime' here, get SYSTEM_TIME_BOOTTIME instead. + nsecs_t bootTime = systemTime(SYSTEM_TIME_BOOTTIME); + if (dropReason == DropReason::NOT_DROPPED && isStaleEvent(bootTime, *sensorEntry)) { + dropReason = DropReason::STALE; + } + dispatchSensorLocked(currentTime, sensorEntry, &dropReason, nextWakeupTime); + done = true; + break; + } } if (done) { @@ -873,6 +889,7 @@ bool InputDispatcher::enqueueInboundEventLocked(std::unique_ptr newE } case EventEntry::Type::CONFIGURATION_CHANGED: case EventEntry::Type::DEVICE_RESET: + case EventEntry::Type::SENSOR: case EventEntry::Type::POINTER_CAPTURE_CHANGED: { // nothing to do break; @@ -883,7 +900,10 @@ bool InputDispatcher::enqueueInboundEventLocked(std::unique_ptr newE } void InputDispatcher::addRecentEventLocked(std::shared_ptr entry) { - mRecentQueue.push_back(entry); + // Do not store sensor event in recent queue to avoid flooding the queue. + if (entry->type != EventEntry::Type::SENSOR) { + mRecentQueue.push_back(entry); + } if (mRecentQueue.size() > RECENT_QUEUE_MAX_SIZE) { mRecentQueue.pop_front(); } @@ -1006,13 +1026,16 @@ void InputDispatcher::dropInboundEventLocked(const EventEntry& entry, DropReason } break; } + case EventEntry::Type::SENSOR: { + break; + } case EventEntry::Type::POINTER_CAPTURE_CHANGED: { break; } case EventEntry::Type::FOCUS: case EventEntry::Type::CONFIGURATION_CHANGED: case EventEntry::Type::DEVICE_RESET: { - LOG_ALWAYS_FATAL("Should not drop %s events", EventEntry::typeToString(entry.type)); + LOG_ALWAYS_FATAL("Should not drop %s events", NamedEnum::string(entry.type).c_str()); break; } } @@ -1365,6 +1388,51 @@ void InputDispatcher::logOutboundKeyDetails(const char* prefix, const KeyEntry& #endif } +void InputDispatcher::doNotifySensorLockedInterruptible(CommandEntry* commandEntry) { + mLock.unlock(); + + const std::shared_ptr& entry = commandEntry->sensorEntry; + if (entry->accuracyChanged) { + mPolicy->notifySensorAccuracy(entry->deviceId, entry->sensorType, entry->accuracy); + } + mPolicy->notifySensorEvent(entry->deviceId, entry->sensorType, entry->accuracy, + entry->hwTimestamp, entry->values); + mLock.lock(); +} + +void InputDispatcher::dispatchSensorLocked(nsecs_t currentTime, std::shared_ptr entry, + DropReason* dropReason, nsecs_t* nextWakeupTime) { +#if DEBUG_OUTBOUND_EVENT_DETAILS + ALOGD("notifySensorEvent eventTime=%" PRId64 ", hwTimestamp=%" PRId64 ", deviceId=%d, " + "source=0x%x, sensorType=%s", + entry->eventTime, entry->hwTimestamp, entry->deviceId, entry->source, + NamedEnum::string(sensorType).c_str()); +#endif + std::unique_ptr commandEntry = + std::make_unique(&InputDispatcher::doNotifySensorLockedInterruptible); + commandEntry->sensorEntry = entry; + postCommandLocked(std::move(commandEntry)); +} + +bool InputDispatcher::flushSensor(int deviceId, InputDeviceSensorType sensorType) { +#if DEBUG_OUTBOUND_EVENT_DETAILS + ALOGD("flushSensor deviceId=%d, sensorType=%s", deviceId, + NamedEnum::string(sensorType).c_str()); +#endif + { // acquire lock + std::scoped_lock _l(mLock); + + for (auto it = mInboundQueue.begin(); it != mInboundQueue.end(); it++) { + std::shared_ptr entry = *it; + if (entry->type == EventEntry::Type::SENSOR) { + it = mInboundQueue.erase(it); + releaseInboundEventLocked(entry); + } + } + } + return true; +} + bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, std::shared_ptr entry, DropReason* dropReason, nsecs_t* nextWakeupTime) { ATRACE_CALL(); @@ -1553,8 +1621,9 @@ int32_t InputDispatcher::getTargetDisplayId(const EventEntry& entry) { case EventEntry::Type::POINTER_CAPTURE_CHANGED: case EventEntry::Type::FOCUS: case EventEntry::Type::CONFIGURATION_CHANGED: - case EventEntry::Type::DEVICE_RESET: { - ALOGE("%s events do not have a target display", EventEntry::typeToString(entry.type)); + case EventEntry::Type::DEVICE_RESET: + case EventEntry::Type::SENSOR: { + ALOGE("%s events do not have a target display", NamedEnum::string(entry.type).c_str()); return ADISPLAY_ID_NONE; } } @@ -1608,7 +1677,7 @@ InputEventInjectionResult InputDispatcher::findFocusedWindowTargetsLocked( 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); + NamedEnum::string(entry.type).c_str(), displayId); return InputEventInjectionResult::FAILED; } @@ -1633,7 +1702,7 @@ InputEventInjectionResult InputDispatcher::findFocusedWindowTargetsLocked( } else if (currentTime > *mNoFocusedWindowTimeoutTime) { // Already raised ANR. Drop the event ALOGE("Dropping %s event because there is no focused window", - EventEntry::typeToString(entry.type)); + NamedEnum::string(entry.type).c_str()); return InputEventInjectionResult::FAILED; } else { // Still waiting for the focused window @@ -2476,9 +2545,10 @@ void InputDispatcher::pokeUserActivityLocked(const EventEntry& eventEntry) { case EventEntry::Type::FOCUS: case EventEntry::Type::CONFIGURATION_CHANGED: case EventEntry::Type::DEVICE_RESET: + case EventEntry::Type::SENSOR: case EventEntry::Type::POINTER_CAPTURE_CHANGED: { LOG_ALWAYS_FATAL("%s events are not user activity", - EventEntry::typeToString(eventEntry.type)); + NamedEnum::string(eventEntry.type).c_str()); break; } } @@ -2522,7 +2592,7 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, if (inputTarget.flags & InputTarget::FLAG_SPLIT) { LOG_ALWAYS_FATAL_IF(eventEntry->type != EventEntry::Type::MOTION, "Entry type %s should not have FLAG_SPLIT", - EventEntry::typeToString(eventEntry->type)); + NamedEnum::string(eventEntry->type).c_str()); const MotionEntry& originalMotionEntry = static_cast(*eventEntry); if (inputTarget.pointerIds.count() != originalMotionEntry.pointerCount) { @@ -2692,10 +2762,14 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp& connectio case EventEntry::Type::POINTER_CAPTURE_CHANGED: { break; } + case EventEntry::Type::SENSOR: { + LOG_ALWAYS_FATAL("SENSOR events should not go to apps via input channel"); + break; + } case EventEntry::Type::CONFIGURATION_CHANGED: case EventEntry::Type::DEVICE_RESET: { LOG_ALWAYS_FATAL("%s events should not go to apps", - EventEntry::typeToString(newEntry.type)); + NamedEnum::string(newEntry.type).c_str()); break; } } @@ -2913,9 +2987,10 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, } case EventEntry::Type::CONFIGURATION_CHANGED: - case EventEntry::Type::DEVICE_RESET: { + case EventEntry::Type::DEVICE_RESET: + case EventEntry::Type::SENSOR: { LOG_ALWAYS_FATAL("Should never start dispatch cycles for %s events", - EventEntry::typeToString(eventEntry.type)); + NamedEnum::string(eventEntry.type).c_str()); return; } } @@ -3209,13 +3284,14 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( case EventEntry::Type::FOCUS: case EventEntry::Type::POINTER_CAPTURE_CHANGED: { LOG_ALWAYS_FATAL("Canceling %s events is not supported", - EventEntry::typeToString(cancelationEventEntry->type)); + NamedEnum::string(cancelationEventEntry->type).c_str()); break; } case EventEntry::Type::CONFIGURATION_CHANGED: - case EventEntry::Type::DEVICE_RESET: { + case EventEntry::Type::DEVICE_RESET: + case EventEntry::Type::SENSOR: { LOG_ALWAYS_FATAL("%s event should not be found inside Connections's queue", - EventEntry::typeToString(cancelationEventEntry->type)); + NamedEnum::string(cancelationEventEntry->type).c_str()); break; } } @@ -3270,9 +3346,10 @@ void InputDispatcher::synthesizePointerDownEventsForConnectionLocked( case EventEntry::Type::FOCUS: case EventEntry::Type::CONFIGURATION_CHANGED: case EventEntry::Type::DEVICE_RESET: - case EventEntry::Type::POINTER_CAPTURE_CHANGED: { + case EventEntry::Type::POINTER_CAPTURE_CHANGED: + case EventEntry::Type::SENSOR: { LOG_ALWAYS_FATAL("%s event should not be found inside Connections's queue", - EventEntry::typeToString(downEventEntry->type)); + NamedEnum::string(downEventEntry->type).c_str()); break; } } @@ -3605,6 +3682,34 @@ void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) { } } +void InputDispatcher::notifySensor(const NotifySensorArgs* args) { +#if DEBUG_INBOUND_EVENT_DETAILS + ALOGD("notifySensor - id=%" PRIx32 " eventTime=%" PRId64 ", deviceId=%d, source=0x%x, " + " sensorType=%s", + args->id, args->eventTime, args->deviceId, args->source, + NamedEnum::string(args->sensorType).c_str()); +#endif + + bool needWake; + { // acquire lock + mLock.lock(); + + // Just enqueue a new sensor event. + std::unique_ptr newEntry = + std::make_unique(args->id, args->eventTime, args->deviceId, + args->source, 0 /* policyFlags*/, args->hwTimestamp, + args->sensorType, args->accuracy, + args->accuracyChanged, args->values); + + needWake = enqueueInboundEventLocked(std::move(newEntry)); + mLock.unlock(); + } // release lock + + if (needWake) { + mLooper->wake(); + } +} + bool InputDispatcher::shouldSendMotionToInputFilterLocked(const NotifyMotionArgs* args) { return mInputFilterEnabled; } diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index 5d37645e40..3004701424 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -94,6 +94,7 @@ public: virtual void notifyKey(const NotifyKeyArgs* args) override; virtual void notifyMotion(const NotifyMotionArgs* args) override; virtual void notifySwitch(const NotifySwitchArgs* args) override; + virtual void notifySensor(const NotifySensorArgs* args) override; virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) override; virtual void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) override; @@ -128,6 +129,7 @@ public: virtual status_t removeInputChannel(const sp& connectionToken) override; virtual status_t pilferPointers(const sp& token) override; virtual void requestPointerCapture(const sp& windowToken, bool enabled) override; + virtual bool flushSensor(int deviceId, InputDeviceSensorType sensorType) override; std::array sign(const VerifiedInputEvent& event) const; @@ -392,7 +394,8 @@ private: DropReason& dropReason) REQUIRES(mLock); void dispatchEventLocked(nsecs_t currentTime, std::shared_ptr entry, const std::vector& inputTargets) REQUIRES(mLock); - + void dispatchSensorLocked(nsecs_t currentTime, std::shared_ptr entry, + DropReason* dropReason, nsecs_t* nextWakeupTime) REQUIRES(mLock); void logOutboundKeyDetails(const char* prefix, const KeyEntry& entry); void logOutboundMotionDetails(const char* prefix, const MotionEntry& entry); @@ -592,6 +595,7 @@ private: REQUIRES(mLock); void doNotifyConnectionResponsiveLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); + void doNotifySensorLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); void doNotifyUntrustedTouchLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); void doInterceptKeyBeforeDispatchingLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); diff --git a/services/inputflinger/dispatcher/InputState.cpp b/services/inputflinger/dispatcher/InputState.cpp index 1656a21b41..3bb0bc995c 100644 --- a/services/inputflinger/dispatcher/InputState.cpp +++ b/services/inputflinger/dispatcher/InputState.cpp @@ -14,6 +14,8 @@ * limitations under the License. */ +#include "input/InputDevice.h" + #include "InputState.h" #include "InputDispatcher.h" diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h index 2909d6940d..6e76c92b72 100644 --- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h +++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -193,6 +194,11 @@ public: * InputDispatcher is the source of truth of Pointer Capture. */ virtual void requestPointerCapture(const sp& windowToken, bool enabled) = 0; + /* Flush input device motion sensor. + * + * Returns true on success. + */ + virtual bool flushSensor(int deviceId, InputDeviceSensorType sensorType) = 0; }; } // namespace android diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h index c1821ab10f..d9ec020a2b 100644 --- a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h +++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h @@ -68,6 +68,11 @@ public: /* Notifies the system that an input channel is unrecoverably broken. */ virtual void notifyInputChannelBroken(const sp& token) = 0; virtual void notifyFocusChanged(const sp& oldToken, const sp& newToken) = 0; + virtual void notifySensorEvent(int32_t deviceId, InputDeviceSensorType sensorType, + InputDeviceSensorAccuracy accuracy, nsecs_t timestamp, + const std::vector& values) = 0; + virtual void notifySensorAccuracy(int32_t deviceId, InputDeviceSensorType sensorType, + InputDeviceSensorAccuracy accuracy) = 0; /* Notifies the system that an untrusted touch occurred. */ virtual void notifyUntrustedTouch(const std::string& obscuringPackage) = 0; diff --git a/services/inputflinger/include/InputListener.h b/services/inputflinger/include/InputListener.h index 58eb91597b..11f726d50c 100644 --- a/services/inputflinger/include/InputListener.h +++ b/services/inputflinger/include/InputListener.h @@ -20,6 +20,7 @@ #include #include +#include #include #include @@ -141,6 +142,30 @@ struct NotifyMotionArgs : public NotifyArgs { virtual void notify(const sp& listener) const; }; +/* Describes a sensor event. */ +struct NotifySensorArgs : public NotifyArgs { + int32_t deviceId; + uint32_t source; + InputDeviceSensorType sensorType; + InputDeviceSensorAccuracy accuracy; + bool accuracyChanged; + nsecs_t hwTimestamp; + std::vector values; + + inline NotifySensorArgs() {} + + NotifySensorArgs(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source, + InputDeviceSensorType sensorType, InputDeviceSensorAccuracy accuracy, + bool accuracyChanged, nsecs_t hwTimestamp, std::vector values); + + NotifySensorArgs(const NotifySensorArgs& other); + + bool operator==(const NotifySensorArgs rhs) const; + + ~NotifySensorArgs() override {} + + void notify(const sp& listener) const override; +}; /* Describes a switch event. */ struct NotifySwitchArgs : public NotifyArgs { @@ -211,6 +236,7 @@ public: virtual void notifyKey(const NotifyKeyArgs* args) = 0; virtual void notifyMotion(const NotifyMotionArgs* args) = 0; virtual void notifySwitch(const NotifySwitchArgs* args) = 0; + virtual void notifySensor(const NotifySensorArgs* args) = 0; virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) = 0; virtual void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) = 0; }; @@ -231,6 +257,7 @@ public: virtual void notifyKey(const NotifyKeyArgs* args) override; virtual void notifyMotion(const NotifyMotionArgs* args) override; virtual void notifySwitch(const NotifySwitchArgs* args) override; + virtual void notifySensor(const NotifySensorArgs* args) override; virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) override; void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) override; diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h index 6cce8ecc7b..ea9b48368e 100644 --- a/services/inputflinger/include/InputReaderBase.h +++ b/services/inputflinger/include/InputReaderBase.h @@ -111,6 +111,17 @@ public: /* Return true if the device can send input events to the specified display. */ virtual bool canDispatchToDisplay(int32_t deviceId, int32_t displayId) = 0; + + /* Enable sensor in input reader mapper. */ + virtual bool enableSensor(int32_t deviceId, InputDeviceSensorType sensorType, + std::chrono::microseconds samplingPeriod, + std::chrono::microseconds maxBatchReportLatency) = 0; + + /* Disable sensor in input reader mapper. */ + virtual void disableSensor(int32_t deviceId, InputDeviceSensorType sensorType) = 0; + + /* Flush sensor data in input reader mapper. */ + virtual void flushSensor(int32_t deviceId, InputDeviceSensorType sensorType) = 0; }; // --- InputReaderConfiguration --- diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp index 0ccada94e4..abda4ef45c 100644 --- a/services/inputflinger/reader/Android.bp +++ b/services/inputflinger/reader/Android.bp @@ -37,6 +37,7 @@ filegroup { "mapper/KeyboardInputMapper.cpp", "mapper/MultiTouchInputMapper.cpp", "mapper/RotaryEncoderInputMapper.cpp", + "mapper/SensorInputMapper.cpp", "mapper/SingleTouchInputMapper.cpp", "mapper/SwitchInputMapper.cpp", "mapper/TouchInputMapper.cpp", diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp index f864c0e5b6..b97ff909d6 100644 --- a/services/inputflinger/reader/EventHub.cpp +++ b/services/inputflinger/reader/EventHub.cpp @@ -157,6 +157,18 @@ Flags getAbsAxisUsage(int32_t axis, Flags de } } + if (deviceClasses.test(InputDeviceClass::SENSOR)) { + switch (axis) { + case ABS_X: + case ABS_Y: + case ABS_Z: + case ABS_RX: + case ABS_RY: + case ABS_RZ: + return InputDeviceClass::SENSOR; + } + } + // External stylus gets the pressure axis if (deviceClasses.test(InputDeviceClass::EXTERNAL_STYLUS)) { if (axis == ABS_PRESSURE) { @@ -250,6 +262,11 @@ void EventHub::Device::configureFd() { // uses the timestamps extensively and assumes they were recorded using the monotonic // clock. int clockId = CLOCK_MONOTONIC; + if (classes.test(InputDeviceClass::SENSOR)) { + // Each new sensor event should use the same time base as + // SystemClock.elapsedRealtimeNanos(). + clockId = CLOCK_BOOTTIME; + } bool usingClockIoctl = !ioctl(fd, EVIOCSCLOCKID, &clockId); ALOGI("usingClockIoctl=%s", toString(usingClockIoctl)); } @@ -550,6 +567,15 @@ bool EventHub::hasInputProperty(int32_t deviceId, int property) const { : false; } +bool EventHub::hasMscEvent(int32_t deviceId, int mscEvent) const { + std::scoped_lock _l(mLock); + + Device* device = getDeviceLocked(deviceId); + return mscEvent >= 0 && mscEvent <= MSC_MAX && device != nullptr + ? device->mscBitmask.test(mscEvent) + : false; +} + int32_t EventHub::getScanCodeState(int32_t deviceId, int32_t scanCode) const { if (scanCode >= 0 && scanCode <= KEY_MAX) { std::scoped_lock _l(mLock); @@ -705,6 +731,17 @@ status_t EventHub::mapAxis(int32_t deviceId, int32_t scanCode, AxisInfo* outAxis return NAME_NOT_FOUND; } +base::Result> EventHub::mapSensor(int32_t deviceId, + int32_t absCode) { + std::scoped_lock _l(mLock); + Device* device = getDeviceLocked(deviceId); + + if (device != nullptr && device->keyMap.haveKeyLayout()) { + return device->keyMap.keyLayoutMap->mapSensor(absCode); + } + return Errorf("Device not found or device has no key layout."); +} + void EventHub::setExcludedDevices(const std::vector& devices) { std::scoped_lock _l(mLock); @@ -1405,6 +1442,7 @@ status_t EventHub::openDeviceLocked(const std::string& devicePath) { device->readDeviceBitMask(EVIOCGBIT(EV_SW, 0), device->swBitmask); device->readDeviceBitMask(EVIOCGBIT(EV_LED, 0), device->ledBitmask); device->readDeviceBitMask(EVIOCGBIT(EV_FF, 0), device->ffBitmask); + device->readDeviceBitMask(EVIOCGBIT(EV_MSC, 0), device->mscBitmask); device->readDeviceBitMask(EVIOCGPROP(0), device->propBitmask); // See if this is a keyboard. Ignore everything in the button range except for @@ -1469,6 +1507,11 @@ status_t EventHub::openDeviceLocked(const std::string& devicePath) { } } + // Check whether this device is an accelerometer. + if (device->propBitmask.test(INPUT_PROP_ACCELEROMETER)) { + device->classes |= InputDeviceClass::SENSOR; + } + // Check whether this device has switches. for (int i = 0; i <= SW_MAX; i++) { if (device->swBitmask.test(i)) { @@ -1493,9 +1536,11 @@ status_t EventHub::openDeviceLocked(const std::string& devicePath) { } // Load the key map. - // We need to do this for joysticks too because the key layout may specify axes. + // We need to do this for joysticks too because the key layout may specify axes, and for + // sensor as well because the key layout may specify the axes to sensor data mapping. status_t keyMapStatus = NAME_NOT_FOUND; - if (device->classes.any(InputDeviceClass::KEYBOARD | InputDeviceClass::JOYSTICK)) { + if (device->classes.any(InputDeviceClass::KEYBOARD | InputDeviceClass::JOYSTICK | + InputDeviceClass::SENSOR)) { // Load the keymap for the device. keyMapStatus = device->loadKeyMapLocked(); } diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp index d25d64a92c..ac72ac4189 100644 --- a/services/inputflinger/reader/InputDevice.cpp +++ b/services/inputflinger/reader/InputDevice.cpp @@ -28,6 +28,7 @@ #include "KeyboardInputMapper.h" #include "MultiTouchInputMapper.h" #include "RotaryEncoderInputMapper.h" +#include "SensorInputMapper.h" #include "SingleTouchInputMapper.h" #include "SwitchInputMapper.h" #include "VibratorInputMapper.h" @@ -196,6 +197,11 @@ void InputDevice::addEventHubDevice(int32_t eventHubId, bool populateMappers) { mappers.push_back(std::make_unique(*contextPtr)); } + // Motion sensor enabled devices. + if (classes.test(InputDeviceClass::SENSOR)) { + mappers.push_back(std::make_unique(*contextPtr)); + } + // External stylus-like devices. if (classes.test(InputDeviceClass::EXTERNAL_STYLUS)) { mappers.push_back(std::make_unique(*contextPtr)); @@ -460,6 +466,25 @@ std::vector InputDevice::getVibratorIds() { return vibrators; } +bool InputDevice::enableSensor(InputDeviceSensorType sensorType, + std::chrono::microseconds samplingPeriod, + std::chrono::microseconds maxBatchReportLatency) { + bool success = true; + for_each_mapper( + [&success, sensorType, samplingPeriod, maxBatchReportLatency](InputMapper& mapper) { + success &= mapper.enableSensor(sensorType, samplingPeriod, maxBatchReportLatency); + }); + return success; +} + +void InputDevice::disableSensor(InputDeviceSensorType sensorType) { + for_each_mapper([sensorType](InputMapper& mapper) { mapper.disableSensor(sensorType); }); +} + +void InputDevice::flushSensor(InputDeviceSensorType sensorType) { + for_each_mapper([sensorType](InputMapper& mapper) { mapper.flushSensor(sensorType); }); +} + void InputDevice::cancelTouch(nsecs_t when) { for_each_mapper([when](InputMapper& mapper) { mapper.cancelTouch(when); }); } diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp index a6b5e2deb8..be21ace221 100644 --- a/services/inputflinger/reader/InputReader.cpp +++ b/services/inputflinger/reader/InputReader.cpp @@ -220,6 +220,11 @@ void InputReader::addDeviceLocked(nsecs_t when, int32_t eventHubId) { if (device->getClasses().test(InputDeviceClass::EXTERNAL_STYLUS)) { notifyExternalStylusPresenceChangedLocked(); } + + // Sensor input device is noisy, to save power disable it by default. + if (device->getClasses().test(InputDeviceClass::SENSOR)) { + mEventHub->disableDevice(eventHubId); + } } void InputReader::removeDeviceLocked(nsecs_t when, int32_t eventHubId) { @@ -639,6 +644,36 @@ std::vector InputReader::getVibratorIds(int32_t deviceId) { return {}; } +void InputReader::disableSensor(int32_t deviceId, InputDeviceSensorType sensorType) { + std::scoped_lock _l(mLock); + + InputDevice* device = findInputDeviceLocked(deviceId); + if (device) { + device->disableSensor(sensorType); + } +} + +bool InputReader::enableSensor(int32_t deviceId, InputDeviceSensorType sensorType, + std::chrono::microseconds samplingPeriod, + std::chrono::microseconds maxBatchReportLatency) { + std::scoped_lock _l(mLock); + + InputDevice* device = findInputDeviceLocked(deviceId); + if (device) { + return device->enableSensor(sensorType, samplingPeriod, maxBatchReportLatency); + } + return false; +} + +void InputReader::flushSensor(int32_t deviceId, InputDeviceSensorType sensorType) { + std::scoped_lock _l(mLock); + + InputDevice* device = findInputDeviceLocked(deviceId); + if (device) { + device->flushSensor(sensorType); + } +} + bool InputReader::isInputDeviceEnabled(int32_t deviceId) { std::scoped_lock _l(mLock); diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h index 9e38d0a72a..2cea01776a 100644 --- a/services/inputflinger/reader/include/EventHub.h +++ b/services/inputflinger/reader/include/EventHub.h @@ -118,6 +118,9 @@ enum class InputDeviceClass : uint32_t { /* The input device has a rotary encoder */ ROTARY_ENCODER = 0x00001000, + /* The input device has a sensor like accelerometer, gyro, etc */ + SENSOR = 0x00002000, + /* The input device is virtual (not a real device, not part of UI configuration). */ VIRTUAL = 0x40000000, @@ -177,6 +180,8 @@ public: virtual bool hasInputProperty(int32_t deviceId, int property) const = 0; + virtual bool hasMscEvent(int32_t deviceId, int mscEvent) const = 0; + virtual status_t mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t metaState, int32_t* outKeycode, int32_t* outMetaState, uint32_t* outFlags) const = 0; @@ -201,6 +206,8 @@ public: */ virtual size_t getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) = 0; virtual std::vector getVideoFrames(int32_t deviceId) = 0; + virtual base::Result> mapSensor(int32_t deviceId, + int32_t absCode) = 0; /* * Query current input state. @@ -346,6 +353,8 @@ public: bool hasInputProperty(int32_t deviceId, int property) const override final; + bool hasMscEvent(int32_t deviceId, int mscEvent) const override final; + status_t mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t metaState, int32_t* outKeycode, int32_t* outMetaState, uint32_t* outFlags) const override final; @@ -353,6 +362,9 @@ public: status_t mapAxis(int32_t deviceId, int32_t scanCode, AxisInfo* outAxisInfo) const override final; + base::Result> mapSensor( + int32_t deviceId, int32_t absCode) override final; + void setExcludedDevices(const std::vector& devices) override final; int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const override final; @@ -420,6 +432,7 @@ private: BitArray ledBitmask; BitArray ffBitmask; BitArray propBitmask; + BitArray mscBitmask; std::string configurationFile; std::unique_ptr configuration; diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h index 8b14b062cd..5af76b7a71 100644 --- a/services/inputflinger/reader/include/InputDevice.h +++ b/services/inputflinger/reader/include/InputDevice.h @@ -87,6 +87,10 @@ public: bool isVibrating(); std::vector getVibratorIds(); void cancelTouch(nsecs_t when); + bool enableSensor(InputDeviceSensorType sensorType, std::chrono::microseconds samplingPeriod, + std::chrono::microseconds maxBatchReportLatency); + void disableSensor(InputDeviceSensorType sensorType); + void flushSensor(InputDeviceSensorType sensorType); int32_t getMetaState(); void updateMetaState(int32_t keyCode); @@ -229,9 +233,12 @@ public: inline bool hasRelativeAxis(int32_t code) const { return mEventHub->hasRelativeAxis(mId, code); } - inline bool hasInputProperty(int property) const { + inline bool hasInputProperty(int32_t property) const { return mEventHub->hasInputProperty(mId, property); } + + inline bool hasMscEvent(int mscEvent) const { return mEventHub->hasMscEvent(mId, mscEvent); } + inline status_t mapKey(int32_t scanCode, int32_t usageCode, int32_t metaState, int32_t* outKeycode, int32_t* outMetaState, uint32_t* outFlags) const { return mEventHub->mapKey(mId, scanCode, usageCode, metaState, outKeycode, outMetaState, @@ -240,6 +247,10 @@ public: inline status_t mapAxis(int32_t scanCode, AxisInfo* outAxisInfo) const { return mEventHub->mapAxis(mId, scanCode, outAxisInfo); } + inline base::Result> mapSensor(int32_t absCode) { + return mEventHub->mapSensor(mId, absCode); + } + inline std::vector getVideoFrames() { return mEventHub->getVideoFrames(mId); } inline int32_t getScanCodeState(int32_t scanCode) const { return mEventHub->getScanCodeState(mId, scanCode); diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h index b16b86caaa..48d4596383 100644 --- a/services/inputflinger/reader/include/InputReader.h +++ b/services/inputflinger/reader/include/InputReader.h @@ -87,6 +87,14 @@ public: bool canDispatchToDisplay(int32_t deviceId, int32_t displayId) override; + bool enableSensor(int32_t deviceId, InputDeviceSensorType sensorType, + std::chrono::microseconds samplingPeriod, + std::chrono::microseconds maxBatchReportLatency) override; + + void disableSensor(int32_t deviceId, InputDeviceSensorType sensorType) override; + + void flushSensor(int32_t deviceId, InputDeviceSensorType sensorType) override; + protected: // These members are protected so they can be instrumented by test cases. virtual std::shared_ptr createDeviceLocked(int32_t deviceId, diff --git a/services/inputflinger/reader/mapper/InputMapper.cpp b/services/inputflinger/reader/mapper/InputMapper.cpp index 913cef72bb..1ce54ae2dd 100644 --- a/services/inputflinger/reader/mapper/InputMapper.cpp +++ b/services/inputflinger/reader/mapper/InputMapper.cpp @@ -70,6 +70,16 @@ std::vector InputMapper::getVibratorIds() { void InputMapper::cancelTouch(nsecs_t when) {} +bool InputMapper::enableSensor(InputDeviceSensorType sensorType, + std::chrono::microseconds samplingPeriod, + std::chrono::microseconds maxBatchReportLatency) { + return true; +} + +void InputMapper::disableSensor(InputDeviceSensorType sensorType) {} + +void InputMapper::flushSensor(InputDeviceSensorType sensorType) {} + int32_t InputMapper::getMetaState() { return 0; } diff --git a/services/inputflinger/reader/mapper/InputMapper.h b/services/inputflinger/reader/mapper/InputMapper.h index 088dbd8615..bd64d8d0a8 100644 --- a/services/inputflinger/reader/mapper/InputMapper.h +++ b/services/inputflinger/reader/mapper/InputMapper.h @@ -68,6 +68,11 @@ public: virtual bool isVibrating(); virtual std::vector getVibratorIds(); virtual void cancelTouch(nsecs_t when); + virtual bool enableSensor(InputDeviceSensorType sensorType, + std::chrono::microseconds samplingPeriod, + std::chrono::microseconds maxBatchReportLatency); + virtual void disableSensor(InputDeviceSensorType sensorType); + virtual void flushSensor(InputDeviceSensorType sensorType); virtual int32_t getMetaState(); virtual void updateMetaState(int32_t keyCode); diff --git a/services/inputflinger/reader/mapper/SensorInputMapper.cpp b/services/inputflinger/reader/mapper/SensorInputMapper.cpp new file mode 100644 index 0000000000..7d97014f55 --- /dev/null +++ b/services/inputflinger/reader/mapper/SensorInputMapper.cpp @@ -0,0 +1,421 @@ +/* + * 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 "../Macros.h" + +#include "SensorInputMapper.h" + +// Log detailed debug messages about each sensor event notification to the dispatcher. +constexpr bool DEBUG_SENSOR_EVENT_DETAILS = false; + +namespace android { + +// Mask for the LSB 2nd, 3rd and fourth bits. +constexpr int REPORTING_MODE_MASK = 0xE; +constexpr int REPORTING_MODE_SHIFT = 1; +constexpr float GRAVITY_MS2_UNIT = 9.80665f; +constexpr float DEGREE_RADIAN_UNIT = 0.0174533f; + +/* Convert the sensor data from Linux to Android + * Linux accelerometer unit is per g, Android unit is m/s^2 + * Linux gyroscope unit is degree/second, Android unit is radians/second + */ +static void convertFromLinuxToAndroid(std::vector& values, + InputDeviceSensorType sensorType) { + for (size_t i = 0; i < values.size(); i++) { + switch (sensorType) { + case InputDeviceSensorType::ACCELEROMETER: + values[i] *= GRAVITY_MS2_UNIT; + break; + case InputDeviceSensorType::GYROSCOPE: + values[i] *= DEGREE_RADIAN_UNIT; + break; + default: + break; + } + } +} + +SensorInputMapper::SensorInputMapper(InputDeviceContext& deviceContext) + : InputMapper(deviceContext) {} + +SensorInputMapper::~SensorInputMapper() {} + +uint32_t SensorInputMapper::getSources() { + return AINPUT_SOURCE_SENSOR; +} + +template +bool SensorInputMapper::tryGetProperty(std::string keyName, T& outValue) { + const auto& config = getDeviceContext().getConfiguration(); + return config.tryGetProperty(String8(keyName.c_str()), outValue); +} + +void SensorInputMapper::parseSensorConfiguration(InputDeviceSensorType sensorType, int32_t absCode, + int32_t sensorDataIndex, const Axis& axis) { + auto it = mSensors.find(sensorType); + if (it == mSensors.end()) { + Sensor sensor = createSensor(sensorType, axis); + sensor.dataVec[sensorDataIndex] = absCode; + mSensors.emplace(sensorType, sensor); + } else { + it->second.dataVec[sensorDataIndex] = absCode; + } +} + +void SensorInputMapper::populateDeviceInfo(InputDeviceInfo* info) { + InputMapper::populateDeviceInfo(info); + + for (const auto& [sensorType, sensor] : mSensors) { + info->addSensorInfo(sensor.sensorInfo); + info->setHasSensor(true); + } +} + +void SensorInputMapper::dump(std::string& dump) { + dump += INDENT2 "Sensor Input Mapper:\n"; + dump += StringPrintf(INDENT3 " isDeviceEnabled %d\n", getDeviceContext().isDeviceEnabled()); + dump += StringPrintf(INDENT3 " mHasHardwareTimestamp %d\n", mHasHardwareTimestamp); + dump += INDENT3 "Sensors:\n"; + for (const auto& [sensorType, sensor] : mSensors) { + dump += StringPrintf(INDENT4 "%s\n", NamedEnum::string(sensorType).c_str()); + dump += StringPrintf(INDENT5 "enabled: %d\n", sensor.enabled); + dump += StringPrintf(INDENT5 "samplingPeriod: %lld\n", sensor.samplingPeriod.count()); + dump += StringPrintf(INDENT5 "maxBatchReportLatency: %lld\n", + sensor.maxBatchReportLatency.count()); + dump += StringPrintf(INDENT5 "maxRange: %f\n", sensor.sensorInfo.maxRange); + dump += StringPrintf(INDENT5 "power: %f\n", sensor.sensorInfo.power); + for (ssize_t i = 0; i < SENSOR_VEC_LEN; i++) { + int32_t rawAxis = sensor.dataVec[i]; + dump += StringPrintf(INDENT5 "[%zd]: rawAxis: %d \n", i, rawAxis); + const auto it = mAxes.find(rawAxis); + if (it != mAxes.end()) { + const Axis& axis = it->second; + dump += StringPrintf(INDENT5 " min=%0.5f, max=%0.5f, flat=%0.5f, fuzz=%0.5f," + "resolution=%0.5f\n", + axis.min, axis.max, axis.flat, axis.fuzz, axis.resolution); + dump += StringPrintf(INDENT5 " scale=%0.5f, offset=%0.5f\n", axis.scale, + axis.offset); + dump += StringPrintf(INDENT5 " rawMin=%d, rawMax=%d, " + "rawFlat=%d, rawFuzz=%d, rawResolution=%d\n", + axis.rawAxisInfo.minValue, axis.rawAxisInfo.maxValue, + axis.rawAxisInfo.flat, axis.rawAxisInfo.fuzz, + axis.rawAxisInfo.resolution); + } + } + } +} + +void SensorInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config, + uint32_t changes) { + InputMapper::configure(when, config, changes); + + if (!changes) { // first time only + mDeviceEnabled = true; + // Check if device has MSC_TIMESTAMP event. + mHasHardwareTimestamp = getDeviceContext().hasMscEvent(MSC_TIMESTAMP); + // Collect all axes. + for (int32_t abs = ABS_X; abs <= ABS_MAX; abs++) { + // axis must be claimed by sensor class device + if (!(getAbsAxisUsage(abs, getDeviceContext().getDeviceClasses()) + .test(InputDeviceClass::SENSOR))) { + continue; + } + RawAbsoluteAxisInfo rawAxisInfo; + getAbsoluteAxisInfo(abs, &rawAxisInfo); + if (rawAxisInfo.valid) { + AxisInfo axisInfo; + // Axis doesn't need to be mapped, as sensor mapper doesn't generate any motion + // input events + axisInfo.mode = AxisInfo::MODE_NORMAL; + axisInfo.axis = -1; + // Check key layout map for sensor data mapping to axes + auto ret = getDeviceContext().mapSensor(abs); + if (ret) { + InputDeviceSensorType sensorType = (*ret).first; + int32_t sensorDataIndex = (*ret).second; + const Axis& axis = createAxis(axisInfo, rawAxisInfo); + parseSensorConfiguration(sensorType, abs, sensorDataIndex, axis); + + mAxes.insert({abs, axis}); + } + } + } + } +} + +SensorInputMapper::Axis SensorInputMapper::createAxis(const AxisInfo& axisInfo, + const RawAbsoluteAxisInfo& rawAxisInfo) { + // Apply flat override. + int32_t rawFlat = axisInfo.flatOverride < 0 ? rawAxisInfo.flat : axisInfo.flatOverride; + + float scale = std::numeric_limits::signaling_NaN(); + float offset = 0; + + // resolution is 1 of sensor's unit. For accelerometer, it is G, for gyroscope, + // it is degree/s. + scale = 1.0f / rawAxisInfo.resolution; + offset = avg(rawAxisInfo.minValue, rawAxisInfo.maxValue) * -scale; + + const float max = rawAxisInfo.maxValue / rawAxisInfo.resolution; + const float min = rawAxisInfo.minValue / rawAxisInfo.resolution; + const float flat = rawFlat * scale; + const float fuzz = rawAxisInfo.fuzz * scale; + const float resolution = rawAxisInfo.resolution; + + // To eliminate noise while the Sensor is at rest, filter out small variations + // in axis values up front. + const float filter = fuzz ? fuzz : flat * 0.25f; + return Axis(rawAxisInfo, axisInfo, scale, offset, min, max, flat, fuzz, resolution, filter); +} + +void SensorInputMapper::reset(nsecs_t when) { + // Recenter all axes. + for (std::pair& pair : mAxes) { + Axis& axis = pair.second; + axis.resetValue(); + } + mHardwareTimestamp = 0; + mPrevMscTime = 0; + InputMapper::reset(when); +} + +SensorInputMapper::Sensor SensorInputMapper::createSensor(InputDeviceSensorType sensorType, + const Axis& axis) { + InputDeviceIdentifier identifier = getDeviceContext().getDeviceIdentifier(); + // Sensor Id will be assigned to device Id to distinguish same sensor from multiple input + // devices, in such a way that the sensor Id will be same as input device Id. + // The sensorType is to distinguish different sensors within one device. + // One input device can only have 1 sensor for each sensor Type. + InputDeviceSensorInfo sensorInfo(identifier.name, std::to_string(identifier.vendor), + identifier.version, sensorType, + InputDeviceSensorAccuracy::ACCURACY_HIGH, + axis.max /* maxRange */, axis.scale /* resolution */, + 0.0f /* power */, 0 /* minDelay */, + 0 /* fifoReservedEventCount */, 0 /* fifoMaxEventCount */, + NamedEnum::string(sensorType), 0 /* maxDelay */, 0 /* flags */, + getDeviceId()); + + std::string prefix = "sensor." + NamedEnum::string(sensorType); + transform(prefix.begin(), prefix.end(), prefix.begin(), ::tolower); + + int32_t reportingMode = 0; + if (!tryGetProperty(prefix + ".reportingMode", reportingMode)) { + sensorInfo.flags |= (reportingMode & REPORTING_MODE_MASK) << REPORTING_MODE_SHIFT; + } + + tryGetProperty(prefix + ".maxDelay", sensorInfo.maxDelay); + + tryGetProperty(prefix + ".minDelay", sensorInfo.minDelay); + + tryGetProperty(prefix + ".power", sensorInfo.power); + + tryGetProperty(prefix + ".fifoReservedEventCount", sensorInfo.fifoReservedEventCount); + + tryGetProperty(prefix + ".fifoMaxEventCount", sensorInfo.fifoMaxEventCount); + + return Sensor(sensorInfo); +} + +void SensorInputMapper::processHardWareTimestamp(nsecs_t evTime, int32_t mscTime) { + // Since MSC_TIMESTAMP initial state is different from the system time, we + // calculate the difference between two MSC_TIMESTAMP events, and use that + // to calculate the system time that should be tagged on the event. + // if the first time MSC_TIMESTAMP, store it + // else calculate difference between previous and current MSC_TIMESTAMP + if (mPrevMscTime == 0) { + mHardwareTimestamp = evTime; + if (DEBUG_SENSOR_EVENT_DETAILS) { + ALOGD("Initialize hardware timestamp = %" PRId64, mHardwareTimestamp); + } + } else { + // Calculate the difference between current msc_timestamp and + // previous msc_timestamp, including when msc_timestamp wraps around. + uint32_t timeDiff = (mPrevMscTime > static_cast(mscTime)) + ? (UINT32_MAX - mPrevMscTime + static_cast(mscTime + 1)) + : (static_cast(mscTime) - mPrevMscTime); + + mHardwareTimestamp += timeDiff * 1000LL; + } + mPrevMscTime = static_cast(mscTime); +} + +void SensorInputMapper::process(const RawEvent* rawEvent) { + switch (rawEvent->type) { + case EV_ABS: { + auto it = mAxes.find(rawEvent->code); + if (it != mAxes.end()) { + Axis& axis = it->second; + axis.newValue = rawEvent->value * axis.scale + axis.offset; + } + break; + } + + case EV_SYN: + switch (rawEvent->code) { + case SYN_REPORT: + for (std::pair& pair : mAxes) { + Axis& axis = pair.second; + axis.currentValue = axis.newValue; + } + sync(rawEvent->when, false /*force*/); + break; + } + break; + + case EV_MSC: + switch (rawEvent->code) { + case MSC_TIMESTAMP: + // hardware timestamp is nano seconds + processHardWareTimestamp(rawEvent->when, rawEvent->value); + break; + } + } +} + +bool SensorInputMapper::setSensorEnabled(InputDeviceSensorType sensorType, bool enabled) { + auto it = mSensors.find(sensorType); + if (it == mSensors.end()) { + return false; + } + + it->second.enabled = enabled; + if (!enabled) { + it->second.resetValue(); + } + + /* Currently we can't enable/disable sensors individually. Enabling any sensor will enable + * the device + */ + mDeviceEnabled = false; + for (const auto& [sensorType, sensor] : mSensors) { + // If any sensor is on we will turn on the device. + if (sensor.enabled) { + mDeviceEnabled = true; + break; + } + } + return true; +} + +void SensorInputMapper::flushSensor(InputDeviceSensorType sensorType) { + auto it = mSensors.find(sensorType); + if (it == mSensors.end()) { + return; + } + auto& sensor = it->second; + sensor.lastSampleTimeNs = 0; + for (size_t i = 0; i < SENSOR_VEC_LEN; i++) { + int32_t abs = sensor.dataVec[i]; + auto itAxis = mAxes.find(abs); + if (itAxis != mAxes.end()) { + Axis& axis = itAxis->second; + axis.resetValue(); + } + } +} + +bool SensorInputMapper::enableSensor(InputDeviceSensorType sensorType, + std::chrono::microseconds samplingPeriod, + std::chrono::microseconds maxBatchReportLatency) { + if (DEBUG_SENSOR_EVENT_DETAILS) { + ALOGD("Enable Sensor %s samplingPeriod %lld maxBatchReportLatency %lld", + NamedEnum::string(sensorType).c_str(), samplingPeriod.count(), + maxBatchReportLatency.count()); + } + + if (!setSensorEnabled(sensorType, true /* enabled */)) { + return false; + } + + // Enable device + if (mDeviceEnabled) { + getDeviceContext().enableDevice(); + } + + // We know the sensor exists now, update the sampling period and batch report latency. + auto it = mSensors.find(sensorType); + it->second.samplingPeriod = + std::chrono::duration_cast(samplingPeriod); + it->second.maxBatchReportLatency = + std::chrono::duration_cast(maxBatchReportLatency); + return true; +} + +void SensorInputMapper::disableSensor(InputDeviceSensorType sensorType) { + if (DEBUG_SENSOR_EVENT_DETAILS) { + ALOGD("Disable Sensor %s", NamedEnum::string(sensorType).c_str()); + } + + if (!setSensorEnabled(sensorType, false /* enabled */)) { + return; + } + + // Disable device + if (!mDeviceEnabled) { + mHardwareTimestamp = 0; + mPrevMscTime = 0; + getDeviceContext().disableDevice(); + } +} + +void SensorInputMapper::sync(nsecs_t when, bool force) { + for (auto& [sensorType, sensor] : mSensors) { + // Skip if sensor not enabled + if (!sensor.enabled) { + continue; + } + std::vector values; + for (ssize_t i = 0; i < SENSOR_VEC_LEN; i++) { + int32_t abs = sensor.dataVec[i]; + auto it = mAxes.find(abs); + if (it != mAxes.end()) { + const Axis& axis = it->second; + values.push_back(axis.currentValue); + } + } + + nsecs_t timestamp = mHasHardwareTimestamp ? mHardwareTimestamp : when; + if (DEBUG_SENSOR_EVENT_DETAILS) { + ALOGD("Sensor %s timestamp %" PRIu64 " values [%f %f %f]", + NamedEnum::string(sensorType).c_str(), timestamp, values[0], values[1], + values[2]); + } + if (sensor.lastSampleTimeNs.has_value() && + timestamp - sensor.lastSampleTimeNs.value() < sensor.samplingPeriod.count()) { + if (DEBUG_SENSOR_EVENT_DETAILS) { + ALOGD("Sensor %s Skip a sample.", NamedEnum::string(sensorType).c_str()); + } + } else { + // Convert to Android unit + convertFromLinuxToAndroid(values, sensorType); + // Notify dispatcher for sensor event + NotifySensorArgs args(getContext()->getNextId(), when, getDeviceId(), + AINPUT_SOURCE_SENSOR, sensorType, sensor.sensorInfo.accuracy, + sensor.accuracy != + sensor.sensorInfo.accuracy /* accuracyChanged */, + timestamp /* hwTimestamp */, values); + + getListener()->notifySensor(&args); + sensor.lastSampleTimeNs = timestamp; + sensor.accuracy = sensor.sensorInfo.accuracy; + } + } +} + +} // namespace android diff --git a/services/inputflinger/reader/mapper/SensorInputMapper.h b/services/inputflinger/reader/mapper/SensorInputMapper.h new file mode 100644 index 0000000000..337100216e --- /dev/null +++ b/services/inputflinger/reader/mapper/SensorInputMapper.h @@ -0,0 +1,138 @@ +/* + * 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_INPUTREADER_SENSOR_INPUT_MAPPER_H +#define _UI_INPUTREADER_SENSOR_INPUT_MAPPER_H + +#include "InputMapper.h" + +namespace android { +// sensor data vector length +static constexpr ssize_t SENSOR_VEC_LEN = 3; + +class SensorInputMapper : public InputMapper { +public: + explicit SensorInputMapper(InputDeviceContext& deviceContext); + ~SensorInputMapper() override; + + uint32_t getSources() override; + void populateDeviceInfo(InputDeviceInfo* deviceInfo) override; + void dump(std::string& dump) override; + void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes) override; + void reset(nsecs_t when) override; + void process(const RawEvent* rawEvent) override; + bool enableSensor(InputDeviceSensorType sensorType, std::chrono::microseconds samplingPeriod, + std::chrono::microseconds maxBatchReportLatency) override; + void disableSensor(InputDeviceSensorType sensorType) override; + void flushSensor(InputDeviceSensorType sensorType) override; + +private: + struct Axis { + explicit Axis(const RawAbsoluteAxisInfo& rawAxisInfo, const AxisInfo& axisInfo, float scale, + float offset, float min, float max, float flat, float fuzz, float resolution, + float filter) + : rawAxisInfo(rawAxisInfo), + axisInfo(axisInfo), + scale(scale), + offset(offset), + min(min), + max(max), + flat(flat), + fuzz(fuzz), + resolution(resolution), + filter(filter) { + resetValue(); + } + + RawAbsoluteAxisInfo rawAxisInfo; + AxisInfo axisInfo; + + float scale; // scale factor from raw to normalized values + float offset; // offset to add after scaling for normalization + + float min; // normalized inclusive minimum + float max; // normalized inclusive maximum + float flat; // normalized flat region size + float fuzz; // normalized error tolerance + float resolution; // normalized resolution in units + + float filter; // filter out small variations of this size + float currentValue; // current value + float newValue; // most recent value + + void resetValue() { + this->currentValue = 0; + this->newValue = 0; + } + }; + + struct Sensor { + explicit Sensor(const InputDeviceSensorInfo& sensorInfo) : sensorInfo(sensorInfo) { + resetValue(); + } + bool enabled; + InputDeviceSensorAccuracy accuracy; + std::chrono::nanoseconds samplingPeriod; + std::chrono::nanoseconds maxBatchReportLatency; + // last sample time in nano seconds + std::optional lastSampleTimeNs; + InputDeviceSensorInfo sensorInfo; + // Sensor X, Y, Z data mapping to abs + std::array dataVec; + void resetValue() { + this->enabled = false; + this->accuracy = InputDeviceSensorAccuracy::ACCURACY_NONE; + this->samplingPeriod = std::chrono::nanoseconds(0); + this->maxBatchReportLatency = std::chrono::nanoseconds(0); + this->lastSampleTimeNs = std::nullopt; + this->dataVec.fill(0); + } + }; + + static Axis createAxis(const AxisInfo& AxisInfo, const RawAbsoluteAxisInfo& rawAxisInfo); + + // Axes indexed by raw ABS_* axis index. + std::unordered_map mAxes; + + // hardware timestamp from MSC_TIMESTAMP + nsecs_t mHardwareTimestamp; + uint32_t mPrevMscTime; + + bool mDeviceEnabled; + // Does device support MSC_TIMESTAMP + bool mHasHardwareTimestamp; + + // Sensor list + std::unordered_map mSensors; + + void sync(nsecs_t when, bool force); + + template + bool tryGetProperty(std::string keyName, T& outValue); + + void parseSensorConfiguration(InputDeviceSensorType sensorType, int32_t absCode, + int32_t sensorDataIndex, const Axis& axis); + + void processHardWareTimestamp(nsecs_t evTime, int32_t evValue); + + Sensor createSensor(InputDeviceSensorType sensorType, const Axis& axis); + + bool setSensorEnabled(InputDeviceSensorType sensorType, bool enabled); +}; + +} // namespace android + +#endif // _UI_INPUTREADER_SENSOR_INPUT_MAPPER_H \ No newline at end of file diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 76328148c2..c819091ae0 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -284,6 +284,12 @@ private: void notifyFocusChanged(const sp&, const sp&) override {} void notifyUntrustedTouch(const std::string& obscuringPackage) override {} + void notifySensorEvent(int32_t deviceId, InputDeviceSensorType sensorType, + InputDeviceSensorAccuracy accuracy, nsecs_t timestamp, + const std::vector& values) override {} + + void notifySensorAccuracy(int deviceId, InputDeviceSensorType sensorType, + InputDeviceSensorAccuracy accuracy) override {} void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) override { *outConfig = mConfig; diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index c26a389342..23f30260bc 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -364,6 +365,11 @@ class FakeEventHub : public EventHubInterface { uint32_t flags; }; + struct SensorInfo { + InputDeviceSensorType sensorType; + int32_t sensorDataIndex; + }; + struct Device { InputDeviceIdentifier identifier; Flags classes; @@ -377,6 +383,8 @@ class FakeEventHub : public EventHubInterface { KeyedVector keysByScanCode; KeyedVector keysByUsageCode; KeyedVector leds; + std::unordered_map sensorsByAbsCode; + BitArray mscBitmask; std::vector virtualKeys; bool enabled; @@ -535,6 +543,22 @@ public: device->leds.add(led, initialState); } + void addSensorAxis(int32_t deviceId, int32_t absCode, InputDeviceSensorType sensorType, + int32_t sensorDataIndex) { + Device* device = getDevice(deviceId); + SensorInfo info; + info.sensorType = sensorType; + info.sensorDataIndex = sensorDataIndex; + device->sensorsByAbsCode.emplace(absCode, info); + } + + void setMscEvent(int32_t deviceId, int32_t mscEvent) { + Device* device = getDevice(deviceId); + typename BitArray::Buffer buffer; + buffer[mscEvent / 32] = 1 << mscEvent % 32; + device->mscBitmask.loadFromBuffer(buffer); + } + bool getLedState(int32_t deviceId, int32_t led) { Device* device = getDevice(deviceId); return device->leds.valueFor(led); @@ -630,6 +654,14 @@ private: bool hasInputProperty(int32_t, int) const override { return false; } + bool hasMscEvent(int32_t deviceId, int mscEvent) const override final { + Device* device = getDevice(deviceId); + if (device) { + return mscEvent >= 0 && mscEvent <= MSC_MAX ? device->mscBitmask.test(mscEvent) : false; + } + return false; + } + status_t mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t metaState, int32_t* outKeycode, int32_t* outMetaState, uint32_t* outFlags) const override { Device* device = getDevice(deviceId); @@ -669,6 +701,20 @@ private: status_t mapAxis(int32_t, int32_t, AxisInfo*) const override { return NAME_NOT_FOUND; } + base::Result> mapSensor(int32_t deviceId, + int32_t absCode) { + Device* device = getDevice(deviceId); + if (!device) { + return Errorf("Sensor device not found."); + } + auto it = device->sensorsByAbsCode.find(absCode); + if (it == device->sensorsByAbsCode.end()) { + return Errorf("Sensor map not found."); + } + const SensorInfo& info = it->second; + return std::make_pair(info.sensorType, info.sensorDataIndex); + } + void setExcludedDevices(const std::vector& devices) override { mExcludedDevices = devices; } @@ -2574,6 +2620,157 @@ TEST_F(VibratorInputMapperTest, Vibrate) { ASSERT_TRUE(mapper.isVibrating()); } +// --- SensorInputMapperTest --- + +class SensorInputMapperTest : public InputMapperTest { +protected: + static const int32_t ACCEL_RAW_MIN; + static const int32_t ACCEL_RAW_MAX; + static const int32_t ACCEL_RAW_FUZZ; + static const int32_t ACCEL_RAW_FLAT; + static const int32_t ACCEL_RAW_RESOLUTION; + + static const int32_t GYRO_RAW_MIN; + static const int32_t GYRO_RAW_MAX; + static const int32_t GYRO_RAW_FUZZ; + static const int32_t GYRO_RAW_FLAT; + static const int32_t GYRO_RAW_RESOLUTION; + + static const float GRAVITY_MS2_UNIT; + static const float DEGREE_RADIAN_UNIT; + + void prepareAccelAxes(); + void prepareGyroAxes(); + void setAccelProperties(); + void setGyroProperties(); + void SetUp() override { InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::SENSOR); } +}; + +const int32_t SensorInputMapperTest::ACCEL_RAW_MIN = -32768; +const int32_t SensorInputMapperTest::ACCEL_RAW_MAX = 32768; +const int32_t SensorInputMapperTest::ACCEL_RAW_FUZZ = 16; +const int32_t SensorInputMapperTest::ACCEL_RAW_FLAT = 0; +const int32_t SensorInputMapperTest::ACCEL_RAW_RESOLUTION = 8192; + +const int32_t SensorInputMapperTest::GYRO_RAW_MIN = -2097152; +const int32_t SensorInputMapperTest::GYRO_RAW_MAX = 2097152; +const int32_t SensorInputMapperTest::GYRO_RAW_FUZZ = 16; +const int32_t SensorInputMapperTest::GYRO_RAW_FLAT = 0; +const int32_t SensorInputMapperTest::GYRO_RAW_RESOLUTION = 1024; + +const float SensorInputMapperTest::GRAVITY_MS2_UNIT = 9.80665f; +const float SensorInputMapperTest::DEGREE_RADIAN_UNIT = 0.0174533f; + +void SensorInputMapperTest::prepareAccelAxes() { + mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_X, ACCEL_RAW_MIN, ACCEL_RAW_MAX, ACCEL_RAW_FUZZ, + ACCEL_RAW_FLAT, ACCEL_RAW_RESOLUTION); + mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_Y, ACCEL_RAW_MIN, ACCEL_RAW_MAX, ACCEL_RAW_FUZZ, + ACCEL_RAW_FLAT, ACCEL_RAW_RESOLUTION); + mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_Z, ACCEL_RAW_MIN, ACCEL_RAW_MAX, ACCEL_RAW_FUZZ, + ACCEL_RAW_FLAT, ACCEL_RAW_RESOLUTION); +} + +void SensorInputMapperTest::prepareGyroAxes() { + mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_RX, GYRO_RAW_MIN, GYRO_RAW_MAX, GYRO_RAW_FUZZ, + GYRO_RAW_FLAT, GYRO_RAW_RESOLUTION); + mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_RY, GYRO_RAW_MIN, GYRO_RAW_MAX, GYRO_RAW_FUZZ, + GYRO_RAW_FLAT, GYRO_RAW_RESOLUTION); + mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_RZ, GYRO_RAW_MIN, GYRO_RAW_MAX, GYRO_RAW_FUZZ, + GYRO_RAW_FLAT, GYRO_RAW_RESOLUTION); +} + +void SensorInputMapperTest::setAccelProperties() { + mFakeEventHub->addSensorAxis(EVENTHUB_ID, /* absCode */ 0, InputDeviceSensorType::ACCELEROMETER, + /* sensorDataIndex */ 0); + mFakeEventHub->addSensorAxis(EVENTHUB_ID, /* absCode */ 1, InputDeviceSensorType::ACCELEROMETER, + /* sensorDataIndex */ 1); + mFakeEventHub->addSensorAxis(EVENTHUB_ID, /* absCode */ 2, InputDeviceSensorType::ACCELEROMETER, + /* sensorDataIndex */ 2); + mFakeEventHub->setMscEvent(EVENTHUB_ID, MSC_TIMESTAMP); + addConfigurationProperty("sensor.accelerometer.reportingMode", "0"); + addConfigurationProperty("sensor.accelerometer.maxDelay", "100000"); + addConfigurationProperty("sensor.accelerometer.minDelay", "5000"); + addConfigurationProperty("sensor.accelerometer.power", "1.5"); +} + +void SensorInputMapperTest::setGyroProperties() { + mFakeEventHub->addSensorAxis(EVENTHUB_ID, /* absCode */ 3, InputDeviceSensorType::GYROSCOPE, + /* sensorDataIndex */ 0); + mFakeEventHub->addSensorAxis(EVENTHUB_ID, /* absCode */ 4, InputDeviceSensorType::GYROSCOPE, + /* sensorDataIndex */ 1); + mFakeEventHub->addSensorAxis(EVENTHUB_ID, /* absCode */ 5, InputDeviceSensorType::GYROSCOPE, + /* sensorDataIndex */ 2); + mFakeEventHub->setMscEvent(EVENTHUB_ID, MSC_TIMESTAMP); + addConfigurationProperty("sensor.gyroscope.reportingMode", "0"); + addConfigurationProperty("sensor.gyroscope.maxDelay", "100000"); + addConfigurationProperty("sensor.gyroscope.minDelay", "5000"); + addConfigurationProperty("sensor.gyroscope.power", "0.8"); +} + +TEST_F(SensorInputMapperTest, GetSources) { + SensorInputMapper& mapper = addMapperAndConfigure(); + + ASSERT_EQ(static_cast(AINPUT_SOURCE_SENSOR), mapper.getSources()); +} + +TEST_F(SensorInputMapperTest, ProcessAccelerometerSensor) { + setAccelProperties(); + prepareAccelAxes(); + SensorInputMapper& mapper = addMapperAndConfigure(); + + ASSERT_TRUE(mapper.enableSensor(InputDeviceSensorType::ACCELEROMETER, + std::chrono::microseconds(10000), + std::chrono::microseconds(0))); + process(mapper, ARBITRARY_TIME, EV_ABS, ABS_X, 20000); + process(mapper, ARBITRARY_TIME, EV_ABS, ABS_Y, -20000); + process(mapper, ARBITRARY_TIME, EV_ABS, ABS_Z, 40000); + process(mapper, ARBITRARY_TIME, EV_MSC, MSC_TIMESTAMP, 1000); + process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + + NotifySensorArgs args; + std::vector values = {20000.0f / ACCEL_RAW_RESOLUTION * GRAVITY_MS2_UNIT, + -20000.0f / ACCEL_RAW_RESOLUTION * GRAVITY_MS2_UNIT, + 40000.0f / ACCEL_RAW_RESOLUTION * GRAVITY_MS2_UNIT}; + + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifySensorWasCalled(&args)); + ASSERT_EQ(args.source, AINPUT_SOURCE_SENSOR); + ASSERT_EQ(args.deviceId, DEVICE_ID); + ASSERT_EQ(args.sensorType, InputDeviceSensorType::ACCELEROMETER); + ASSERT_EQ(args.accuracy, InputDeviceSensorAccuracy::ACCURACY_HIGH); + ASSERT_EQ(args.hwTimestamp, ARBITRARY_TIME); + ASSERT_EQ(args.values, values); + mapper.flushSensor(InputDeviceSensorType::ACCELEROMETER); +} + +TEST_F(SensorInputMapperTest, ProcessGyroscopeSensor) { + setGyroProperties(); + prepareGyroAxes(); + SensorInputMapper& mapper = addMapperAndConfigure(); + + ASSERT_TRUE(mapper.enableSensor(InputDeviceSensorType::GYROSCOPE, + std::chrono::microseconds(10000), + std::chrono::microseconds(0))); + process(mapper, ARBITRARY_TIME, EV_ABS, ABS_RX, 20000); + process(mapper, ARBITRARY_TIME, EV_ABS, ABS_RY, -20000); + process(mapper, ARBITRARY_TIME, EV_ABS, ABS_RZ, 40000); + process(mapper, ARBITRARY_TIME, EV_MSC, MSC_TIMESTAMP, 1000); + process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + + NotifySensorArgs args; + std::vector values = {20000.0f / GYRO_RAW_RESOLUTION * DEGREE_RADIAN_UNIT, + -20000.0f / GYRO_RAW_RESOLUTION * DEGREE_RADIAN_UNIT, + 40000.0f / GYRO_RAW_RESOLUTION * DEGREE_RADIAN_UNIT}; + + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifySensorWasCalled(&args)); + ASSERT_EQ(args.source, AINPUT_SOURCE_SENSOR); + ASSERT_EQ(args.deviceId, DEVICE_ID); + ASSERT_EQ(args.sensorType, InputDeviceSensorType::GYROSCOPE); + ASSERT_EQ(args.accuracy, InputDeviceSensorAccuracy::ACCURACY_HIGH); + ASSERT_EQ(args.hwTimestamp, ARBITRARY_TIME); + ASSERT_EQ(args.values, values); + mapper.flushSensor(InputDeviceSensorType::GYROSCOPE); +} + // --- KeyboardInputMapperTest --- class KeyboardInputMapperTest : public InputMapperTest { diff --git a/services/inputflinger/tests/TestInputListener.cpp b/services/inputflinger/tests/TestInputListener.cpp index 1050ab8601..295c6e3057 100644 --- a/services/inputflinger/tests/TestInputListener.cpp +++ b/services/inputflinger/tests/TestInputListener.cpp @@ -80,6 +80,12 @@ void TestInputListener::assertNotifySwitchWasCalled(NotifySwitchArgs* outEventAr "Expected notifySwitch() to have been called.")); } +void TestInputListener::assertNotifySensorWasCalled(NotifySensorArgs* outEventArgs) { + ASSERT_NO_FATAL_FAILURE( + assertCalled(outEventArgs, + "Expected notifySensor() to have been called.")); +} + void TestInputListener::assertNotifyCaptureWasCalled( NotifyPointerCaptureChangedArgs* outEventArgs) { ASSERT_NO_FATAL_FAILURE( @@ -155,4 +161,8 @@ void TestInputListener::notifyPointerCaptureChanged(const NotifyPointerCaptureCh notify(args); } +void TestInputListener::notifySensor(const NotifySensorArgs* args) { + notify(args); +} + } // namespace android diff --git a/services/inputflinger/tests/TestInputListener.h b/services/inputflinger/tests/TestInputListener.h index 887d4eadb1..e54350a4c8 100644 --- a/services/inputflinger/tests/TestInputListener.h +++ b/services/inputflinger/tests/TestInputListener.h @@ -55,6 +55,7 @@ public: void assertNotifySwitchWasCalled(NotifySwitchArgs* outEventArgs = nullptr); void assertNotifyCaptureWasCalled(NotifyPointerCaptureChangedArgs* outEventArgs = nullptr); + void assertNotifySensorWasCalled(NotifySensorArgs* outEventArgs = nullptr); private: template @@ -76,6 +77,8 @@ private: virtual void notifySwitch(const NotifySwitchArgs* args) override; + virtual void notifySensor(const NotifySensorArgs* args) override; + virtual void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) override; std::mutex mLock; @@ -88,6 +91,7 @@ private: std::vector, // std::vector, // std::vector, // + std::vector, // std::vector> // mQueues GUARDED_BY(mLock); }; -- cgit v1.2.3-59-g8ed1b From 0230344dcd32e367dd8ac232155972765343bbac Mon Sep 17 00:00:00 2001 From: Chris Ye Date: Tue, 26 Jan 2021 13:21:01 -0800 Subject: Update SOURCE_SENSOR source for input device. Update the SOURCE_SENSOR input source to not limited to Joystick deivce class. Bug: 170131554 Test: atest InputDeviceSensorManagerTest Change-Id: I08cb72f22d9a06f63139317551c821fd5eaa3e46 Merged-In: I08cb72f22d9a06f63139317551c821fd5eaa3e46 --- include/android/input.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'include/android/input.h') diff --git a/include/android/input.h b/include/android/input.h index b5d399ee09..b70d42427d 100644 --- a/include/android/input.h +++ b/include/android/input.h @@ -856,8 +856,10 @@ enum { AINPUT_SOURCE_TOUCH_NAVIGATION = 0x00200000 | AINPUT_SOURCE_CLASS_NONE, /** joystick */ AINPUT_SOURCE_JOYSTICK = 0x01000000 | AINPUT_SOURCE_CLASS_JOYSTICK, + /** HDMI */ + AINPUT_SOURCE_HDMI = 0x02000000 | AINPUT_SOURCE_CLASS_BUTTON, /** sensor */ - AINPUT_SOURCE_SENSOR = 0x02000000 | AINPUT_SOURCE_CLASS_JOYSTICK, + AINPUT_SOURCE_SENSOR = 0x04000000 | AINPUT_SOURCE_UNKNOWN, /** rotary encoder */ AINPUT_SOURCE_ROTARY_ENCODER = 0x00400000 | AINPUT_SOURCE_CLASS_NONE, -- cgit v1.2.3-59-g8ed1b From 3fdbfef367514df07b0cf158fc24fe72aca0541f Mon Sep 17 00:00:00 2001 From: Chris Ye Date: Wed, 6 Jan 2021 18:45:18 -0800 Subject: Support Inputdevice LightsManager feature in frameworks. Add lights manager support to input frameworks. Bug: 161633625 Test: atest LightsManagerTest, atest InputDeviceLightsManagerTest Change-Id: Ie00357bce0f6c98e9eada5e0a79f93f48e7a4d1b --- include/android/input.h | 2 +- include/input/InputDevice.h | 28 ++ libs/input/InputDevice.cpp | 27 +- services/inputflinger/include/InputReaderBase.h | 13 + services/inputflinger/reader/Android.bp | 1 + services/inputflinger/reader/EventHub.cpp | 347 +++++++++++++-- services/inputflinger/reader/InputDevice.cpp | 44 +- services/inputflinger/reader/InputReader.cpp | 64 +++ services/inputflinger/reader/include/EventHub.h | 78 +++- services/inputflinger/reader/include/InputDevice.h | 29 ++ services/inputflinger/reader/include/InputReader.h | 12 + .../reader/mapper/BatteryInputMapper.cpp | 2 +- services/inputflinger/reader/mapper/InputMapper.h | 5 + .../reader/mapper/LightInputMapper.cpp | 478 +++++++++++++++++++++ .../inputflinger/reader/mapper/LightInputMapper.h | 135 ++++++ services/inputflinger/tests/InputReader_test.cpp | 239 ++++++++++- 16 files changed, 1465 insertions(+), 39 deletions(-) create mode 100644 services/inputflinger/reader/mapper/LightInputMapper.cpp create mode 100644 services/inputflinger/reader/mapper/LightInputMapper.h (limited to 'include/android/input.h') diff --git a/include/android/input.h b/include/android/input.h index b70d42427d..797348742d 100644 --- a/include/android/input.h +++ b/include/android/input.h @@ -859,7 +859,7 @@ enum { /** HDMI */ AINPUT_SOURCE_HDMI = 0x02000000 | AINPUT_SOURCE_CLASS_BUTTON, /** sensor */ - AINPUT_SOURCE_SENSOR = 0x04000000 | AINPUT_SOURCE_UNKNOWN, + AINPUT_SOURCE_SENSOR = 0x04000000 | AINPUT_SOURCE_CLASS_NONE, /** rotary encoder */ AINPUT_SOURCE_ROTARY_ENCODER = 0x00400000 | AINPUT_SOURCE_CLASS_NONE, diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h index 2bd7bd2004..2deb99d154 100644 --- a/include/input/InputDevice.h +++ b/include/input/InputDevice.h @@ -100,6 +100,13 @@ enum class InputDeviceSensorReportingMode : int32_t { SPECIAL_TRIGGER = 3, }; +enum class InputDeviceLightType : int32_t { + SINGLE = 0, + PLAYER_ID = 1, + RGB = 2, + MULTI_COLOR = 3, +}; + struct InputDeviceSensorInfo { explicit InputDeviceSensorInfo(std::string name, std::string vendor, int32_t version, InputDeviceSensorType type, InputDeviceSensorAccuracy accuracy, @@ -156,6 +163,20 @@ struct InputDeviceSensorInfo { int32_t id; }; +struct InputDeviceLightInfo { + explicit InputDeviceLightInfo(std::string name, int32_t id, InputDeviceLightType type, + int32_t ordinal) + : name(name), id(id), type(type), ordinal(ordinal) {} + // Name string of the light. + std::string name; + // Light id + int32_t id; + // Type of the light. + InputDeviceLightType type; + // Ordinal of the light + int32_t ordinal; +}; + /* * Describes the characteristics and capabilities of an input device. */ @@ -198,6 +219,7 @@ public: float min, float max, float flat, float fuzz, float resolution); void addMotionRange(const MotionRange& range); void addSensorInfo(const InputDeviceSensorInfo& info); + void addLightInfo(const InputDeviceLightInfo& info); inline void setKeyboardType(int32_t keyboardType) { mKeyboardType = keyboardType; } inline int32_t getKeyboardType() const { return mKeyboardType; } @@ -230,6 +252,10 @@ public: const std::vector getSensorTypes(); + const std::vector getLightIds(); + + const InputDeviceLightInfo* getLightInfo(int32_t id); + private: int32_t mId; int32_t mGeneration; @@ -248,6 +274,8 @@ private: std::vector mMotionRanges; std::unordered_map mSensors; + /* Map from light ID to light info */ + std::unordered_map mLights; }; /* Types of input device configuration files. */ diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp index 698cf6eebc..ffcc1cd93a 100644 --- a/libs/input/InputDevice.cpp +++ b/libs/input/InputDevice.cpp @@ -170,7 +170,8 @@ InputDeviceInfo::InputDeviceInfo(const InputDeviceInfo& other) mHasButtonUnderPad(other.mHasButtonUnderPad), mHasSensor(other.mHasSensor), mMotionRanges(other.mMotionRanges), - mSensors(other.mSensors) {} + mSensors(other.mSensors), + mLights(other.mLights) {} InputDeviceInfo::~InputDeviceInfo() { } @@ -193,6 +194,7 @@ void InputDeviceInfo::initialize(int32_t id, int32_t generation, int32_t control mHasSensor = false; mMotionRanges.clear(); mSensors.clear(); + mLights.clear(); } const InputDeviceInfo::MotionRange* InputDeviceInfo::getMotionRange( @@ -229,6 +231,13 @@ void InputDeviceInfo::addSensorInfo(const InputDeviceSensorInfo& info) { mSensors.insert_or_assign(info.type, info); } +void InputDeviceInfo::addLightInfo(const InputDeviceLightInfo& info) { + if (mLights.find(info.id) != mLights.end()) { + ALOGW("Light id %d already exists, will be replaced by new light added.", info.id); + } + mLights.insert_or_assign(info.id, info); +} + const std::vector InputDeviceInfo::getSensorTypes() { std::vector types; for (const auto& [type, info] : mSensors) { @@ -245,4 +254,20 @@ const InputDeviceSensorInfo* InputDeviceInfo::getSensorInfo(InputDeviceSensorTyp return &it->second; } +const std::vector InputDeviceInfo::getLightIds() { + std::vector ids; + for (const auto& [id, info] : mLights) { + ids.push_back(id); + } + return ids; +} + +const InputDeviceLightInfo* InputDeviceInfo::getLightInfo(int32_t id) { + auto it = mLights.find(id); + if (it == mLights.end()) { + return nullptr; + } + return &it->second; +} + } // namespace android diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h index 69aea84cd0..3bf212a51b 100644 --- a/services/inputflinger/include/InputReaderBase.h +++ b/services/inputflinger/include/InputReaderBase.h @@ -113,6 +113,10 @@ public: /* Get battery status of a particular input device. */ virtual std::optional getBatteryStatus(int32_t deviceId) = 0; + virtual std::vector getLightIds(int32_t deviceId) = 0; + + virtual const InputDeviceLightInfo* getLightInfo(int32_t deviceId, int32_t lightId) = 0; + /* Return true if the device can send input events to the specified display. */ virtual bool canDispatchToDisplay(int32_t deviceId, int32_t displayId) = 0; @@ -126,6 +130,15 @@ public: /* Flush sensor data in input reader mapper. */ virtual void flushSensor(int32_t deviceId, InputDeviceSensorType sensorType) = 0; + + /* Set color for the light */ + virtual bool setLightColor(int32_t deviceId, int32_t lightId, int32_t color) = 0; + /* Set player ID for the light */ + virtual bool setLightPlayerId(int32_t deviceId, int32_t lightId, int32_t playerId) = 0; + /* Get light color */ + virtual std::optional getLightColor(int32_t deviceId, int32_t lightId) = 0; + /* Get light player ID */ + virtual std::optional getLightPlayerId(int32_t deviceId, int32_t lightId) = 0; }; // --- InputReaderConfiguration --- diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp index 7f979f2bd3..ffb98e0ca8 100644 --- a/services/inputflinger/reader/Android.bp +++ b/services/inputflinger/reader/Android.bp @@ -36,6 +36,7 @@ filegroup { "mapper/InputMapper.cpp", "mapper/JoystickInputMapper.cpp", "mapper/KeyboardInputMapper.cpp", + "mapper/LightInputMapper.cpp", "mapper/MultiTouchInputMapper.cpp", "mapper/RotaryEncoderInputMapper.cpp", "mapper/SensorInputMapper.cpp", diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp index 8f8c0513b2..e939d1cb91 100644 --- a/services/inputflinger/reader/EventHub.cpp +++ b/services/inputflinger/reader/EventHub.cpp @@ -48,6 +48,7 @@ #include #include +#include #include "EventHub.h" @@ -84,6 +85,30 @@ static const std::unordered_map BATTERY_LEVEL = {{"Critica {"Full", 100}, {"Unknown", 50}}; +// Mapping for input led class node names lookup. +// https://www.kernel.org/doc/html/latest/leds/leds-class.html +static const std::unordered_map LIGHT_CLASSES = + {{"red", InputLightClass::RED}, + {"green", InputLightClass::GREEN}, + {"blue", InputLightClass::BLUE}, + {"global", InputLightClass::GLOBAL}, + {"brightness", InputLightClass::BRIGHTNESS}, + {"multi_index", InputLightClass::MULTI_INDEX}, + {"multi_intensity", InputLightClass::MULTI_INTENSITY}, + {"max_brightness", InputLightClass::MAX_BRIGHTNESS}}; + +// Mapping for input multicolor led class node names. +// https://www.kernel.org/doc/html/latest/leds/leds-class-multicolor.html +static const std::unordered_map LIGHT_NODES = + {{InputLightClass::BRIGHTNESS, "brightness"}, + {InputLightClass::MULTI_INDEX, "multi_index"}, + {InputLightClass::MULTI_INTENSITY, "multi_intensity"}}; + +// Mapping for light color name and the light color +const std::unordered_map LIGHT_COLORS = {{"red", LightColor::RED}, + {"green", LightColor::GREEN}, + {"blue", LightColor::BLUE}}; + static inline const char* toString(bool value) { return value ? "true" : "false"; } @@ -151,14 +176,14 @@ static nsecs_t processEventTimestamp(const struct input_event& event) { * Returns the sysfs root path of the input device * */ -static std::filesystem::path getSysfsRootPath(const char* devicePath) { +static std::optional getSysfsRootPath(const char* devicePath) { std::error_code errorCode; // Stat the device path to get the major and minor number of the character file struct stat statbuf; if (stat(devicePath, &statbuf) == -1) { ALOGE("Could not stat device %s due to error: %s.", devicePath, std::strerror(errno)); - return std::filesystem::path(); + return std::nullopt; } unsigned int major_num = major(statbuf.st_rdev); @@ -173,7 +198,7 @@ static std::filesystem::path getSysfsRootPath(const char* devicePath) { if (errorCode) { ALOGW("Could not run filesystem::canonical() due to error %d : %s.", errorCode.value(), errorCode.message().c_str()); - return std::filesystem::path(); + return std::nullopt; } // Continue to go up a directory until we reach a directory named "input" @@ -192,26 +217,68 @@ static std::filesystem::path getSysfsRootPath(const char* devicePath) { } // Not found - return std::filesystem::path(); + return std::nullopt; } return sysfsPath; } /** - * Returns the power supply node in sys fs - * + * Returns the list of files under a specified path. */ -static std::filesystem::path findPowerSupplyNode(const std::filesystem::path& sysfsRootPath) { - for (auto path = sysfsRootPath; path != "/"; path = path.parent_path()) { - std::error_code errorCode; - auto iter = std::filesystem::directory_iterator(path / "power_supply", errorCode); - if (!errorCode && iter != std::filesystem::directory_iterator()) { - return iter->path(); +static std::vector allFilesInPath(const std::filesystem::path& path) { + std::vector nodes; + std::error_code errorCode; + auto iter = std::filesystem::directory_iterator(path, errorCode); + while (!errorCode && iter != std::filesystem::directory_iterator()) { + nodes.push_back(iter->path()); + iter++; + } + return nodes; +} + +/** + * Returns the list of files under a specified directory in a sysfs path. + * Example: + * findSysfsNodes(sysfsRootPath, SysfsClass::LEDS) will return all led nodes under "leds" directory + * in the sysfs path. + */ +static std::vector findSysfsNodes(const std::filesystem::path& sysfsRoot, + SysfsClass clazz) { + std::string nodeStr = NamedEnum::string(clazz); + std::for_each(nodeStr.begin(), nodeStr.end(), + [](char& c) { c = std::tolower(static_cast(c)); }); + std::vector nodes; + for (auto path = sysfsRoot; path != "/" && nodes.empty(); path = path.parent_path()) { + nodes = allFilesInPath(path / nodeStr); + } + return nodes; +} + +static std::optional> getColorIndexArray( + std::filesystem::path path) { + std::string indexStr; + if (!base::ReadFileToString(path, &indexStr)) { + return std::nullopt; + } + + // Parse the multi color LED index file, refer to kernel docs + // leds/leds-class-multicolor.html + std::regex indexPattern("(red|green|blue)\\s(red|green|blue)\\s(red|green|blue)[\\n]"); + std::smatch results; + std::array colors; + if (!std::regex_match(indexStr, results, indexPattern)) { + return std::nullopt; + } + + for (size_t i = 1; i < results.size(); i++) { + const auto it = LIGHT_COLORS.find(results[i].str()); + if (it != LIGHT_COLORS.end()) { + // intensities.emplace(it->second, 0); + colors[i - 1] = it->second; } } - // Not found - return std::filesystem::path(); + return colors; } // --- Global Functions --- @@ -280,6 +347,7 @@ EventHub::Device::Device(int fd, int32_t id, const std::string& path, virtualKeyMap(nullptr), ffEffectPlaying(false), ffEffectId(-1), + nextLightId(0), controllerNumber(0), enabled(true), isVirtual(fd < 0) {} @@ -469,6 +537,70 @@ status_t EventHub::Device::mapLed(int32_t led, int32_t* outScanCode) const { return NAME_NOT_FOUND; } +// Check the sysfs path for any input device batteries, returns true if battery found. +bool EventHub::Device::configureBatteryLocked() { + if (!sysfsRootPath.has_value()) { + return false; + } + // Check if device has any batteries. + std::vector batteryPaths = + findSysfsNodes(sysfsRootPath.value(), SysfsClass::POWER_SUPPLY); + // We only support single battery for an input device, if multiple batteries exist only the + // first one is supported. + if (batteryPaths.empty()) { + // Set path to be empty + sysfsBatteryPath = std::nullopt; + return false; + } + // If a battery exists + sysfsBatteryPath = batteryPaths[0]; + return true; +} + +// Check the sysfs path for any input device lights, returns true if lights found. +bool EventHub::Device::configureLightsLocked() { + if (!sysfsRootPath.has_value()) { + return false; + } + // Check if device has any lights. + const auto& paths = findSysfsNodes(sysfsRootPath.value(), SysfsClass::LEDS); + for (const auto& nodePath : paths) { + RawLightInfo info; + info.id = ++nextLightId; + info.path = nodePath; + info.name = nodePath.filename(); + info.maxBrightness = std::nullopt; + size_t nameStart = info.name.rfind(":"); + if (nameStart != std::string::npos) { + // Trim the name to color name + info.name = info.name.substr(nameStart + 1); + // Set InputLightClass flag for colors + const auto it = LIGHT_CLASSES.find(info.name); + if (it != LIGHT_CLASSES.end()) { + info.flags |= it->second; + } + } + // Scan the path for all the files + // Refer to https://www.kernel.org/doc/Documentation/leds/leds-class.txt + const auto& files = allFilesInPath(nodePath); + for (const auto& file : files) { + const auto it = LIGHT_CLASSES.find(file.filename().string()); + if (it != LIGHT_CLASSES.end()) { + info.flags |= it->second; + // If the node has maximum brightness, read it + if (it->second == InputLightClass::MAX_BRIGHTNESS) { + std::string str; + if (base::ReadFileToString(file, &str)) { + info.maxBrightness = std::stoi(str); + } + } + } + } + lightInfos.insert_or_assign(info.id, info); + } + return !lightInfos.empty(); +} + /** * Get the capabilities for the current process. * Crashes the system if unable to create / check / destroy the capabilities object. @@ -829,6 +961,161 @@ base::Result> EventHub::mapSensor(int3 return Errorf("Device not found or device has no key layout."); } +const std::vector EventHub::getRawLightIds(int32_t deviceId) { + std::scoped_lock _l(mLock); + Device* device = getDeviceLocked(deviceId); + std::vector lightIds; + + if (device != nullptr) { + for (const auto [id, info] : device->lightInfos) { + lightIds.push_back(id); + } + } + return lightIds; +} + +std::optional EventHub::getRawLightInfo(int32_t deviceId, int32_t lightId) { + std::scoped_lock _l(mLock); + Device* device = getDeviceLocked(deviceId); + + if (device != nullptr) { + auto it = device->lightInfos.find(lightId); + if (it != device->lightInfos.end()) { + return it->second; + } + } + return std::nullopt; +} + +std::optional EventHub::getLightBrightness(int32_t deviceId, int32_t lightId) { + std::scoped_lock _l(mLock); + + Device* device = getDeviceLocked(deviceId); + if (device == nullptr) { + return std::nullopt; + } + + auto it = device->lightInfos.find(lightId); + if (it == device->lightInfos.end()) { + return std::nullopt; + } + std::string buffer; + if (!base::ReadFileToString(it->second.path / LIGHT_NODES.at(InputLightClass::BRIGHTNESS), + &buffer)) { + return std::nullopt; + } + return std::stoi(buffer); +} + +std::optional> EventHub::getLightIntensities( + int32_t deviceId, int32_t lightId) { + std::scoped_lock _l(mLock); + + Device* device = getDeviceLocked(deviceId); + if (device == nullptr) { + return std::nullopt; + } + + auto lightIt = device->lightInfos.find(lightId); + if (lightIt == device->lightInfos.end()) { + return std::nullopt; + } + + auto ret = + getColorIndexArray(lightIt->second.path / LIGHT_NODES.at(InputLightClass::MULTI_INDEX)); + + if (!ret.has_value()) { + return std::nullopt; + } + std::array colors = ret.value(); + + std::string intensityStr; + if (!base::ReadFileToString(lightIt->second.path / + LIGHT_NODES.at(InputLightClass::MULTI_INTENSITY), + &intensityStr)) { + return std::nullopt; + } + + // Intensity node outputs 3 color values + std::regex intensityPattern("([0-9]+)\\s([0-9]+)\\s([0-9]+)[\\n]"); + std::smatch results; + + if (!std::regex_match(intensityStr, results, intensityPattern)) { + return std::nullopt; + } + std::unordered_map intensities; + for (size_t i = 1; i < results.size(); i++) { + int value = std::stoi(results[i].str()); + intensities.emplace(colors[i - 1], value); + } + return intensities; +} + +void EventHub::setLightBrightness(int32_t deviceId, int32_t lightId, int32_t brightness) { + std::scoped_lock _l(mLock); + + Device* device = getDeviceLocked(deviceId); + if (device == nullptr) { + ALOGE("Device Id %d does not exist", deviceId); + return; + } + auto lightIt = device->lightInfos.find(lightId); + if (lightIt == device->lightInfos.end()) { + ALOGE("Light Id %d does not exist.", lightId); + return; + } + + if (!base::WriteStringToFile(std::to_string(brightness), + lightIt->second.path / + LIGHT_NODES.at(InputLightClass::BRIGHTNESS))) { + ALOGE("Can not write to file, error: %s", strerror(errno)); + } +} + +void EventHub::setLightIntensities(int32_t deviceId, int32_t lightId, + std::unordered_map intensities) { + std::scoped_lock _l(mLock); + + Device* device = getDeviceLocked(deviceId); + if (device == nullptr) { + ALOGE("Device Id %d does not exist", deviceId); + return; + } + auto lightIt = device->lightInfos.find(lightId); + if (lightIt == device->lightInfos.end()) { + ALOGE("Light Id %d does not exist.", lightId); + return; + } + + auto ret = + getColorIndexArray(lightIt->second.path / LIGHT_NODES.at(InputLightClass::MULTI_INDEX)); + + if (!ret.has_value()) { + return; + } + std::array colors = ret.value(); + + std::string rgbStr; + for (size_t i = 0; i < COLOR_NUM; i++) { + auto it = intensities.find(colors[i]); + if (it != intensities.end()) { + rgbStr += std::to_string(it->second); + // Insert space between colors + if (i < COLOR_NUM - 1) { + rgbStr += " "; + } + } + } + // Append new line + rgbStr += "\n"; + + if (!base::WriteStringToFile(rgbStr, + lightIt->second.path / + LIGHT_NODES.at(InputLightClass::MULTI_INTENSITY))) { + ALOGE("Can not write to file, error: %s", strerror(errno)); + } +} + void EventHub::setExcludedDevices(const std::vector& devices) { std::scoped_lock _l(mLock); @@ -1068,18 +1355,18 @@ std::optional EventHub::getBatteryCapacity(int32_t deviceId) const { Device* device = getDeviceLocked(deviceId); std::string buffer; - if (!device || (device->sysfsBatteryPath.empty())) { + if (device == nullptr || !device->sysfsBatteryPath.has_value()) { return std::nullopt; } // Some devices report battery capacity as an integer through the "capacity" file - if (base::ReadFileToString(device->sysfsBatteryPath / "capacity", &buffer)) { + if (base::ReadFileToString(device->sysfsBatteryPath.value() / "capacity", &buffer)) { return std::stoi(buffer); } // Other devices report capacity as an enum value POWER_SUPPLY_CAPACITY_LEVEL_XXX // These values are taken from kernel source code include/linux/power_supply.h - if (base::ReadFileToString(device->sysfsBatteryPath / "capacity_level", &buffer)) { + if (base::ReadFileToString(device->sysfsBatteryPath.value() / "capacity_level", &buffer)) { const auto it = BATTERY_LEVEL.find(buffer); if (it != BATTERY_LEVEL.end()) { return it->second; @@ -1093,11 +1380,11 @@ std::optional EventHub::getBatteryStatus(int32_t deviceId) const { Device* device = getDeviceLocked(deviceId); std::string buffer; - if (!device || (device->sysfsBatteryPath.empty())) { + if (device == nullptr || !device->sysfsBatteryPath.has_value()) { return std::nullopt; } - if (!base::ReadFileToString(device->sysfsBatteryPath / "status", &buffer)) { + if (!base::ReadFileToString(device->sysfsBatteryPath.value() / "status", &buffer)) { ALOGE("Failed to read sysfs battery info: %s", strerror(errno)); return std::nullopt; } @@ -1572,6 +1859,12 @@ status_t EventHub::openDeviceLocked(const std::string& devicePath) { // Load the configuration file for the device. device->loadConfigurationLocked(); + // Grab the device's sysfs path + device->sysfsRootPath = getSysfsRootPath(devicePath.c_str()); + // find related components + bool hasBattery = device->configureBatteryLocked(); + bool hasLights = device->configureLightsLocked(); + // Figure out the kinds of events the device reports. device->readDeviceBitMask(EVIOCGBIT(EV_KEY, 0), device->keyBitmask); device->readDeviceBitMask(EVIOCGBIT(EV_ABS, 0), device->absBitmask); @@ -1721,16 +2014,14 @@ status_t EventHub::openDeviceLocked(const std::string& devicePath) { return -1; } - // Grab the device's sysfs path - device->sysfsRootPath = getSysfsRootPath(devicePath.c_str()); - - if (!device->sysfsRootPath.empty()) { - device->sysfsBatteryPath = findPowerSupplyNode(device->sysfsRootPath); + // Classify InputDeviceClass::BATTERY. + if (hasBattery) { + device->classes |= InputDeviceClass::BATTERY; + } - // Check if a battery exists - if (!device->sysfsBatteryPath.empty()) { - device->classes |= InputDeviceClass::BATTERY; - } + // Classify InputDeviceClass::LIGHT. + if (hasLights) { + device->classes |= InputDeviceClass::LIGHT; } // Determine whether the device has a mic. diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp index 574f65122a..cbf3b69f29 100644 --- a/services/inputflinger/reader/InputDevice.cpp +++ b/services/inputflinger/reader/InputDevice.cpp @@ -27,6 +27,7 @@ #include "InputReaderContext.h" #include "JoystickInputMapper.h" #include "KeyboardInputMapper.h" +#include "LightInputMapper.h" #include "MultiTouchInputMapper.h" #include "RotaryEncoderInputMapper.h" #include "SensorInputMapper.h" @@ -161,9 +162,22 @@ void InputDevice::addEventHubDevice(int32_t eventHubId, bool populateMappers) { mappers.push_back(std::make_unique(*contextPtr)); } - // Battery-like devices. + // Battery-like devices. Only one battery mapper for each EventHub device. if (classes.test(InputDeviceClass::BATTERY)) { - mappers.push_back(std::make_unique(*contextPtr)); + InputDeviceInfo deviceInfo; + getDeviceInfo(&deviceInfo); + if (!deviceInfo.hasBattery()) { + mappers.push_back(std::make_unique(*contextPtr)); + } + } + + // Light-containing devices. Only one light mapper for each EventHub device. + if (classes.test(InputDeviceClass::LIGHT)) { + InputDeviceInfo deviceInfo; + getDeviceInfo(&deviceInfo); + if (deviceInfo.getLightIds().empty()) { + mappers.push_back(std::make_unique(*contextPtr)); + } } // Keyboard-like devices. @@ -505,6 +519,32 @@ std::optional InputDevice::getBatteryStatus() { return first_in_mappers([](InputMapper& mapper) { return mapper.getBatteryStatus(); }); } +bool InputDevice::setLightColor(int32_t lightId, int32_t color) { + bool success = true; + for_each_mapper([&success, lightId, color](InputMapper& mapper) { + success &= mapper.setLightColor(lightId, color); + }); + return success; +} + +bool InputDevice::setLightPlayerId(int32_t lightId, int32_t playerId) { + bool success = true; + for_each_mapper([&success, lightId, playerId](InputMapper& mapper) { + success &= mapper.setLightPlayerId(lightId, playerId); + }); + return success; +} + +std::optional InputDevice::getLightColor(int32_t lightId) { + return first_in_mappers( + [lightId](InputMapper& mapper) { return mapper.getLightColor(lightId); }); +} + +std::optional InputDevice::getLightPlayerId(int32_t lightId) { + return first_in_mappers( + [lightId](InputMapper& mapper) { return mapper.getLightPlayerId(lightId); }); +} + int32_t InputDevice::getMetaState() { int32_t result = 0; for_each_mapper([&result](InputMapper& mapper) { result |= mapper.getMetaState(); }); diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp index 14fb77bfad..2d0fdf7958 100644 --- a/services/inputflinger/reader/InputReader.cpp +++ b/services/inputflinger/reader/InputReader.cpp @@ -696,6 +696,70 @@ std::optional InputReader::getBatteryStatus(int32_t deviceId) { return std::nullopt; } +std::vector InputReader::getLightIds(int32_t deviceId) { + std::scoped_lock _l(mLock); + + InputDevice* device = findInputDeviceLocked(deviceId); + if (device) { + InputDeviceInfo info; + device->getDeviceInfo(&info); + return info.getLightIds(); + } + return {}; +} + +const InputDeviceLightInfo* InputReader::getLightInfo(int32_t deviceId, int32_t lightId) { + std::scoped_lock _l(mLock); + + InputDevice* device = findInputDeviceLocked(deviceId); + if (device) { + InputDeviceInfo info; + device->getDeviceInfo(&info); + return info.getLightInfo(lightId); + } + return nullptr; +} + +bool InputReader::setLightColor(int32_t deviceId, int32_t lightId, int32_t color) { + std::scoped_lock _l(mLock); + + InputDevice* device = findInputDeviceLocked(deviceId); + if (device) { + return device->setLightColor(lightId, color); + } + return false; +} + +bool InputReader::setLightPlayerId(int32_t deviceId, int32_t lightId, int32_t playerId) { + std::scoped_lock _l(mLock); + + InputDevice* device = findInputDeviceLocked(deviceId); + if (device) { + return device->setLightPlayerId(lightId, playerId); + } + return false; +} + +std::optional InputReader::getLightColor(int32_t deviceId, int32_t lightId) { + std::scoped_lock _l(mLock); + + InputDevice* device = findInputDeviceLocked(deviceId); + if (device) { + return device->getLightColor(lightId); + } + return std::nullopt; +} + +std::optional InputReader::getLightPlayerId(int32_t deviceId, int32_t lightId) { + std::scoped_lock _l(mLock); + + InputDevice* device = findInputDeviceLocked(deviceId); + if (device) { + return device->getLightPlayerId(lightId); + } + return std::nullopt; +} + bool InputReader::isInputDeviceEnabled(int32_t deviceId) { std::scoped_lock _l(mLock); diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h index 30967dfdf2..e6164d38d4 100644 --- a/services/inputflinger/reader/include/EventHub.h +++ b/services/inputflinger/reader/include/EventHub.h @@ -47,6 +47,8 @@ namespace android { +/* Number of colors : {red, green, blue} */ +static constexpr size_t COLOR_NUM = 3; /* * A raw event as retrieved from the EventHub. */ @@ -127,6 +129,9 @@ enum class InputDeviceClass : uint32_t { /* The input device has a battery */ BATTERY = 0x00004000, + /* The input device has sysfs controllable lights */ + LIGHT = 0x00008000, + /* The input device is virtual (not a real device, not part of UI configuration). */ VIRTUAL = 0x40000000, @@ -134,6 +139,46 @@ enum class InputDeviceClass : uint32_t { EXTERNAL = 0x80000000, }; +enum class SysfsClass : uint32_t { + POWER_SUPPLY = 0, + LEDS = 1, +}; + +enum class LightColor : uint32_t { + RED = 0, + GREEN = 1, + BLUE = 2, +}; + +enum class InputLightClass : uint32_t { + /* The input light has brightness node. */ + BRIGHTNESS = 0x00000001, + /* The input light has red name. */ + RED = 0x00000002, + /* The input light has green name. */ + GREEN = 0x00000004, + /* The input light has blue name. */ + BLUE = 0x00000008, + /* The input light has global name. */ + GLOBAL = 0x00000010, + /* The input light has multi index node. */ + MULTI_INDEX = 0x00000020, + /* The input light has multi intensity node. */ + MULTI_INTENSITY = 0x00000040, + /* The input light has max brightness node. */ + MAX_BRIGHTNESS = 0x00000080, +}; + +/* Describes a raw light. */ +struct RawLightInfo { + int32_t id; + std::string name; + std::optional maxBrightness; + Flags flags; + std::array rgbIndex; + std::filesystem::path path; +}; + /* * Gets the class that owns an axis, in cases where multiple classes might claim * the same axis for different purposes. @@ -214,7 +259,16 @@ public: virtual std::vector getVideoFrames(int32_t deviceId) = 0; virtual base::Result> mapSensor(int32_t deviceId, int32_t absCode) = 0; - + // Raw lights are sysfs led light nodes we found from the EventHub device sysfs node, + // containing the raw info of the sysfs node structure. + virtual const std::vector getRawLightIds(int32_t deviceId) = 0; + virtual std::optional getRawLightInfo(int32_t deviceId, int32_t lightId) = 0; + virtual std::optional getLightBrightness(int32_t deviceId, int32_t lightId) = 0; + virtual void setLightBrightness(int32_t deviceId, int32_t lightId, int32_t brightness) = 0; + virtual std::optional> getLightIntensities( + int32_t deviceId, int32_t lightId) = 0; + virtual void setLightIntensities(int32_t deviceId, int32_t lightId, + std::unordered_map intensities) = 0; /* * Query current input state. */ @@ -377,6 +431,17 @@ public: base::Result> mapSensor( int32_t deviceId, int32_t absCode) override final; + const std::vector getRawLightIds(int32_t deviceId) override final; + + std::optional getRawLightInfo(int32_t deviceId, int32_t lightId) override final; + + std::optional getLightBrightness(int32_t deviceId, int32_t lightId) override final; + void setLightBrightness(int32_t deviceId, int32_t lightId, int32_t brightness) override final; + std::optional> getLightIntensities( + int32_t deviceId, int32_t lightId) override final; + void setLightIntensities(int32_t deviceId, int32_t lightId, + std::unordered_map intensities) override final; + void setExcludedDevices(const std::vector& devices) override final; int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const override final; @@ -458,9 +523,12 @@ private: bool ffEffectPlaying; int16_t ffEffectId; // initially -1 - // The paths are invalid when .empty() returns true - std::filesystem::path sysfsRootPath; - std::filesystem::path sysfsBatteryPath; + // The paths are invalid when they're std::nullopt + std::optional sysfsRootPath; + std::optional sysfsBatteryPath; + // maps from light id to light info + std::unordered_map lightInfos; + int32_t nextLightId; int32_t controllerNumber; @@ -491,6 +559,8 @@ private: void setLedForControllerLocked(); status_t mapLed(int32_t led, int32_t* outScanCode) const; void setLedStateLocked(int32_t led, bool on); + bool configureBatteryLocked(); + bool configureLightsLocked(); }; status_t openDeviceLocked(const std::string& devicePath); diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h index e4186c86cd..34c330b89c 100644 --- a/services/inputflinger/reader/include/InputDevice.h +++ b/services/inputflinger/reader/include/InputDevice.h @@ -95,6 +95,11 @@ public: std::optional getBatteryCapacity(); std::optional getBatteryStatus(); + bool setLightColor(int32_t lightId, int32_t color); + bool setLightPlayerId(int32_t lightId, int32_t playerId); + std::optional getLightColor(int32_t lightId); + std::optional getLightPlayerId(int32_t lightId); + int32_t getMetaState(); void updateMetaState(int32_t keyCode); @@ -254,6 +259,30 @@ public: return mEventHub->mapSensor(mId, absCode); } + inline const std::vector getRawLightIds() { return mEventHub->getRawLightIds(mId); } + + inline std::optional getRawLightInfo(int32_t lightId) { + return mEventHub->getRawLightInfo(mId, lightId); + } + + inline std::optional getLightBrightness(int32_t lightId) { + return mEventHub->getLightBrightness(mId, lightId); + } + + inline void setLightBrightness(int32_t lightId, int32_t brightness) { + return mEventHub->setLightBrightness(mId, lightId, brightness); + } + + inline std::optional> getLightIntensities( + int32_t lightId) { + return mEventHub->getLightIntensities(mId, lightId); + } + + inline void setLightIntensities(int32_t lightId, + std::unordered_map intensities) { + return mEventHub->setLightIntensities(mId, lightId, intensities); + } + inline std::vector getVideoFrames() { return mEventHub->getVideoFrames(mId); } inline int32_t getScanCodeState(int32_t scanCode) const { return mEventHub->getScanCodeState(mId, scanCode); diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h index 81e3e9ab24..1405671a50 100644 --- a/services/inputflinger/reader/include/InputReader.h +++ b/services/inputflinger/reader/include/InputReader.h @@ -99,6 +99,18 @@ public: std::optional getBatteryStatus(int32_t deviceId) override; + std::vector getLightIds(int32_t deviceId) override; + + const InputDeviceLightInfo* getLightInfo(int32_t deviceId, int32_t lightId) override; + + bool setLightColor(int32_t deviceId, int32_t lightId, int32_t color) override; + + bool setLightPlayerId(int32_t deviceId, int32_t lightId, int32_t playerId) override; + + std::optional getLightColor(int32_t deviceId, int32_t lightId) override; + + std::optional getLightPlayerId(int32_t deviceId, int32_t lightId) override; + protected: // These members are protected so they can be instrumented by test cases. virtual std::shared_ptr createDeviceLocked(int32_t deviceId, diff --git a/services/inputflinger/reader/mapper/BatteryInputMapper.cpp b/services/inputflinger/reader/mapper/BatteryInputMapper.cpp index afdc5abdc2..e4fb3a6c9f 100644 --- a/services/inputflinger/reader/mapper/BatteryInputMapper.cpp +++ b/services/inputflinger/reader/mapper/BatteryInputMapper.cpp @@ -24,7 +24,7 @@ BatteryInputMapper::BatteryInputMapper(InputDeviceContext& deviceContext) : InputMapper(deviceContext) {} uint32_t BatteryInputMapper::getSources() { - return 0; + return AINPUT_SOURCE_UNKNOWN; } void BatteryInputMapper::populateDeviceInfo(InputDeviceInfo* info) { diff --git a/services/inputflinger/reader/mapper/InputMapper.h b/services/inputflinger/reader/mapper/InputMapper.h index 1cc59795c8..bd543e5798 100644 --- a/services/inputflinger/reader/mapper/InputMapper.h +++ b/services/inputflinger/reader/mapper/InputMapper.h @@ -77,6 +77,11 @@ public: virtual std::optional getBatteryCapacity() { return std::nullopt; } virtual std::optional getBatteryStatus() { return std::nullopt; } + virtual bool setLightColor(int32_t lightId, int32_t color) { return true; } + virtual bool setLightPlayerId(int32_t lightId, int32_t playerId) { return true; } + virtual std::optional getLightColor(int32_t lightId) { return std::nullopt; } + virtual std::optional getLightPlayerId(int32_t lightId) { return std::nullopt; } + virtual int32_t getMetaState(); virtual void updateMetaState(int32_t keyCode); diff --git a/services/inputflinger/reader/mapper/LightInputMapper.cpp b/services/inputflinger/reader/mapper/LightInputMapper.cpp new file mode 100644 index 0000000000..be1f722e2e --- /dev/null +++ b/services/inputflinger/reader/mapper/LightInputMapper.cpp @@ -0,0 +1,478 @@ +/* + * Copyright (C) 2021 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 "../Macros.h" + +#include "LightInputMapper.h" +#include "input/NamedEnum.h" + +// Log detailed debug messages about input device lights. +static constexpr bool DEBUG_LIGHT_DETAILS = false; + +namespace android { + +static inline int32_t getAlpha(int32_t color) { + return (color >> 24) & 0xff; +} + +static inline int32_t getRed(int32_t color) { + return (color >> 16) & 0xff; +} + +static inline int32_t getGreen(int32_t color) { + return (color >> 8) & 0xff; +} + +static inline int32_t getBlue(int32_t color) { + return color & 0xff; +} + +static inline int32_t toArgb(int32_t brightness, int32_t red, int32_t green, int32_t blue) { + return (brightness & 0xff) << 24 | (red & 0xff) << 16 | (green & 0xff) << 8 | (blue & 0xff); +} + +/** + * Light input mapper owned by InputReader device, implements the native API for querying input + * lights, getting and setting the lights brightness and color, by interacting with EventHub + * devices. + * TODO b/180342233: Reconsider the inputflinger design to accommodate the device class + * like lights and battery. + */ +LightInputMapper::LightInputMapper(InputDeviceContext& deviceContext) + : InputMapper(deviceContext) {} + +LightInputMapper::~LightInputMapper() {} + +std::optional LightInputMapper::Light::getRawLightBrightness(int32_t rawLightId) { + std::optional rawInfo = context.getRawLightInfo(rawLightId); + std::optional ret = context.getLightBrightness(rawLightId); + if (!rawInfo.has_value() || !ret.has_value()) { + return std::nullopt; + } + int brightness = ret.value(); + + // If the light node doesn't have max brightness, use the default max brightness. + int rawMaxBrightness = rawInfo->maxBrightness.value_or(MAX_BRIGHTNESS); + float ratio = MAX_BRIGHTNESS / rawMaxBrightness; + // Scale the returned brightness in [0, rawMaxBrightness] to [0, 255] + if (rawMaxBrightness != MAX_BRIGHTNESS) { + brightness = brightness * ratio; + } + if (DEBUG_LIGHT_DETAILS) { + ALOGD("getRawLightBrightness rawLightId %d brightness 0x%x ratio %.2f", rawLightId, + brightness, ratio); + } + return brightness; +} + +void LightInputMapper::Light::setRawLightBrightness(int32_t rawLightId, int32_t brightness) { + std::optional rawInfo = context.getRawLightInfo(rawLightId); + if (!rawInfo.has_value()) { + return; + } + // If the light node doesn't have max brightness, use the default max brightness. + int rawMaxBrightness = rawInfo->maxBrightness.value_or(MAX_BRIGHTNESS); + float ratio = MAX_BRIGHTNESS / rawMaxBrightness; + // Scale the requested brightness in [0, 255] to [0, rawMaxBrightness] + if (rawMaxBrightness != MAX_BRIGHTNESS) { + brightness = ceil(brightness / ratio); + } + if (DEBUG_LIGHT_DETAILS) { + ALOGD("setRawLightBrightness rawLightId %d brightness 0x%x ratio %.2f", rawLightId, + brightness, ratio); + } + context.setLightBrightness(rawLightId, brightness); +} + +bool LightInputMapper::SingleLight::setLightColor(int32_t color) { + int32_t brightness = getAlpha(color); + setRawLightBrightness(rawId, brightness); + + return true; +} + +bool LightInputMapper::RgbLight::setLightColor(int32_t color) { + // Compose color value as per: + // https://developer.android.com/reference/android/graphics/Color?hl=en + // int color = (A & 0xff) << 24 | (R & 0xff) << 16 | (G & 0xff) << 8 | (B & 0xff); + // The alpha component is used to scale the R,G,B leds brightness, with the ratio to + // MAX_BRIGHTNESS. + brightness = getAlpha(color); + int32_t red = 0; + int32_t green = 0; + int32_t blue = 0; + if (brightness > 0) { + float ratio = MAX_BRIGHTNESS / brightness; + red = ceil(getRed(color) / ratio); + green = ceil(getGreen(color) / ratio); + blue = ceil(getBlue(color) / ratio); + } + setRawLightBrightness(rawRgbIds.at(LightColor::RED), red); + setRawLightBrightness(rawRgbIds.at(LightColor::GREEN), green); + setRawLightBrightness(rawRgbIds.at(LightColor::BLUE), blue); + if (rawGlobalId.has_value()) { + setRawLightBrightness(rawGlobalId.value(), brightness); + } + + return true; +} + +bool LightInputMapper::MultiColorLight::setLightColor(int32_t color) { + std::unordered_map intensities; + intensities.emplace(LightColor::RED, getRed(color)); + intensities.emplace(LightColor::GREEN, getGreen(color)); + intensities.emplace(LightColor::BLUE, getBlue(color)); + + context.setLightIntensities(rawId, intensities); + setRawLightBrightness(rawId, getAlpha(color)); + return true; +} + +std::optional LightInputMapper::SingleLight::getLightColor() { + std::optional brightness = getRawLightBrightness(rawId); + if (!brightness.has_value()) { + return std::nullopt; + } + + return toArgb(brightness.value(), 0 /* red */, 0 /* green */, 0 /* blue */); +} + +std::optional LightInputMapper::RgbLight::getLightColor() { + // If the Alpha component is zero, then return color 0. + if (brightness == 0) { + return 0; + } + // Compose color value as per: + // https://developer.android.com/reference/android/graphics/Color?hl=en + // int color = (A & 0xff) << 24 | (R & 0xff) << 16 | (G & 0xff) << 8 | (B & 0xff); + std::optional redOr = getRawLightBrightness(rawRgbIds.at(LightColor::RED)); + std::optional greenOr = getRawLightBrightness(rawRgbIds.at(LightColor::GREEN)); + std::optional blueOr = getRawLightBrightness(rawRgbIds.at(LightColor::BLUE)); + // If we can't get brightness for any of the RGB light + if (!redOr.has_value() || !greenOr.has_value() || !blueOr.has_value()) { + return std::nullopt; + } + + // Compose the ARGB format color. As the R,G,B color led brightness is scaled by Alpha + // value, scale it back to return the nominal color value. + float ratio = MAX_BRIGHTNESS / brightness; + int32_t red = round(redOr.value() * ratio); + int32_t green = round(greenOr.value() * ratio); + int32_t blue = round(blueOr.value() * ratio); + + if (red > MAX_BRIGHTNESS || green > MAX_BRIGHTNESS || blue > MAX_BRIGHTNESS) { + // Previously stored brightness isn't valid for current LED values, so just reset to max + // brightness since an app couldn't have provided these values in the first place. + red = redOr.value(); + green = greenOr.value(); + blue = blueOr.value(); + brightness = MAX_BRIGHTNESS; + } + + return toArgb(brightness, red, green, blue); +} + +std::optional LightInputMapper::MultiColorLight::getLightColor() { + auto ret = context.getLightIntensities(rawId); + if (!ret.has_value()) { + return std::nullopt; + } + std::unordered_map intensities = ret.value(); + // Get red, green, blue colors + int32_t color = toArgb(0 /* brightness */, intensities.at(LightColor::RED) /* red */, + intensities.at(LightColor::GREEN) /* green */, + intensities.at(LightColor::BLUE) /* blue */); + // Get brightness + std::optional brightness = getRawLightBrightness(rawId); + if (brightness.has_value()) { + return toArgb(brightness.value() /* A */, 0, 0, 0) | color; + } + return std::nullopt; +} + +bool LightInputMapper::PlayerIdLight::setLightPlayerId(int32_t playerId) { + if (rawLightIds.find(playerId) == rawLightIds.end()) { + return false; + } + for (const auto& [id, rawId] : rawLightIds) { + if (playerId == id) { + setRawLightBrightness(rawId, MAX_BRIGHTNESS); + } else { + setRawLightBrightness(rawId, 0); + } + } + return true; +} + +std::optional LightInputMapper::PlayerIdLight::getLightPlayerId() { + for (const auto& [id, rawId] : rawLightIds) { + std::optional brightness = getRawLightBrightness(rawId); + if (brightness.has_value() && brightness.value() > 0) { + return id; + } + } + return std::nullopt; +} + +void LightInputMapper::SingleLight::dump(std::string& dump) { + dump += StringPrintf(INDENT4 "Color: 0x%x\n", getLightColor().value_or(0)); +} + +void LightInputMapper::PlayerIdLight::dump(std::string& dump) { + dump += StringPrintf(INDENT4 "PlayerId: %d\n", getLightPlayerId().value_or(-1)); + dump += StringPrintf(INDENT4 "Raw Player ID LEDs:"); + for (const auto& [id, rawId] : rawLightIds) { + dump += StringPrintf("id %d -> %d ", id, rawId); + } + dump += "\n"; +} + +void LightInputMapper::RgbLight::dump(std::string& dump) { + dump += StringPrintf(INDENT4 "Color: 0x%x\n", getLightColor().value_or(0)); + dump += StringPrintf(INDENT4 "Raw RGB LEDs: [%d, %d, %d] ", rawRgbIds.at(LightColor::RED), + rawRgbIds.at(LightColor::GREEN), rawRgbIds.at(LightColor::BLUE)); + if (rawGlobalId.has_value()) { + dump += StringPrintf(INDENT4 "Raw Global LED: [%d] ", rawGlobalId.value()); + } + dump += "\n"; +} + +void LightInputMapper::MultiColorLight::dump(std::string& dump) { + dump += StringPrintf(INDENT4 "Color: 0x%x\n", getLightColor().value_or(0)); +} + +uint32_t LightInputMapper::getSources() { + return AINPUT_SOURCE_UNKNOWN; +} + +void LightInputMapper::populateDeviceInfo(InputDeviceInfo* info) { + InputMapper::populateDeviceInfo(info); + + for (const auto& [lightId, light] : mLights) { + // Input device light doesn't support ordinal, always pass 1. + InputDeviceLightInfo lightInfo(light->name, light->id, light->type, 1 /* ordinal */); + info->addLightInfo(lightInfo); + } +} + +void LightInputMapper::dump(std::string& dump) { + dump += INDENT2 "Light Input Mapper:\n"; + dump += INDENT3 "Lights:\n"; + for (const auto& [lightId, light] : mLights) { + dump += StringPrintf(INDENT4 "Id: %d", lightId); + dump += StringPrintf(INDENT4 "Name: %s", light->name.c_str()); + dump += StringPrintf(INDENT4 "Type: %s", NamedEnum::string(light->type).c_str()); + light->dump(dump); + } + // Dump raw lights + dump += INDENT3 "RawLights:\n"; + dump += INDENT4 "Id:\t Name:\t Flags:\t Max brightness:\t Brightness\n"; + const std::vector rawLightIds = getDeviceContext().getRawLightIds(); + // Map from raw light id to raw light info + std::unordered_map rawInfos; + for (const auto& rawId : rawLightIds) { + std::optional rawInfo = getDeviceContext().getRawLightInfo(rawId); + if (!rawInfo.has_value()) { + continue; + } + dump += StringPrintf(INDENT4 "%d", rawId); + dump += StringPrintf(INDENT4 "%s", rawInfo->name.c_str()); + dump += StringPrintf(INDENT4 "%s", rawInfo->flags.string().c_str()); + dump += StringPrintf(INDENT4 "%d", rawInfo->maxBrightness.value_or(MAX_BRIGHTNESS)); + dump += StringPrintf(INDENT4 "%d\n", + getDeviceContext().getLightBrightness(rawId).value_or(-1)); + } +} + +void LightInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config, + uint32_t changes) { + InputMapper::configure(when, config, changes); + + if (!changes) { // first time only + bool hasRedLed = false; + bool hasGreenLed = false; + bool hasBlueLed = false; + std::optional rawGlobalId = std::nullopt; + // Player ID light common name string + std::string playerIdName; + // Raw RGB color to raw light ID + std::unordered_map rawRgbIds; + // Map from player Id to raw light Id + std::unordered_map playerIdLightIds; + mLights.clear(); + + // Check raw lights + const std::vector rawLightIds = getDeviceContext().getRawLightIds(); + // Map from raw light id to raw light info + std::unordered_map rawInfos; + for (const auto& rawId : rawLightIds) { + std::optional rawInfo = getDeviceContext().getRawLightInfo(rawId); + if (!rawInfo.has_value()) { + continue; + } + rawInfos.insert_or_assign(rawId, rawInfo.value()); + // Check if this is a group LEDs for player ID + std::regex lightPattern("([a-z]+)([0-9]+)"); + std::smatch results; + if (std::regex_match(rawInfo->name, results, lightPattern)) { + std::string commonName = results[1].str(); + int32_t playerId = std::stoi(results[2]); + if (playerIdLightIds.empty()) { + playerIdName = commonName; + playerIdLightIds.insert_or_assign(playerId, rawId); + } else { + // Make sure the player ID leds have common string name + if (playerIdName.compare(commonName) == 0 && + playerIdLightIds.find(playerId) == playerIdLightIds.end()) { + playerIdLightIds.insert_or_assign(playerId, rawId); + } + } + } + // Check if this is an LED of RGB light + if (rawInfo->flags.test(InputLightClass::RED)) { + hasRedLed = true; + rawRgbIds.emplace(LightColor::RED, rawId); + } + if (rawInfo->flags.test(InputLightClass::GREEN)) { + hasGreenLed = true; + rawRgbIds.emplace(LightColor::GREEN, rawId); + } + if (rawInfo->flags.test(InputLightClass::BLUE)) { + hasBlueLed = true; + rawRgbIds.emplace(LightColor::BLUE, rawId); + } + if (rawInfo->flags.test(InputLightClass::GLOBAL)) { + rawGlobalId = rawId; + } + if (DEBUG_LIGHT_DETAILS) { + ALOGD("Light rawId %d name %s max %d flags %s \n", rawInfo->id, + rawInfo->name.c_str(), rawInfo->maxBrightness.value_or(MAX_BRIGHTNESS), + rawInfo->flags.string().c_str()); + } + } + + // Construct a player ID light + if (playerIdLightIds.size() > 1) { + std::unique_ptr light = + std::make_unique(getDeviceContext(), playerIdName, ++mNextId, + playerIdLightIds); + mLights.insert_or_assign(light->id, std::move(light)); + // Remove these raw lights from raw light info as they've been used to compose a + // Player ID light, so we do not expose these raw lights as single lights. + for (const auto& [playerId, rawId] : playerIdLightIds) { + rawInfos.erase(rawId); + } + } + // Construct a RGB light for composed RGB light + if (hasRedLed && hasGreenLed && hasBlueLed) { + if (DEBUG_LIGHT_DETAILS) { + ALOGD("Rgb light ids [%d, %d, %d] \n", rawRgbIds.at(LightColor::RED), + rawRgbIds.at(LightColor::GREEN), rawRgbIds.at(LightColor::BLUE)); + } + std::unique_ptr light = std::make_unique(getDeviceContext(), ++mNextId, + rawRgbIds, rawGlobalId); + mLights.insert_or_assign(light->id, std::move(light)); + // Remove from raw light info as they've been composed a RBG light. + rawInfos.erase(rawRgbIds.at(LightColor::RED)); + rawInfos.erase(rawRgbIds.at(LightColor::GREEN)); + rawInfos.erase(rawRgbIds.at(LightColor::BLUE)); + if (rawGlobalId.has_value()) { + rawInfos.erase(rawGlobalId.value()); + } + } + + // Check the rest of raw light infos + for (const auto& [rawId, rawInfo] : rawInfos) { + // If the node is multi-color led, construct a MULTI_COLOR light + if (rawInfo.flags.test(InputLightClass::MULTI_INDEX) && + rawInfo.flags.test(InputLightClass::MULTI_INTENSITY)) { + if (DEBUG_LIGHT_DETAILS) { + ALOGD("Multicolor light Id %d name %s \n", rawInfo.id, rawInfo.name.c_str()); + } + std::unique_ptr light = + std::make_unique(getDeviceContext(), rawInfo.name, + ++mNextId, rawInfo.id); + mLights.insert_or_assign(light->id, std::move(light)); + continue; + } + // Construct a single LED light + if (DEBUG_LIGHT_DETAILS) { + ALOGD("Single light Id %d name %s \n", rawInfo.id, rawInfo.name.c_str()); + } + std::unique_ptr light = + std::make_unique(getDeviceContext(), rawInfo.name, ++mNextId, + rawInfo.id); + + mLights.insert_or_assign(light->id, std::move(light)); + } + } +} + +void LightInputMapper::reset(nsecs_t when) { + InputMapper::reset(when); +} + +void LightInputMapper::process(const RawEvent* rawEvent) {} + +bool LightInputMapper::setLightColor(int32_t lightId, int32_t color) { + auto it = mLights.find(lightId); + if (it == mLights.end()) { + return false; + } + auto& light = it->second; + if (DEBUG_LIGHT_DETAILS) { + ALOGD("setLightColor lightId %d type %s color 0x%x", lightId, + NamedEnum::string(light->type).c_str(), color); + } + return light->setLightColor(color); +} + +std::optional LightInputMapper::getLightColor(int32_t lightId) { + auto it = mLights.find(lightId); + if (it == mLights.end()) { + return std::nullopt; + } + auto& light = it->second; + std::optional color = light->getLightColor(); + if (DEBUG_LIGHT_DETAILS) { + ALOGD("getLightColor lightId %d type %s color 0x%x", lightId, + NamedEnum::string(light->type).c_str(), color.value_or(0)); + } + return color; +} + +bool LightInputMapper::setLightPlayerId(int32_t lightId, int32_t playerId) { + auto it = mLights.find(lightId); + if (it == mLights.end()) { + return false; + } + auto& light = it->second; + return light->setLightPlayerId(playerId); +} + +std::optional LightInputMapper::getLightPlayerId(int32_t lightId) { + auto it = mLights.find(lightId); + if (it == mLights.end()) { + return std::nullopt; + } + auto& light = it->second; + return light->getLightPlayerId(); +} + +} // namespace android diff --git a/services/inputflinger/reader/mapper/LightInputMapper.h b/services/inputflinger/reader/mapper/LightInputMapper.h new file mode 100644 index 0000000000..9254720385 --- /dev/null +++ b/services/inputflinger/reader/mapper/LightInputMapper.h @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2021 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_INPUTREADER_LIGHT_INPUT_MAPPER_H +#define _UI_INPUTREADER_LIGHT_INPUT_MAPPER_H + +#include "InputMapper.h" + +namespace android { + +class LightInputMapper : public InputMapper { + // Refer to https://developer.android.com/reference/kotlin/android/graphics/Color + /* Number of colors : {red, green, blue} */ + static constexpr size_t COLOR_NUM = 3; + static constexpr int32_t MAX_BRIGHTNESS = 0xff; + +public: + explicit LightInputMapper(InputDeviceContext& deviceContext); + ~LightInputMapper() override; + + uint32_t getSources() override; + void populateDeviceInfo(InputDeviceInfo* deviceInfo) override; + void dump(std::string& dump) override; + void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes) override; + void reset(nsecs_t when) override; + void process(const RawEvent* rawEvent) override; + bool setLightColor(int32_t lightId, int32_t color) override; + bool setLightPlayerId(int32_t lightId, int32_t playerId) override; + std::optional getLightColor(int32_t lightId) override; + std::optional getLightPlayerId(int32_t lightId) override; + +private: + struct Light { + explicit Light(InputDeviceContext& context, std::string name, int32_t id, + InputDeviceLightType type) + : context(context), name(name), id(id), type(type) {} + virtual ~Light() {} + InputDeviceContext& context; + std::string name; + int32_t id; + InputDeviceLightType type; + + virtual bool setLightColor(int32_t color) { return false; } + virtual std::optional getLightColor() { return std::nullopt; } + virtual bool setLightPlayerId(int32_t playerId) { return false; } + virtual std::optional getLightPlayerId() { return std::nullopt; } + + virtual void dump(std::string& dump) {} + + std::optional getRawLightBrightness(int32_t rawLightId); + void setRawLightBrightness(int32_t rawLightId, int32_t brightness); + }; + + struct SingleLight : public Light { + explicit SingleLight(InputDeviceContext& context, std::string name, int32_t id, + int32_t rawId) + : Light(context, name, id, InputDeviceLightType::SINGLE), rawId(rawId) {} + int32_t rawId; + + bool setLightColor(int32_t color) override; + std::optional getLightColor() override; + void dump(std::string& dump) override; + }; + + struct RgbLight : public Light { + explicit RgbLight(InputDeviceContext& context, int32_t id, + std::unordered_map rawRgbIds, + std::optional rawGlobalId) + : Light(context, "RGB", id, InputDeviceLightType::RGB), + rawRgbIds(rawRgbIds), + rawGlobalId(rawGlobalId) { + brightness = rawGlobalId.has_value() + ? getRawLightBrightness(rawGlobalId.value()).value_or(MAX_BRIGHTNESS) + : MAX_BRIGHTNESS; + } + // Map from color to raw light id. + std::unordered_map rawRgbIds; + // Optional global control raw light id. + std::optional rawGlobalId; + int32_t brightness; + + bool setLightColor(int32_t color) override; + std::optional getLightColor() override; + void dump(std::string& dump) override; + }; + + struct MultiColorLight : public Light { + explicit MultiColorLight(InputDeviceContext& context, std::string name, int32_t id, + int32_t rawId) + : Light(context, name, id, InputDeviceLightType::MULTI_COLOR), rawId(rawId) {} + int32_t rawId; + + bool setLightColor(int32_t color) override; + std::optional getLightColor() override; + void dump(std::string& dump) override; + }; + + struct PlayerIdLight : public Light { + explicit PlayerIdLight(InputDeviceContext& context, std::string name, int32_t id, + std::unordered_map rawLightIds) + : Light(context, name, id, InputDeviceLightType::PLAYER_ID), + rawLightIds(rawLightIds) {} + // Map from player Id to raw light Id + std::unordered_map rawLightIds; + + bool setLightPlayerId(int32_t palyerId) override; + std::optional getLightPlayerId() override; + void dump(std::string& dump) override; + }; + + int32_t mNextId = 0; + + // Light color map from light color to the color index. + static const std::unordered_map LIGHT_COLORS; + + // Light map from light ID to Light + std::unordered_map> mLights; +}; + +} // namespace android + +#endif // _UI_INPUTREADER_LIGHT_INPUT_MAPPER_H \ No newline at end of file diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index 409c62a0e2..2836516a41 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -36,6 +37,7 @@ #include #include +#include #include "input/DisplayViewport.h" #include "input/Input.h" @@ -70,6 +72,9 @@ static constexpr int32_t SECOND_TRACKING_ID = 1; static constexpr int32_t THIRD_TRACKING_ID = 2; static constexpr int32_t BATTERY_STATUS = 4; static constexpr int32_t BATTERY_CAPACITY = 66; +static constexpr int32_t LIGHT_BRIGHTNESS = 0x55000000; +static constexpr int32_t LIGHT_COLOR = 0x7F448866; +static constexpr int32_t LIGHT_PLAYER_ID = 2; // Error tolerance for floating point assertions. static const float EPSILON = 0.001f; @@ -83,6 +88,10 @@ static inline float avg(float x, float y) { return (x + y) / 2; } +// Mapping for light color name and the light color +const std::unordered_map LIGHT_COLORS = {{"red", LightColor::RED}, + {"green", LightColor::GREEN}, + {"blue", LightColor::BLUE}}; // --- FakePointerController --- @@ -412,6 +421,12 @@ class FakeEventHub : public EventHubInterface { std::vector mEvents GUARDED_BY(mLock); std::unordered_map> mVideoFrames; std::vector mVibrators = {0, 1}; + std::unordered_map mRawLightInfos; + // Simulates a device light brightness, from light id to light brightness. + std::unordered_map mLightBrightness; + // Simulates a device light intensities, from light id to light intensities map. + std::unordered_map> + mLightIntensities; public: virtual ~FakeEventHub() { @@ -562,6 +577,19 @@ public: device->mscBitmask.loadFromBuffer(buffer); } + void addRawLightInfo(int32_t rawId, RawLightInfo&& info) { + mRawLightInfos.emplace(rawId, std::move(info)); + } + + void fakeLightBrightness(int32_t rawId, int32_t brightness) { + mLightBrightness.emplace(rawId, brightness); + } + + void fakeLightIntensities(int32_t rawId, + const std::unordered_map intensities) { + mLightIntensities.emplace(rawId, std::move(intensities)); + } + bool getLedState(int32_t deviceId, int32_t led) { Device* device = getDevice(deviceId); return device->leds.valueFor(led); @@ -869,6 +897,48 @@ private: std::optional getBatteryStatus(int32_t) const override { return BATTERY_STATUS; } + const std::vector getRawLightIds(int32_t deviceId) override { + std::vector ids; + for (const auto& [rawId, info] : mRawLightInfos) { + ids.push_back(rawId); + } + return ids; + } + + std::optional getRawLightInfo(int32_t deviceId, int32_t lightId) override { + auto it = mRawLightInfos.find(lightId); + if (it == mRawLightInfos.end()) { + return std::nullopt; + } + return it->second; + } + + void setLightBrightness(int32_t deviceId, int32_t lightId, int32_t brightness) override { + mLightBrightness.emplace(lightId, brightness); + } + + void setLightIntensities(int32_t deviceId, int32_t lightId, + std::unordered_map intensities) override { + mLightIntensities.emplace(lightId, intensities); + }; + + std::optional getLightBrightness(int32_t deviceId, int32_t lightId) override { + auto lightIt = mLightBrightness.find(lightId); + if (lightIt == mLightBrightness.end()) { + return std::nullopt; + } + return lightIt->second; + } + + std::optional> getLightIntensities( + int32_t deviceId, int32_t lightId) override { + auto lightIt = mLightIntensities.find(lightId); + if (lightIt == mLightIntensities.end()) { + return std::nullopt; + } + return lightIt->second; + }; + virtual bool isExternal(int32_t) const { return false; } @@ -1976,6 +2046,49 @@ TEST_F(InputReaderTest, BatteryGetStatus) { ASSERT_EQ(mReader->getBatteryStatus(deviceId), BATTERY_STATUS); } +class FakeLightInputMapper : public FakeInputMapper { +public: + FakeLightInputMapper(InputDeviceContext& deviceContext, uint32_t sources) + : FakeInputMapper(deviceContext, sources) {} + + bool setLightColor(int32_t lightId, int32_t color) override { + getDeviceContext().setLightBrightness(lightId, color >> 24); + return true; + } + + std::optional getLightColor(int32_t lightId) override { + std::optional result = getDeviceContext().getLightBrightness(lightId); + if (!result.has_value()) { + return std::nullopt; + } + return result.value() << 24; + } +}; + +TEST_F(InputReaderTest, LightGetColor) { + constexpr int32_t deviceId = END_RESERVED_ID + 1000; + Flags deviceClass = InputDeviceClass::KEYBOARD | InputDeviceClass::LIGHT; + constexpr int32_t eventHubId = 1; + const char* DEVICE_LOCATION = "BLUETOOTH"; + std::shared_ptr device = mReader->newDevice(deviceId, "fake", DEVICE_LOCATION); + FakeLightInputMapper& mapper = + device->addMapper(eventHubId, AINPUT_SOURCE_KEYBOARD); + mReader->pushNextDevice(device); + RawLightInfo info = {.id = 1, + .name = "Mono", + .maxBrightness = 255, + .flags = InputLightClass::BRIGHTNESS, + .path = ""}; + mFakeEventHub->addRawLightInfo(1 /* rawId */, std::move(info)); + mFakeEventHub->fakeLightBrightness(1 /* rawId */, 0x55); + + ASSERT_NO_FATAL_FAILURE(addDevice(eventHubId, "fake", deviceClass, nullptr)); + ASSERT_NO_FATAL_FAILURE(mapper.assertConfigureWasCalled()); + + ASSERT_TRUE(mReader->setLightColor(deviceId, 1 /* lightId */, LIGHT_BRIGHTNESS)); + ASSERT_EQ(mReader->getLightColor(deviceId, 1 /* lightId */), LIGHT_BRIGHTNESS); +} + // --- InputReaderIntegrationTest --- // These tests create and interact with the InputReader only through its interface. @@ -2883,14 +2996,136 @@ TEST_F(BatteryInputMapperTest, GetBatteryCapacity) { BatteryInputMapper& mapper = addMapperAndConfigure(); ASSERT_TRUE(mapper.getBatteryCapacity()); - ASSERT_EQ(*mapper.getBatteryCapacity(), BATTERY_CAPACITY); + ASSERT_EQ(mapper.getBatteryCapacity().value_or(-1), BATTERY_CAPACITY); } TEST_F(BatteryInputMapperTest, GetBatteryStatus) { BatteryInputMapper& mapper = addMapperAndConfigure(); ASSERT_TRUE(mapper.getBatteryStatus()); - ASSERT_EQ(*mapper.getBatteryStatus(), BATTERY_STATUS); + ASSERT_EQ(mapper.getBatteryStatus().value_or(-1), BATTERY_STATUS); +} + +// --- LightInputMapperTest --- +class LightInputMapperTest : public InputMapperTest { +protected: + void SetUp() override { InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::LIGHT); } +}; + +TEST_F(LightInputMapperTest, GetSources) { + LightInputMapper& mapper = addMapperAndConfigure(); + + ASSERT_EQ(AINPUT_SOURCE_UNKNOWN, mapper.getSources()); +} + +TEST_F(LightInputMapperTest, SingleLight) { + RawLightInfo infoSingle = {.id = 1, + .name = "Mono", + .maxBrightness = 255, + .flags = InputLightClass::BRIGHTNESS, + .path = ""}; + mFakeEventHub->addRawLightInfo(infoSingle.id, std::move(infoSingle)); + + LightInputMapper& mapper = addMapperAndConfigure(); + InputDeviceInfo info; + mapper.populateDeviceInfo(&info); + const auto& ids = info.getLightIds(); + ASSERT_EQ(1UL, ids.size()); + ASSERT_EQ(InputDeviceLightType::SINGLE, info.getLightInfo(ids[0])->type); + + ASSERT_TRUE(mapper.setLightColor(ids[0], LIGHT_BRIGHTNESS)); + ASSERT_EQ(mapper.getLightColor(ids[0]).value_or(-1), LIGHT_BRIGHTNESS); +} + +TEST_F(LightInputMapperTest, RGBLight) { + RawLightInfo infoRed = {.id = 1, + .name = "red", + .maxBrightness = 255, + .flags = InputLightClass::BRIGHTNESS | InputLightClass::RED, + .path = ""}; + RawLightInfo infoGreen = {.id = 2, + .name = "green", + .maxBrightness = 255, + .flags = InputLightClass::BRIGHTNESS | InputLightClass::GREEN, + .path = ""}; + RawLightInfo infoBlue = {.id = 3, + .name = "blue", + .maxBrightness = 255, + .flags = InputLightClass::BRIGHTNESS | InputLightClass::BLUE, + .path = ""}; + mFakeEventHub->addRawLightInfo(infoRed.id, std::move(infoRed)); + mFakeEventHub->addRawLightInfo(infoGreen.id, std::move(infoGreen)); + mFakeEventHub->addRawLightInfo(infoBlue.id, std::move(infoBlue)); + + LightInputMapper& mapper = addMapperAndConfigure(); + InputDeviceInfo info; + mapper.populateDeviceInfo(&info); + const auto& ids = info.getLightIds(); + ASSERT_EQ(1UL, ids.size()); + ASSERT_EQ(InputDeviceLightType::RGB, info.getLightInfo(ids[0])->type); + + ASSERT_TRUE(mapper.setLightColor(ids[0], LIGHT_COLOR)); + ASSERT_EQ(mapper.getLightColor(ids[0]).value_or(-1), LIGHT_COLOR); +} + +TEST_F(LightInputMapperTest, MultiColorRGBLight) { + RawLightInfo infoColor = {.id = 1, + .name = "red", + .maxBrightness = 255, + .flags = InputLightClass::BRIGHTNESS | + InputLightClass::MULTI_INTENSITY | + InputLightClass::MULTI_INDEX, + .path = ""}; + + mFakeEventHub->addRawLightInfo(infoColor.id, std::move(infoColor)); + + LightInputMapper& mapper = addMapperAndConfigure(); + InputDeviceInfo info; + mapper.populateDeviceInfo(&info); + const auto& ids = info.getLightIds(); + ASSERT_EQ(1UL, ids.size()); + ASSERT_EQ(InputDeviceLightType::MULTI_COLOR, info.getLightInfo(ids[0])->type); + + ASSERT_TRUE(mapper.setLightColor(ids[0], LIGHT_COLOR)); + ASSERT_EQ(mapper.getLightColor(ids[0]).value_or(-1), LIGHT_COLOR); +} + +TEST_F(LightInputMapperTest, PlayerIdLight) { + RawLightInfo info1 = {.id = 1, + .name = "player1", + .maxBrightness = 255, + .flags = InputLightClass::BRIGHTNESS, + .path = ""}; + RawLightInfo info2 = {.id = 2, + .name = "player2", + .maxBrightness = 255, + .flags = InputLightClass::BRIGHTNESS, + .path = ""}; + RawLightInfo info3 = {.id = 3, + .name = "player3", + .maxBrightness = 255, + .flags = InputLightClass::BRIGHTNESS, + .path = ""}; + RawLightInfo info4 = {.id = 4, + .name = "player4", + .maxBrightness = 255, + .flags = InputLightClass::BRIGHTNESS, + .path = ""}; + mFakeEventHub->addRawLightInfo(info1.id, std::move(info1)); + mFakeEventHub->addRawLightInfo(info2.id, std::move(info2)); + mFakeEventHub->addRawLightInfo(info3.id, std::move(info3)); + mFakeEventHub->addRawLightInfo(info4.id, std::move(info4)); + + LightInputMapper& mapper = addMapperAndConfigure(); + InputDeviceInfo info; + mapper.populateDeviceInfo(&info); + const auto& ids = info.getLightIds(); + ASSERT_EQ(1UL, ids.size()); + ASSERT_EQ(InputDeviceLightType::PLAYER_ID, info.getLightInfo(ids[0])->type); + + ASSERT_FALSE(mapper.setLightColor(ids[0], LIGHT_COLOR)); + ASSERT_TRUE(mapper.setLightPlayerId(ids[0], LIGHT_PLAYER_ID)); + ASSERT_EQ(mapper.getLightPlayerId(ids[0]).value_or(-1), LIGHT_PLAYER_ID); } // --- KeyboardInputMapperTest --- -- cgit v1.2.3-59-g8ed1b From 7632c3391394a47cface411997ce03d126d1e4da Mon Sep 17 00:00:00 2001 From: arthurhung Date: Wed, 30 Dec 2020 16:58:01 +0800 Subject: Move drag event to InputDispatcher (1/n) This CL adds the ability to send a DRAG event through the InputChannel, and adds the appropriate processing logic to InputPublisher and InputConsumer. Bug: 158242495 Test: atest libinput_tests InputPublisherAndConsumerTest Change-Id: I7aead341a9851facf654024c476bd6d7eaae4590 --- include/android/input.h | 3 ++ include/input/Input.h | 29 ++++++++++ include/input/InputTransport.h | 21 ++++++++ libs/input/Input.cpp | 35 +++++++++++++ libs/input/InputTransport.cpp | 51 ++++++++++++++++++ .../input/tests/InputPublisherAndConsumer_test.cpp | 61 +++++++++++++++++++++- libs/input/tests/StructLayout_test.cpp | 7 +++ 7 files changed, 205 insertions(+), 2 deletions(-) (limited to 'include/android/input.h') diff --git a/include/android/input.h b/include/android/input.h index 797348742d..6fe95c0fc3 100644 --- a/include/android/input.h +++ b/include/android/input.h @@ -166,6 +166,9 @@ enum { /** Capture event */ AINPUT_EVENT_TYPE_CAPTURE = 4, + + /** Drag event */ + AINPUT_EVENT_TYPE_DRAG = 5, }; /** diff --git a/include/input/Input.h b/include/input/Input.h index aa42db8ea8..f9fe6b9597 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -792,6 +792,30 @@ protected: bool mPointerCaptureEnabled; }; +/* + * Drag events. + */ +class DragEvent : public InputEvent { +public: + virtual ~DragEvent() {} + + virtual int32_t getType() const override { return AINPUT_EVENT_TYPE_DRAG; } + + inline bool isExiting() const { return mIsExiting; } + + inline float getX() const { return mX; } + + inline float getY() const { return mY; } + + void initialize(int32_t id, float x, float y, bool isExiting); + + void initialize(const DragEvent& from); + +protected: + bool mIsExiting; + float mX, mY; +}; + /** * Base class for verified events. * Do not create a VerifiedInputEvent explicitly. @@ -855,6 +879,7 @@ public: virtual MotionEvent* createMotionEvent() = 0; virtual FocusEvent* createFocusEvent() = 0; virtual CaptureEvent* createCaptureEvent() = 0; + virtual DragEvent* createDragEvent() = 0; }; /* @@ -870,12 +895,14 @@ public: virtual MotionEvent* createMotionEvent() override { return &mMotionEvent; } virtual FocusEvent* createFocusEvent() override { return &mFocusEvent; } virtual CaptureEvent* createCaptureEvent() override { return &mCaptureEvent; } + virtual DragEvent* createDragEvent() override { return &mDragEvent; } private: KeyEvent mKeyEvent; MotionEvent mMotionEvent; FocusEvent mFocusEvent; CaptureEvent mCaptureEvent; + DragEvent mDragEvent; }; /* @@ -890,6 +917,7 @@ public: virtual MotionEvent* createMotionEvent() override; virtual FocusEvent* createFocusEvent() override; virtual CaptureEvent* createCaptureEvent() override; + virtual DragEvent* createDragEvent() override; void recycle(InputEvent* event); @@ -900,6 +928,7 @@ private: std::queue> mMotionEventPool; std::queue> mFocusEventPool; std::queue> mCaptureEventPool; + std::queue> mDragEventPool; }; } // namespace android diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h index ba9ae204eb..f1b22587ce 100644 --- a/include/input/InputTransport.h +++ b/include/input/InputTransport.h @@ -69,6 +69,7 @@ struct InputMessage { FINISHED, FOCUS, CAPTURE, + DRAG, }; struct Header { @@ -183,6 +184,16 @@ struct InputMessage { inline size_t size() const { return sizeof(Capture); } } capture; + + struct Drag { + int32_t eventId; + float x; + float y; + bool isExiting; + uint8_t empty[3]; + + inline size_t size() const { return sizeof(Drag); } + } drag; } __attribute__((aligned(8))) body; bool isValid(size_t actualSize) const; @@ -354,6 +365,15 @@ public: */ status_t publishCaptureEvent(uint32_t seq, int32_t eventId, bool pointerCaptureEnabled); + /* Publishes a drag 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 publishDragEvent(uint32_t seq, int32_t eventId, float x, float y, bool isExiting); + /* Receives the finished signal from the consumer in reply to the original dispatch signal. * If a signal was received, returns the message sequence number, * whether the consumer handled the message, and the time the event was first read by the @@ -601,6 +621,7 @@ private: static void initializeMotionEvent(MotionEvent* event, const InputMessage* msg); static void initializeFocusEvent(FocusEvent* event, const InputMessage* msg); static void initializeCaptureEvent(CaptureEvent* event, const InputMessage* msg); + static void initializeDragEvent(DragEvent* 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 0a00d68556..5600eb3f5e 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -92,6 +92,9 @@ const char* inputEventTypeToString(int32_t type) { case AINPUT_EVENT_TYPE_CAPTURE: { return "CAPTURE"; } + case AINPUT_EVENT_TYPE_DRAG: { + return "DRAG"; + } } return "UNKNOWN"; } @@ -770,6 +773,23 @@ void CaptureEvent::initialize(const CaptureEvent& from) { mPointerCaptureEnabled = from.mPointerCaptureEnabled; } +// --- DragEvent --- + +void DragEvent::initialize(int32_t id, float x, float y, bool isExiting) { + InputEvent::initialize(id, ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID, AINPUT_SOURCE_UNKNOWN, + ADISPLAY_ID_NONE, INVALID_HMAC); + mIsExiting = isExiting; + mX = x; + mY = y; +} + +void DragEvent::initialize(const DragEvent& from) { + InputEvent::initialize(from); + mIsExiting = from.mIsExiting; + mX = from.mX; + mY = from.mY; +} + // --- PooledInputEventFactory --- PooledInputEventFactory::PooledInputEventFactory(size_t maxPoolSize) : @@ -815,6 +835,15 @@ CaptureEvent* PooledInputEventFactory::createCaptureEvent() { return event; } +DragEvent* PooledInputEventFactory::createDragEvent() { + if (mDragEventPool.empty()) { + return new DragEvent(); + } + DragEvent* event = mDragEventPool.front().release(); + mDragEventPool.pop(); + return event; +} + void PooledInputEventFactory::recycle(InputEvent* event) { switch (event->getType()) { case AINPUT_EVENT_TYPE_KEY: @@ -842,6 +871,12 @@ void PooledInputEventFactory::recycle(InputEvent* event) { return; } break; + case AINPUT_EVENT_TYPE_DRAG: + if (mDragEventPool.size() < mMaxPoolSize) { + mDragEventPool.push(std::unique_ptr(static_cast(event))); + return; + } + break; } delete event; } diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp index ee2daec5fc..6ef01737f9 100644 --- a/libs/input/InputTransport.cpp +++ b/libs/input/InputTransport.cpp @@ -108,6 +108,8 @@ bool InputMessage::isValid(size_t actualSize) const { return true; case Type::CAPTURE: return true; + case Type::DRAG: + return true; } } return false; @@ -125,6 +127,8 @@ size_t InputMessage::size() const { return sizeof(Header) + body.focus.size(); case Type::CAPTURE: return sizeof(Header) + body.capture.size(); + case Type::DRAG: + return sizeof(Header) + body.drag.size(); } return sizeof(Header); } @@ -249,6 +253,13 @@ void InputMessage::getSanitizedCopy(InputMessage* msg) const { msg->body.capture.pointerCaptureEnabled = body.capture.pointerCaptureEnabled; break; } + case InputMessage::Type::DRAG: { + msg->body.drag.eventId = body.drag.eventId; + msg->body.drag.x = body.drag.x; + msg->body.drag.y = body.drag.y; + msg->body.drag.isExiting = body.drag.isExiting; + break; + } } } @@ -599,6 +610,25 @@ status_t InputPublisher::publishCaptureEvent(uint32_t seq, int32_t eventId, return mChannel->sendMessage(&msg); } +status_t InputPublisher::publishDragEvent(uint32_t seq, int32_t eventId, float x, float y, + bool isExiting) { + if (ATRACE_ENABLED()) { + std::string message = + StringPrintf("publishDragEvent(inputChannel=%s, x=%f, y=%f, isExiting=%s)", + mChannel->getName().c_str(), x, y, toString(isExiting)); + ATRACE_NAME(message.c_str()); + } + + InputMessage msg; + msg.header.type = InputMessage::Type::DRAG; + msg.header.seq = seq; + msg.body.drag.eventId = eventId; + msg.body.drag.isExiting = isExiting; + msg.body.drag.x = x; + msg.body.drag.y = y; + return mChannel->sendMessage(&msg); +} + status_t InputPublisher::receiveFinishedSignal( const std::function& callback) { if (DEBUG_TRANSPORT_ACTIONS) { @@ -779,6 +809,16 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consum *outEvent = captureEvent; break; } + + case InputMessage::Type::DRAG: { + DragEvent* dragEvent = factory->createDragEvent(); + if (!dragEvent) return NO_MEMORY; + + initializeDragEvent(dragEvent, &mMsg); + *outSeq = mMsg.header.seq; + *outEvent = dragEvent; + break; + } } } return OK; @@ -1236,6 +1276,11 @@ void InputConsumer::initializeCaptureEvent(CaptureEvent* event, const InputMessa event->initialize(msg->body.capture.eventId, msg->body.capture.pointerCaptureEnabled); } +void InputConsumer::initializeDragEvent(DragEvent* event, const InputMessage* msg) { + event->initialize(msg->body.drag.eventId, msg->body.drag.x, msg->body.drag.y, + msg->body.drag.isExiting); +} + void InputConsumer::initializeMotionEvent(MotionEvent* event, const InputMessage* msg) { uint32_t pointerCount = msg->body.motion.pointerCount; PointerProperties pointerProperties[pointerCount]; @@ -1346,6 +1391,12 @@ std::string InputConsumer::dump() const { .pointerCaptureEnabled)); break; } + case InputMessage::Type::DRAG: { + out += android::base::StringPrintf("x=%.1f y=%.1f, isExiting=%s", + msg.body.drag.x, msg.body.drag.y, + toString(msg.body.drag.isExiting)); + break; + } } out += "\n"; } diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp index e7e566dde6..b5ed8d7ba7 100644 --- a/libs/input/tests/InputPublisherAndConsumer_test.cpp +++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp @@ -52,6 +52,7 @@ protected: void PublishAndConsumeMotionEvent(); void PublishAndConsumeFocusEvent(); void PublishAndConsumeCaptureEvent(); + void PublishAndConsumeDragEvent(); }; TEST_F(InputPublisherAndConsumerTest, GetChannel_ReturnsTheChannel) { @@ -301,7 +302,7 @@ void InputPublisherAndConsumerTest::PublishAndConsumeFocusEvent() { const nsecs_t publishTime = systemTime(SYSTEM_TIME_MONOTONIC); status = mPublisher->publishFocusEvent(seq, eventId, hasFocus, inTouchMode); - ASSERT_EQ(OK, status) << "publisher publishKeyEvent should return OK"; + ASSERT_EQ(OK, status) << "publisher publishFocusEvent should return OK"; uint32_t consumeSeq; InputEvent* event; @@ -349,7 +350,7 @@ void InputPublisherAndConsumerTest::PublishAndConsumeCaptureEvent() { const nsecs_t publishTime = systemTime(SYSTEM_TIME_MONOTONIC); status = mPublisher->publishCaptureEvent(seq, eventId, captureEnabled); - ASSERT_EQ(OK, status) << "publisher publishKeyEvent should return OK"; + ASSERT_EQ(OK, status) << "publisher publishCaptureEvent should return OK"; uint32_t consumeSeq; InputEvent* event; @@ -387,6 +388,57 @@ void InputPublisherAndConsumerTest::PublishAndConsumeCaptureEvent() { << "finished signal's consume time should be greater than publish time"; } +void InputPublisherAndConsumerTest::PublishAndConsumeDragEvent() { + status_t status; + + constexpr uint32_t seq = 15; + int32_t eventId = InputEvent::nextId(); + constexpr bool isExiting = false; + constexpr float x = 10; + constexpr float y = 15; + const nsecs_t publishTime = systemTime(SYSTEM_TIME_MONOTONIC); + + status = mPublisher->publishDragEvent(seq, eventId, x, y, isExiting); + ASSERT_EQ(OK, status) << "publisher publishDragEvent 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_DRAG, event->getType()) + << "consumer should have returned a drag event"; + + DragEvent* dragEvent = static_cast(event); + EXPECT_EQ(seq, consumeSeq); + EXPECT_EQ(eventId, dragEvent->getId()); + EXPECT_EQ(isExiting, dragEvent->isExiting()); + EXPECT_EQ(x, dragEvent->getX()); + EXPECT_EQ(y, dragEvent->getY()); + + status = mConsumer->sendFinishedSignal(seq, true); + ASSERT_EQ(OK, status) << "consumer sendFinishedSignal should return OK"; + + uint32_t finishedSeq = 0; + bool handled = false; + nsecs_t consumeTime; + status = mPublisher->receiveFinishedSignal( + [&finishedSeq, &handled, &consumeTime](uint32_t inSeq, bool inHandled, + nsecs_t inConsumeTime) -> void { + finishedSeq = inSeq; + handled = inHandled; + consumeTime = inConsumeTime; + }); + 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"; + ASSERT_GE(consumeTime, publishTime) + << "finished signal's consume time should be greater than publish time"; +} + TEST_F(InputPublisherAndConsumerTest, PublishKeyEvent_EndToEnd) { ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent()); } @@ -403,6 +455,10 @@ TEST_F(InputPublisherAndConsumerTest, PublishCaptureEvent_EndToEnd) { ASSERT_NO_FATAL_FAILURE(PublishAndConsumeCaptureEvent()); } +TEST_F(InputPublisherAndConsumerTest, PublishDragEvent_EndToEnd) { + ASSERT_NO_FATAL_FAILURE(PublishAndConsumeDragEvent()); +} + TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenSequenceNumberIsZero_ReturnsError) { status_t status; const size_t pointerCount = 1; @@ -468,6 +524,7 @@ TEST_F(InputPublisherAndConsumerTest, PublishMultipleEvents_EndToEnd) { ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent()); ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent()); ASSERT_NO_FATAL_FAILURE(PublishAndConsumeCaptureEvent()); + ASSERT_NO_FATAL_FAILURE(PublishAndConsumeDragEvent()); 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 8f436085f7..3d80b38636 100644 --- a/libs/input/tests/StructLayout_test.cpp +++ b/libs/input/tests/StructLayout_test.cpp @@ -87,6 +87,12 @@ void TestInputMessageAlignment() { CHECK_OFFSET(InputMessage::Body::Capture, pointerCaptureEnabled, 4); CHECK_OFFSET(InputMessage::Body::Capture, empty, 5); + CHECK_OFFSET(InputMessage::Body::Drag, eventId, 0); + CHECK_OFFSET(InputMessage::Body::Drag, x, 4); + CHECK_OFFSET(InputMessage::Body::Drag, y, 8); + CHECK_OFFSET(InputMessage::Body::Drag, isExiting, 12); + CHECK_OFFSET(InputMessage::Body::Drag, empty, 13); + CHECK_OFFSET(InputMessage::Body::Finished, handled, 0); CHECK_OFFSET(InputMessage::Body::Finished, empty, 1); CHECK_OFFSET(InputMessage::Body::Finished, consumeTime, 8); @@ -110,6 +116,7 @@ void TestBodySize() { static_assert(sizeof(InputMessage::Body::Finished) == 16); static_assert(sizeof(InputMessage::Body::Focus) == 8); static_assert(sizeof(InputMessage::Body::Capture) == 8); + static_assert(sizeof(InputMessage::Body::Drag) == 16); } // --- VerifiedInputEvent --- -- cgit v1.2.3-59-g8ed1b From 2745c956d03aa5bba0fe8b0efb58a7c2756f611b Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Wed, 17 Mar 2021 20:27:41 +0000 Subject: Update android/input.h docs Fix doc generation error Bug: 183013620 Test: ./_scripts/update_api_doc.py ~/android/master/ 2>&1 >/dev/null | grep -i input Before: android/input.h:680: warning: unable to resolve link to `RELATIVE_X' for \link command After: the line above is not present Change-Id: I995ac91d8bfd53309607e3cdac305adb68d40d78 --- include/android/input.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/android/input.h') diff --git a/include/android/input.h b/include/android/input.h index 6fe95c0fc3..bb98beb41a 100644 --- a/include/android/input.h +++ b/include/android/input.h @@ -678,7 +678,7 @@ enum { /** * Axis constant: The movement of y position of a motion event. * - * Same as {@link RELATIVE_X}, but for y position. + * Same as {@link AMOTION_EVENT_AXIS_RELATIVE_X}, but for y position. */ AMOTION_EVENT_AXIS_RELATIVE_Y = 28, /** -- cgit v1.2.3-59-g8ed1b