Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7dfa7b85 authored by Jeff Brown's avatar Jeff Brown Committed by Android Git Automerger
Browse files

am cbee6d6e: Rewrite input transport using sockets.

* commit 'cbee6d6ede0499fb4a2c00bfc00d5db8d9ed5139':
  Rewrite input transport using sockets.
parents f4a42eb9 6cdee983
Loading
Loading
Loading
Loading
+112 −173
Original line number Diff line number Diff line
@@ -20,17 +20,13 @@
/**
 * Native input transport.
 *
 * Uses anonymous shared memory as a whiteboard for sending input events from an
 * InputPublisher to an InputConsumer and ensuring appropriate synchronization.
 * One interesting feature is that published events can be updated in place as long as they
 * have not yet been consumed.
 * The InputChannel provides a mechanism for exchanging InputMessage structures across processes.
 *
 * The InputPublisher and InputConsumer only take care of transferring event data
 * over an InputChannel and sending synchronization signals.  The InputDispatcher and InputQueue
 * build on these abstractions to add multiplexing and queueing.
 * The InputPublisher and InputConsumer each handle one end-point of an input channel.
 * The InputPublisher is used by the input dispatcher to send events to the application.
 * The InputConsumer is used by the application to receive events from the input dispatcher.
 */

#include <semaphore.h>
#include <ui/Input.h>
#include <utils/Errors.h>
#include <utils/Timers.h>
@@ -40,88 +36,25 @@
namespace android {

/*
 * An input channel consists of a shared memory buffer and a pair of pipes
 * used to send input messages from an InputPublisher to an InputConsumer
 * across processes.  Each channel has a descriptive name for debugging purposes.
 *
 * Each endpoint has its own InputChannel object that specifies its own file descriptors.
 *
 * The input channel is closed when all references to it are released.
 */
class InputChannel : public RefBase {
protected:
    virtual ~InputChannel();

public:
    InputChannel(const String8& name, int32_t ashmemFd, int32_t receivePipeFd,
            int32_t sendPipeFd);

    /* Creates a pair of input channels and their underlying shared memory buffers
     * and pipes.
     *
     * Returns OK on success.
     */
    static status_t openInputChannelPair(const String8& name,
            sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel);

    inline String8 getName() const { return mName; }
    inline int32_t getAshmemFd() const { return mAshmemFd; }
    inline int32_t getReceivePipeFd() const { return mReceivePipeFd; }
    inline int32_t getSendPipeFd() const { return mSendPipeFd; }

    /* Sends a signal to the other endpoint.
     *
     * Returns OK on success.
     * Returns DEAD_OBJECT if the channel's peer has been closed.
     * Other errors probably indicate that the channel is broken.
     */
    status_t sendSignal(char signal);

    /* Receives a signal send by the other endpoint.
     * (Should only call this after poll() indicates that the receivePipeFd has available input.)
     *
     * Returns OK on success.
     * Returns WOULD_BLOCK if there is no signal present.
     * Returns DEAD_OBJECT if the channel's peer has been closed.
     * Other errors probably indicate that the channel is broken.
     */
    status_t receiveSignal(char* outSignal);

private:
    String8 mName;
    int32_t mAshmemFd;
    int32_t mReceivePipeFd;
    int32_t mSendPipeFd;
};

/*
 * Private intermediate representation of input events as messages written into an
 * ashmem buffer.
 * Intermediate representation used to send input events and related signals.
 */
struct InputMessage {
    /* Semaphore count is set to 1 when the message is published.
     * It becomes 0 transiently while the publisher updates the message.
     * It becomes 0 permanently when the consumer consumes the message.
     */
    sem_t semaphore;

    /* Initialized to false by the publisher.
     * Set to true by the consumer when it consumes the message.
     */
    bool consumed;
    enum {
        TYPE_KEY = 1,
        TYPE_MOTION = 2,
        TYPE_FINISHED = 3,
    };

    int32_t type;
    struct Header {
        uint32_t type;
        uint32_t padding; // 8 byte alignment for the body that follows
    } header;

    struct SampleData {
    union Body {
        struct Key {
            nsecs_t eventTime;
        PointerCoords coords[0]; // variable length
    };

            int32_t deviceId;
            int32_t source;

