Add FocusEvent and InputMessage::Type::FOCUS
FocusEvents will be consumed by InputConsumer on the app side. They
will be used to notify app that focus has been gained / lost. They will
also carry information about the current state of touch mode.
Also add a new type of InputMessage with type FOCUS.
This new data structure will be used to pass focus events to the apps
from input across the socket.
Bug: 70668286
Test: presubmit
Change-Id: I88582c64ee41ecb49623b9b7f5c149eafa694788
diff --git a/include/android/input.h b/include/android/input.h
index ce439c6..f51cd79 100644
--- a/include/android/input.h
+++ b/include/android/input.h
@@ -158,7 +158,10 @@
AINPUT_EVENT_TYPE_KEY = 1,
/** Indicates that the input event is a motion event. */
- AINPUT_EVENT_TYPE_MOTION = 2
+ AINPUT_EVENT_TYPE_MOTION = 2,
+
+ /** Focus event */
+ AINPUT_EVENT_TYPE_FOCUS = 3,
};
/**
diff --git a/include/input/Input.h b/include/input/Input.h
index a7e706e..f871847 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -167,6 +167,8 @@
class Parcel;
#endif
+const char* inputEventTypeToString(int32_t type);
+
/*
* Flags that flow alongside events in the input dispatch system to help with certain
* policy decisions such as waking from device sleep.
@@ -687,6 +689,28 @@
};
/*
+ * Focus events.
+ */
+class FocusEvent : public InputEvent {
+public:
+ virtual ~FocusEvent() {}
+
+ virtual int32_t getType() const override { return AINPUT_EVENT_TYPE_FOCUS; }
+
+ inline bool getHasFocus() const { return mHasFocus; }
+
+ inline bool getInTouchMode() const { return mInTouchMode; }
+
+ void initialize(bool hasFocus, bool inTouchMode);
+
+ void initialize(const FocusEvent& from);
+
+protected:
+ bool mHasFocus;
+ bool mInTouchMode;
+};
+
+/*
* Input event factory.
*/
class InputEventFactoryInterface {
@@ -698,6 +722,7 @@
virtual KeyEvent* createKeyEvent() = 0;
virtual MotionEvent* createMotionEvent() = 0;
+ virtual FocusEvent* createFocusEvent() = 0;
};
/*
@@ -711,10 +736,12 @@
virtual KeyEvent* createKeyEvent() override { return &mKeyEvent; }
virtual MotionEvent* createMotionEvent() override { return &mMotionEvent; }
+ virtual FocusEvent* createFocusEvent() override { return &mFocusEvent; }
private:
KeyEvent mKeyEvent;
MotionEvent mMotionEvent;
+ FocusEvent mFocusEvent;
};
/*
@@ -727,6 +754,7 @@
virtual KeyEvent* createKeyEvent() override;
virtual MotionEvent* createMotionEvent() override;
+ virtual FocusEvent* createFocusEvent() override;
void recycle(InputEvent* event);
@@ -735,6 +763,7 @@
std::queue<std::unique_ptr<KeyEvent>> mKeyEventPool;
std::queue<std::unique_ptr<MotionEvent>> mMotionEventPool;
+ std::queue<std::unique_ptr<FocusEvent>> mFocusEventPool;
};
} // namespace android
diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h
index d39ee25..ae47438 100644
--- a/include/input/InputTransport.h
+++ b/include/input/InputTransport.h
@@ -64,6 +64,7 @@
KEY,
MOTION,
FINISHED,
+ FOCUS,
};
struct Header {
@@ -92,9 +93,7 @@
uint32_t empty2;
nsecs_t downTime __attribute__((aligned(8)));
- inline size_t size() const {
- return sizeof(Key);
- }
+ inline size_t size() const { return sizeof(Key); }
} key;
struct Motion {
@@ -110,7 +109,7 @@
int32_t metaState;
int32_t buttonState;
MotionClassification classification; // base type: uint8_t
- uint8_t empty2[3];
+ uint8_t empty2[3]; // 3 bytes to fill gap created by classification
int32_t edgeFlags;
nsecs_t downTime __attribute__((aligned(8)));
float xOffset;
@@ -148,10 +147,17 @@
uint32_t seq;
uint32_t handled; // actually a bool, but we must maintain 8-byte alignment
- inline size_t size() const {
- return sizeof(Finished);
- }
+ inline size_t size() const { return sizeof(Finished); }
} finished;
+
+ struct Focus {
+ uint32_t seq;
+ // The following two fields take up 4 bytes total
+ uint16_t hasFocus; // actually a bool
+ uint16_t inTouchMode; // actually a bool, but we must maintain 8-byte alignment
+
+ inline size_t size() const { return sizeof(Focus); }
+ } focus;
} __attribute__((aligned(8))) body;
bool isValid(size_t actualSize) const;
@@ -294,6 +300,15 @@
uint32_t pointerCount, const PointerProperties* pointerProperties,
const PointerCoords* pointerCoords);
+ /* Publishes a focus event to the input channel.
+ *
+ * Returns OK on success.
+ * Returns WOULD_BLOCK if the channel is full.
+ * Returns DEAD_OBJECT if the channel's peer has been closed.
+ * Other errors probably indicate that the channel is broken.
+ */
+ status_t publishFocusEvent(uint32_t seq, bool hasFocus, bool inTouchMode);
+
/* Receives the finished signal from the consumer in reply to the original dispatch signal.
* If a signal was received, returns the message sequence number,
* and whether the consumer handled the message.
@@ -349,8 +364,8 @@
* Returns NO_MEMORY if the event could not be created.
* Other errors probably indicate that the channel is broken.
*/
- status_t consume(InputEventFactoryInterface* factory, bool consumeBatches,
- nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent);
+ status_t consume(InputEventFactoryInterface* factory, bool consumeBatches, nsecs_t frameTime,
+ uint32_t* outSeq, InputEvent** outEvent);
/* Sends a finished signal to the publisher to inform it that the message
* with the specified sequence number has finished being process and whether
@@ -521,6 +536,7 @@
static void rewriteMessage(TouchState& state, InputMessage& msg);
static void initializeKeyEvent(KeyEvent* event, const InputMessage* msg);
static void initializeMotionEvent(MotionEvent* event, const InputMessage* msg);
+ static void initializeFocusEvent(FocusEvent* event, const InputMessage* msg);
static void addSample(MotionEvent* event, const InputMessage* msg);
static bool canAddSample(const Batch& batch, const InputMessage* msg);
static ssize_t findSampleNoLaterThan(const Batch& batch, nsecs_t time);
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index c7303ef..8ccbc7f 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -20,6 +20,7 @@
#include <limits.h>
#include <input/Input.h>
+#include <input/InputDevice.h>
#include <input/InputEventLabels.h>
#ifdef __ANDROID__
@@ -41,6 +42,21 @@
// --- InputEvent ---
+const char* inputEventTypeToString(int32_t type) {
+ switch (type) {
+ case AINPUT_EVENT_TYPE_KEY: {
+ return "KEY";
+ }
+ case AINPUT_EVENT_TYPE_MOTION: {
+ return "MOTION";
+ }
+ case AINPUT_EVENT_TYPE_FOCUS: {
+ return "FOCUS";
+ }
+ }
+ return "UNKNOWN";
+}
+
void InputEvent::initialize(int32_t deviceId, int32_t source, int32_t displayId) {
mDeviceId = deviceId;
mSource = source;
@@ -587,6 +603,20 @@
return getAxisByLabel(label);
}
+// --- FocusEvent ---
+
+void FocusEvent::initialize(bool hasFocus, bool inTouchMode) {
+ InputEvent::initialize(ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID, AINPUT_SOURCE_UNKNOWN,
+ ADISPLAY_ID_NONE);
+ mHasFocus = hasFocus;
+ mInTouchMode = inTouchMode;
+}
+
+void FocusEvent::initialize(const FocusEvent& from) {
+ InputEvent::initialize(from);
+ mHasFocus = from.mHasFocus;
+ mInTouchMode = from.mInTouchMode;
+}
// --- PooledInputEventFactory ---
@@ -615,6 +645,15 @@
return event;
}
+FocusEvent* PooledInputEventFactory::createFocusEvent() {
+ if (mFocusEventPool.empty()) {
+ return new FocusEvent();
+ }
+ FocusEvent* event = mFocusEventPool.front().release();
+ mFocusEventPool.pop();
+ return event;
+}
+
void PooledInputEventFactory::recycle(InputEvent* event) {
switch (event->getType()) {
case AINPUT_EVENT_TYPE_KEY:
@@ -629,6 +668,12 @@
return;
}
break;
+ case AINPUT_EVENT_TYPE_FOCUS:
+ if (mFocusEventPool.size() < mMaxPoolSize) {
+ mFocusEventPool.push(std::unique_ptr<FocusEvent>(static_cast<FocusEvent*>(event)));
+ return;
+ }
+ break;
}
delete event;
}
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index b7937dc..200e1f3 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -103,6 +103,8 @@
return body.motion.pointerCount > 0 && body.motion.pointerCount <= MAX_POINTERS;
case Type::FINISHED:
return true;
+ case Type::FOCUS:
+ return true;
}
}
return false;
@@ -116,6 +118,8 @@
return sizeof(Header) + body.motion.size();
case Type::FINISHED:
return sizeof(Header) + body.finished.size();
+ case Type::FOCUS:
+ return sizeof(Header) + body.focus.size();
}
return sizeof(Header);
}
@@ -220,6 +224,12 @@
msg->body.finished.handled = body.finished.handled;
break;
}
+ case InputMessage::Type::FOCUS: {
+ msg->body.focus.seq = body.focus.seq;
+ msg->body.focus.hasFocus = body.focus.hasFocus;
+ msg->body.focus.inTouchMode = body.focus.inTouchMode;
+ break;
+ }
}
}
@@ -529,6 +539,23 @@
return mChannel->sendMessage(&msg);
}
+status_t InputPublisher::publishFocusEvent(uint32_t seq, bool hasFocus, bool inTouchMode) {
+ if (ATRACE_ENABLED()) {
+ std::string message =
+ StringPrintf("publishFocusEvent(inputChannel=%s, hasFocus=%s, inTouchMode=%s)",
+ mChannel->getName().c_str(), toString(hasFocus),
+ toString(inTouchMode));
+ ATRACE_NAME(message.c_str());
+ }
+
+ InputMessage msg;
+ msg.header.type = InputMessage::Type::FOCUS;
+ msg.body.focus.seq = seq;
+ msg.body.focus.hasFocus = hasFocus ? 1 : 0;
+ msg.body.focus.inTouchMode = inTouchMode ? 1 : 0;
+ return mChannel->sendMessage(&msg);
+}
+
status_t InputPublisher::receiveFinishedSignal(uint32_t* outSeq, bool* outHandled) {
if (DEBUG_TRANSPORT_ACTIONS) {
ALOGD("channel '%s' publisher ~ receiveFinishedSignal", mChannel->getName().c_str());
@@ -565,8 +592,8 @@
return property_get_bool(PROPERTY_RESAMPLING_ENABLED, true);
}
-status_t InputConsumer::consume(InputEventFactoryInterface* factory,
- bool consumeBatches, nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) {
+status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consumeBatches,
+ nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) {
if (DEBUG_TRANSPORT_ACTIONS) {
ALOGD("channel '%s' consumer ~ consume: consumeBatches=%s, frameTime=%" PRId64,
mChannel->getName().c_str(), toString(consumeBatches), frameTime);
@@ -669,19 +696,19 @@
break;
}
- MotionEvent* motionEvent = factory->createMotionEvent();
- if (! motionEvent) return NO_MEMORY;
+ MotionEvent* motionEvent = factory->createMotionEvent();
+ if (!motionEvent) return NO_MEMORY;
- updateTouchState(mMsg);
- initializeMotionEvent(motionEvent, &mMsg);
- *outSeq = mMsg.body.motion.seq;
- *outEvent = motionEvent;
+ updateTouchState(mMsg);
+ initializeMotionEvent(motionEvent, &mMsg);
+ *outSeq = mMsg.body.motion.seq;
+ *outEvent = motionEvent;
- if (DEBUG_TRANSPORT_ACTIONS) {
- ALOGD("channel '%s' consumer ~ consumed motion event, seq=%u",
- mChannel->getName().c_str(), *outSeq);
- }
- break;
+ if (DEBUG_TRANSPORT_ACTIONS) {
+ ALOGD("channel '%s' consumer ~ consumed motion event, seq=%u",
+ mChannel->getName().c_str(), *outSeq);
+ }
+ break;
}
case InputMessage::Type::FINISHED: {
@@ -689,6 +716,16 @@
"InputConsumer!");
break;
}
+
+ case InputMessage::Type::FOCUS: {
+ FocusEvent* focusEvent = factory->createFocusEvent();
+ if (!focusEvent) return NO_MEMORY;
+
+ initializeFocusEvent(focusEvent, &mMsg);
+ *outSeq = mMsg.body.focus.seq;
+ *outEvent = focusEvent;
+ break;
+ }
}
}
return OK;
@@ -1113,6 +1150,10 @@
msg->body.key.eventTime);
}
+void InputConsumer::initializeFocusEvent(FocusEvent* event, const InputMessage* msg) {
+ event->initialize(msg->body.focus.hasFocus == 1, msg->body.focus.inTouchMode == 1);
+}
+
void InputConsumer::initializeMotionEvent(MotionEvent* event, const InputMessage* msg) {
uint32_t pointerCount = msg->body.motion.pointerCount;
PointerProperties pointerProperties[pointerCount];
diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp
index a362f32..2fc77e9 100644
--- a/libs/input/tests/InputPublisherAndConsumer_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp
@@ -60,6 +60,7 @@
void PublishAndConsumeKeyEvent();
void PublishAndConsumeMotionEvent();
+ void PublishAndConsumeFocusEvent();
};
TEST_F(InputPublisherAndConsumerTest, GetChannel_ReturnsTheChannel) {
@@ -256,6 +257,43 @@
<< "publisher receiveFinishedSignal should have set handled to consumer's reply";
}
+void InputPublisherAndConsumerTest::PublishAndConsumeFocusEvent() {
+ status_t status;
+
+ constexpr uint32_t seq = 15;
+ constexpr bool hasFocus = true;
+ constexpr bool inTouchMode = true;
+
+ status = mPublisher->publishFocusEvent(seq, hasFocus, inTouchMode);
+ ASSERT_EQ(OK, status) << "publisher publishKeyEvent should return OK";
+
+ uint32_t consumeSeq;
+ InputEvent* event;
+ status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq, &event);
+ ASSERT_EQ(OK, status) << "consumer consume should return OK";
+
+ ASSERT_TRUE(event != nullptr) << "consumer should have returned non-NULL event";
+ ASSERT_EQ(AINPUT_EVENT_TYPE_FOCUS, event->getType())
+ << "consumer should have returned a focus event";
+
+ FocusEvent* focusEvent = static_cast<FocusEvent*>(event);
+ EXPECT_EQ(seq, consumeSeq);
+ EXPECT_EQ(hasFocus, focusEvent->getHasFocus());
+ EXPECT_EQ(inTouchMode, focusEvent->getInTouchMode());
+
+ status = mConsumer->sendFinishedSignal(seq, true);
+ ASSERT_EQ(OK, status) << "consumer sendFinishedSignal should return OK";
+
+ uint32_t finishedSeq = 0;
+ bool handled = false;
+ status = mPublisher->receiveFinishedSignal(&finishedSeq, &handled);
+ ASSERT_EQ(OK, status) << "publisher receiveFinishedSignal should return OK";
+ ASSERT_EQ(seq, finishedSeq)
+ << "publisher receiveFinishedSignal should have returned the original sequence number";
+ ASSERT_TRUE(handled)
+ << "publisher receiveFinishedSignal should have set handled to consumer's reply";
+}
+
TEST_F(InputPublisherAndConsumerTest, PublishKeyEvent_EndToEnd) {
ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent());
}
@@ -264,6 +302,10 @@
ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent());
}
+TEST_F(InputPublisherAndConsumerTest, PublishFocusEvent_EndToEnd) {
+ ASSERT_NO_FATAL_FAILURE(PublishAndConsumeFocusEvent());
+}
+
TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenSequenceNumberIsZero_ReturnsError) {
status_t status;
const size_t pointerCount = 1;
@@ -322,6 +364,7 @@
ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent());
ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent());
ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent());
+ ASSERT_NO_FATAL_FAILURE(PublishAndConsumeFocusEvent());
ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent());
ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent());
}
diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp
index 0fb6cfc..9ab0dba 100644
--- a/libs/input/tests/StructLayout_test.cpp
+++ b/libs/input/tests/StructLayout_test.cpp
@@ -69,6 +69,10 @@
CHECK_OFFSET(InputMessage::Body::Motion, pointerCount, 88);
CHECK_OFFSET(InputMessage::Body::Motion, pointers, 96);
+ CHECK_OFFSET(InputMessage::Body::Focus, seq, 0);
+ CHECK_OFFSET(InputMessage::Body::Focus, hasFocus, 4);
+ CHECK_OFFSET(InputMessage::Body::Focus, inTouchMode, 6);
+
CHECK_OFFSET(InputMessage::Body::Finished, seq, 0);
CHECK_OFFSET(InputMessage::Body::Finished, handled, 4);
}
@@ -87,6 +91,7 @@
offsetof(InputMessage::Body::Motion, pointers) +
sizeof(InputMessage::Body::Motion::Pointer) * MAX_POINTERS);
static_assert(sizeof(InputMessage::Body::Finished) == 8);
+ static_assert(sizeof(InputMessage::Body::Focus) == 8);
}
} // namespace android
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 746908b..9c0e08e 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -3036,7 +3036,7 @@
}
default:
- ALOGW("Cannot inject event of type %d", event->getType());
+ ALOGW("Cannot inject %s events", inputEventTypeToString(event->getType()));
return INPUT_EVENT_INJECTION_FAILED;
}
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index ebce47f..4f28261 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -19,6 +19,7 @@
#include <InputDispatcherThread.h>
#include <binder/Binder.h>
+#include <input/Input.h>
#include <gtest/gtest.h>
#include <linux/input.h>
@@ -410,7 +411,8 @@
ASSERT_NE(nullptr, event) << mName.c_str()
<< ": consumer should have returned non-NULL event.";
ASSERT_EQ(expectedEventType, event->getType())
- << mName.c_str() << ": event type should match.";
+ << mName.c_str() << "expected " << inputEventTypeToString(expectedEventType)
+ << " event, got " << inputEventTypeToString(event->getType()) << " event";
EXPECT_EQ(expectedDisplayId, event->getDisplayId());