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
diff --git a/include/android/input.h b/include/android/input.h
index b04775b..38af89a 100644
--- a/include/android/input.h
+++ b/include/android/input.h
@@ -163,6 +163,9 @@
 
     /** 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 3facfa5..aa42db8 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -773,6 +773,25 @@
     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 @@
     virtual KeyEvent* createKeyEvent() = 0;
     virtual MotionEvent* createMotionEvent() = 0;
     virtual FocusEvent* createFocusEvent() = 0;
+    virtual CaptureEvent* createCaptureEvent() = 0;
 };
 
 /*
@@ -849,11 +869,13 @@
     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 @@
     virtual KeyEvent* createKeyEvent() override;
     virtual MotionEvent* createMotionEvent() override;
     virtual FocusEvent* createFocusEvent() override;
+    virtual CaptureEvent* createCaptureEvent() override;
 
     void recycle(InputEvent* event);
 
@@ -876,6 +899,7 @@
     std::queue<std::unique_ptr<KeyEvent>> mKeyEventPool;
     std::queue<std::unique_ptr<MotionEvent>> mMotionEventPool;
     std::queue<std::unique_ptr<FocusEvent>> mFocusEventPool;
+    std::queue<std::unique_ptr<CaptureEvent>> mCaptureEventPool;
 };
 
 } // namespace android
diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h
index ad0a14e..8744ef7 100644
--- a/include/input/InputTransport.h
+++ b/include/input/InputTransport.h
@@ -67,6 +67,7 @@
         MOTION,
         FINISHED,
         FOCUS,
+        CAPTURE,
     };
 
     struct Header {
@@ -166,6 +167,13 @@
 
             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 @@
                 return "FINISHED";
             case Type::FOCUS:
                 return "FOCUS";
+            case Type::CAPTURE:
+                return "CAPTURE";
         }
     }
 };
@@ -341,6 +351,15 @@
      */
     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 @@
     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 0ea3889..0a00d68 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -89,6 +89,9 @@
         case AINPUT_EVENT_TYPE_FOCUS: {
             return "FOCUS";
         }
+        case AINPUT_EVENT_TYPE_CAPTURE: {
+            return "CAPTURE";
+        }
     }
     return "UNKNOWN";
 }
@@ -754,6 +757,19 @@
     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 @@
     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 @@
             return;
         }
         break;
+    case AINPUT_EVENT_TYPE_CAPTURE:
+        if (mCaptureEventPool.size() < mMaxPoolSize) {
+            mCaptureEventPool.push(
+                    std::unique_ptr<CaptureEvent>(static_cast<CaptureEvent*>(event)));
+            return;
+        }
+        break;
     }
     delete event;
 }
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index 85df405..acea473 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -105,6 +105,8 @@
                 return true;
             case Type::FOCUS:
                 return true;
+            case Type::CAPTURE:
+                return true;
         }
     }
     return false;
@@ -120,6 +122,8 @@
             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 @@
             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 @@
     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 @@
                 *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 @@
                       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 @@
                                                        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 4f53dc9..9da7b69 100644
--- a/libs/input/tests/InputPublisherAndConsumer_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp
@@ -51,6 +51,7 @@
     void PublishAndConsumeKeyEvent();
     void PublishAndConsumeMotionEvent();
     void PublishAndConsumeFocusEvent();
+    void PublishAndConsumeCaptureEvent();
 };
 
 TEST_F(InputPublisherAndConsumerTest, GetChannel_ReturnsTheChannel) {
@@ -309,6 +310,43 @@
             << "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<CaptureEvent*>(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 @@
     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 @@
     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 3c5fb22..4107d61 100644
--- a/libs/input/tests/StructLayout_test.cpp
+++ b/libs/input/tests/StructLayout_test.cpp
@@ -79,6 +79,9 @@
   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 @@
                           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 ---