    union {
        struct {
            int32_t action;
            int32_t flags;
            int32_t keyCode;
@@ -129,10 +62,16 @@ struct InputMessage {
            int32_t metaState;
            int32_t repeatCount;
            nsecs_t downTime;
            nsecs_t eventTime;

            inline size_t size() const {
                return sizeof(Key);
            }
        } key;

        struct {
        struct Motion {
            nsecs_t eventTime;
            int32_t deviceId;
            int32_t source;
            int32_t action;
            int32_t flags;
            int32_t metaState;
@@ -144,28 +83,87 @@ struct InputMessage {
            float xPrecision;
            float yPrecision;
            size_t pointerCount;
            PointerProperties pointerProperties[MAX_POINTERS];
            size_t sampleCount;
            SampleData sampleData[0]; // variable length
            struct Pointer {
                PointerProperties properties;
                PointerCoords coords;
            } pointers[MAX_POINTERS];

            inline size_t size() const {
                return sizeof(Motion) - sizeof(Pointer) * MAX_POINTERS
                        + sizeof(Pointer) * pointerCount;
            }
        } motion;

        struct Finished {
            bool handled;

            inline size_t size() const {
                return sizeof(Finished);
            }
        } finished;
    } body;

    bool isValid(size_t actualSize) const;
    size_t size() const;
};

    /* Gets the number of bytes to add to step to the next SampleData object in a motion
     * event message for a given number of pointers.
/*
 * An input channel consists of a local unix domain socket used to send and receive
 * input messages across processes.  Each channel has a descriptive name for debugging purposes.
 *
 * Each endpoint has its own InputChannel object that specifies its file descriptor.
 *
 * The input channel is closed when all references to it are released.
 */
    static inline size_t sampleDataStride(size_t pointerCount) {
        return sizeof(InputMessage::SampleData) + pointerCount * sizeof(PointerCoords);
    }
class InputChannel : public RefBase {
protected:
    virtual ~InputChannel();

    /* Adds the SampleData stride to the given pointer. */
    static inline SampleData* sampleDataPtrIncrement(SampleData* ptr, size_t stride) {
        return reinterpret_cast<InputMessage::SampleData*>(reinterpret_cast<char*>(ptr) + stride);
    }
public:
    InputChannel(const String8& name, int32_t fd);

    /* Creates a pair of input channels.
     *
     * Returns OK on success.
     */
    static status_t openInputChannelPair(const String8& name,
            sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel);

    inline String8 getName() const { return mName; }
    inline int32_t getFd() const { return mFd; }

    /* Sends a message to the other endpoint.
     *
     * If the channel is full then the message is guaranteed not to have been sent at all.
     * Try again after the consumer has sent a finished signal indicating that it has
     * consumed some of the pending messages from the channel.
     *
     * Returns OK on success.
     * Returns WOULD_BLOCK if the channel is full.
     * Returns DEAD_OBJECT if the channel's peer has been closed.
     * Other errors probably indicate that the channel is broken.
     */
    status_t sendMessage(const InputMessage* msg);

    /* Receives a message sent by the other endpoint.
     *
     * If there is no message present, try again after poll() indicates that the fd
     * is readable.
     *
     * Returns OK on success.
     * Returns WOULD_BLOCK if there is no message present.
     * Returns DEAD_OBJECT if the channel's peer has been closed.
     * Other errors probably indicate that the channel is broken.
     */
    status_t receiveMessage(InputMessage* msg);

private:
    String8 mName;
    int32_t mFd;
};

/*
 * Publishes input events to an anonymous shared memory buffer.
 * Uses atomic operations to coordinate shared access with a single concurrent consumer.
 * Publishes input events to an input channel.
 */
class InputPublisher {
public:
@@ -178,24 +176,12 @@ public:
    /* Gets the underlying input channel. */
    inline sp<InputChannel> getChannel() { return mChannel; }

    /* Prepares the publisher for use.  Must be called before it is used.
     * Returns OK on success.
     *
     * This method implicitly calls reset(). */
    status_t initialize();

    /* Resets the publisher to its initial state and unpins its ashmem buffer.
     * Returns OK on success.
     *
     * Should be called after an event has been consumed to release resources used by the
     * publisher until the next event is ready to be published.
     */
    status_t reset();

    /* Publishes a key event to the ashmem buffer.
    /* Publishes a key event to the input channel.
     *
     * Returns OK on success.
     * Returns INVALID_OPERATION if the publisher has not been reset.
     * 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 publishKeyEvent(
            int32_t deviceId,
@@ -209,11 +195,13 @@ public:
            nsecs_t downTime,
            nsecs_t eventTime);

    /* Publishes a motion event to the ashmem buffer.
    /* Publishes a motion event to the input channel.
     *
     * Returns OK on success.
     * Returns INVALID_OPERATION if the publisher has not been reset.
     * Returns WOULD_BLOCK if the channel is full.
     * Returns DEAD_OBJECT if the channel's peer has been closed.
     * Returns BAD_VALUE if pointerCount is less than 1 or greater than MAX_POINTERS.
     * Other errors probably indicate that the channel is broken.
     */
    status_t publishMotionEvent(
            int32_t deviceId,
@@ -233,55 +221,22 @@ public:
            const PointerProperties* pointerProperties,
            const PointerCoords* pointerCoords);

    /* Appends a motion sample to a motion event unless already consumed.
     *
     * Returns OK on success.
     * Returns INVALID_OPERATION if the current event is not a AMOTION_EVENT_ACTION_MOVE event.
     * Returns FAILED_TRANSACTION if the current event has already been consumed.
     * Returns NO_MEMORY if the buffer is full and no additional samples can be added.
     */
    status_t appendMotionSample(
            nsecs_t eventTime,
            const PointerCoords* pointerCoords);

    /* Sends a dispatch signal to the consumer to inform it that a new message is available.
     *
     * Returns OK on success.
     * Errors probably indicate that the channel is broken.
     */
    status_t sendDispatchSignal();

    /* Receives the finished signal from the consumer in reply to the original dispatch signal.
     * Returns whether the consumer handled the message.
     *
     * Returns OK on success.
     * Returns WOULD_BLOCK if there is no signal present.
     * Returns DEAD_OBJECT if the channel's peer has been closed.
     * Other errors probably indicate that the channel is broken.
     */
    status_t receiveFinishedSignal(bool* outHandled);

private:
    sp<InputChannel> mChannel;

    size_t mAshmemSize;
    InputMessage* mSharedMessage;
    bool mPinned;
    bool mSemaphoreInitialized;
    bool mWasDispatched;

    size_t mMotionEventPointerCount;
    InputMessage::SampleData* mMotionEventSampleDataTail;
    size_t mMotionEventSampleDataStride;

    status_t publishInputEvent(
            int32_t type,
            int32_t deviceId,
            int32_t source);
};

/*
 * Consumes input events from an anonymous shared memory buffer.
 * Uses atomic operations to coordinate shared access with a single concurrent publisher.
 * Consumes input events from an input channel.
 */
class InputConsumer {
public:
@@ -294,16 +249,14 @@ public:
    /* Gets the underlying input channel. */
    inline sp<InputChannel> getChannel() { return mChannel; }

    /* Prepares the consumer for use.  Must be called before it is used. */
    status_t initialize();

    /* Consumes the input event in the buffer and copies its contents into
    /* Consumes an input event from the input channel and copies its contents into
     * an InputEvent object created using the specified factory.
     * This operation will block if the publisher is updating the event.
     *
     * Returns OK on success.
     * Returns INVALID_OPERATION if there is no currently published event.
     * Returns WOULD_BLOCK if there is no event present.
     * Returns DEAD_OBJECT if the channel's peer has been closed.
     * Returns NO_MEMORY if the event could not be created.
     * Other errors probably indicate that the channel is broken.
     */
    status_t consume(InputEventFactoryInterface* factory, InputEvent** outEvent);

@@ -311,26 +264,12 @@ public:
     * finished processing and specifies whether the message was handled by the consumer.
     *
     * Returns OK on success.
     * Errors probably indicate that the channel is broken.
     */
    status_t sendFinishedSignal(bool handled);

    /* Receives the dispatched signal from the publisher.
     *
     * Returns OK on success.
     * Returns WOULD_BLOCK if there is no signal present.
     * Other errors probably indicate that the channel is broken.
     */
    status_t receiveDispatchSignal();
    status_t sendFinishedSignal(bool handled);

private:
    sp<InputChannel> mChannel;

    size_t mAshmemSize;
    InputMessage* mSharedMessage;

    void populateKeyEvent(KeyEvent* keyEvent) const;
    void populateMotionEvent(MotionEvent* motionEvent) const;
};

} // namespace android
+194 −519

File changed.

Preview size limit exceeded, changes collapsed.

+47 −63
Original line number Diff line number Diff line
@@ -20,8 +20,7 @@
#include <gtest/gtest.h>
#include <unistd.h>
#include <time.h>
#include <sys/mman.h>
#include <cutils/ashmem.h>
#include <errno.h>

#include "../../utils/tests/TestHelpers.h"

@@ -36,35 +35,24 @@ protected:

TEST_F(InputChannelTest, ConstructorAndDestructor_TakesOwnershipOfFileDescriptors) {
    // Our purpose here is to verify that the input channel destructor closes the
    // file descriptors provided to it.  One easy way is to provide it with one end
    // file descriptor provided to it.  One easy way is to provide it with one end
    // of a pipe and to check for EPIPE on the other end after the channel is destroyed.
    Pipe fakeAshmem, sendPipe, receivePipe;
    Pipe pipe;

    sp<InputChannel> inputChannel = new InputChannel(String8("channel name"),
            fakeAshmem.sendFd, receivePipe.receiveFd, sendPipe.sendFd);
    sp<InputChannel> inputChannel = new InputChannel(String8("channel name"), pipe.sendFd);

    EXPECT_STREQ("channel name", inputChannel->getName().string())
            << "channel should have provided name";
    EXPECT_EQ(fakeAshmem.sendFd, inputChannel->getAshmemFd())
            << "channel should have provided ashmem fd";
    EXPECT_EQ(receivePipe.receiveFd, inputChannel->getReceivePipeFd())
            << "channel should have provided receive pipe fd";
    EXPECT_EQ(sendPipe.sendFd, inputChannel->getSendPipeFd())
            << "channel should have provided send pipe fd";
    EXPECT_EQ(pipe.sendFd, inputChannel->getFd())
            << "channel should have provided fd";

    inputChannel.clear(); // destroys input channel

    EXPECT_EQ(-EPIPE, fakeAshmem.readSignal())
            << "channel should have closed ashmem fd when destroyed";
    EXPECT_EQ(-EPIPE, receivePipe.writeSignal())
            << "channel should have closed receive pipe fd when destroyed";
    EXPECT_EQ(-EPIPE, sendPipe.readSignal())
            << "channel should have closed send pipe fd when destroyed";
    EXPECT_EQ(-EPIPE, pipe.readSignal())
            << "channel should have closed fd when destroyed";

    // clean up fds of Pipe endpoints that were closed so we don't try to close them again
    fakeAshmem.sendFd = -1;
    receivePipe.receiveFd = -1;
    sendPipe.sendFd = -1;
    pipe.sendFd = -1;
}

TEST_F(InputChannelTest, OpenInputChannelPair_ReturnsAPairOfConnectedChannels) {
@@ -82,43 +70,37 @@ TEST_F(InputChannelTest, OpenInputChannelPair_ReturnsAPairOfConnectedChannels) {
    EXPECT_STREQ("channel name (client)", clientChannel->getName().string())
            << "client channel should have suffixed name";

    // Ashmem uniqueness
    EXPECT_NE(serverChannel->getAshmemFd(), clientChannel->getAshmemFd())
            << "server and client channel should have different ashmem fds because it was dup'd";

    // Ashmem usability
    ssize_t serverAshmemSize = ashmem_get_size_region(serverChannel->getAshmemFd());
    ssize_t clientAshmemSize = ashmem_get_size_region(clientChannel->getAshmemFd());
    uint32_t* serverAshmem = static_cast<uint32_t*>(mmap(NULL, serverAshmemSize,
            PROT_READ | PROT_WRITE, MAP_SHARED, serverChannel->getAshmemFd(), 0));
    uint32_t* clientAshmem = static_cast<uint32_t*>(mmap(NULL, clientAshmemSize,
            PROT_READ | PROT_WRITE, MAP_SHARED, clientChannel->getAshmemFd(), 0));
    ASSERT_TRUE(serverAshmem != NULL)
            << "server channel ashmem should be mappable";
    ASSERT_TRUE(clientAshmem != NULL)
            << "client channel ashmem should be mappable";
    *serverAshmem = 0xf00dd00d;
    EXPECT_EQ(0xf00dd00d, *clientAshmem)
            << "ashmem buffer should be shared by client and server";
    munmap(serverAshmem, serverAshmemSize);
    munmap(clientAshmem, clientAshmemSize);

    // Server->Client communication
    EXPECT_EQ(OK, serverChannel->sendSignal('S'))
            << "server channel should be able to send signal to client channel";
    char signal;
    EXPECT_EQ(OK, clientChannel->receiveSignal(& signal))
            << "client channel should be able to receive signal from server channel";
    EXPECT_EQ('S', signal)
            << "client channel should receive the correct signal from server channel";
    InputMessage serverMsg;
    memset(&serverMsg, 0, sizeof(InputMessage));
    serverMsg.header.type = InputMessage::TYPE_KEY;
    serverMsg.body.key.action = AKEY_EVENT_ACTION_DOWN;
    EXPECT_EQ(OK, serverChannel->sendMessage(&serverMsg))
            << "server channel should be able to send message to client channel";

    InputMessage clientMsg;
    EXPECT_EQ(OK, clientChannel->receiveMessage(&clientMsg))
            << "client channel should be able to receive message from server channel";
    EXPECT_EQ(serverMsg.header.type, clientMsg.header.type)
            << "client channel should receive the correct message from server channel";
    EXPECT_EQ(serverMsg.body.key.action, clientMsg.body.key.action)
            << "client channel should receive the correct message from server channel";

    // Client->Server communication
    EXPECT_EQ(OK, clientChannel->sendSignal('c'))
            << "client channel should be able to send signal to server channel";
    EXPECT_EQ(OK, serverChannel->receiveSignal(& signal))
            << "server channel should be able to receive signal from client channel";
    EXPECT_EQ('c', signal)
            << "server channel should receive the correct signal from client channel";
    InputMessage clientReply;
    memset(&clientReply, 0, sizeof(InputMessage));
    clientReply.header.type = InputMessage::TYPE_FINISHED;
    clientReply.body.finished.handled = true;
    EXPECT_EQ(OK, clientChannel->sendMessage(&clientReply))
            << "client channel should be able to send message to server channel";

    InputMessage serverReply;
    EXPECT_EQ(OK, serverChannel->receiveMessage(&serverReply))
            << "server channel should be able to receive message from client channel";
    EXPECT_EQ(clientReply.header.type, serverReply.header.type)
            << "server channel should receive the correct message from client channel";
    EXPECT_EQ(clientReply.body.finished.handled, serverReply.body.finished.handled)
            << "server channel should receive the correct message from client channel";
}

TEST_F(InputChannelTest, ReceiveSignal_WhenNoSignalPresent_ReturnsAnError) {
@@ -130,9 +112,9 @@ TEST_F(InputChannelTest, ReceiveSignal_WhenNoSignalPresent_ReturnsAnError) {
    ASSERT_EQ(OK, result)
            << "should have successfully opened a channel pair";

    char signal;
    EXPECT_EQ(WOULD_BLOCK, clientChannel->receiveSignal(& signal))
            << "receiveSignal should have returned WOULD_BLOCK";
    InputMessage msg;
    EXPECT_EQ(WOULD_BLOCK, clientChannel->receiveMessage(&msg))
            << "receiveMessage should have returned WOULD_BLOCK";
}

TEST_F(InputChannelTest, ReceiveSignal_WhenPeerClosed_ReturnsAnError) {
@@ -146,9 +128,9 @@ TEST_F(InputChannelTest, ReceiveSignal_WhenPeerClosed_ReturnsAnError) {

    serverChannel.clear(); // close server channel

    char signal;
    EXPECT_EQ(DEAD_OBJECT, clientChannel->receiveSignal(& signal))
            << "receiveSignal should have returned DEAD_OBJECT";
    InputMessage msg;
    EXPECT_EQ(DEAD_OBJECT, clientChannel->receiveMessage(&msg))
            << "receiveMessage should have returned DEAD_OBJECT";
}

TEST_F(InputChannelTest, SendSignal_WhenPeerClosed_ReturnsAnError) {
@@ -162,8 +144,10 @@ TEST_F(InputChannelTest, SendSignal_WhenPeerClosed_ReturnsAnError) {

    serverChannel.clear(); // close server channel

    EXPECT_EQ(DEAD_OBJECT, clientChannel->sendSignal('S'))
            << "sendSignal should have returned DEAD_OBJECT";
    InputMessage msg;
    msg.header.type = InputMessage::TYPE_KEY;
    EXPECT_EQ(DEAD_OBJECT, clientChannel->sendMessage(&msg))
            << "sendMessage should have returned DEAD_OBJECT";
}


+29 −285

File changed.

Preview size limit exceeded, changes collapsed.