am cbee6d6e: Rewrite input transport using sockets.
* commit 'cbee6d6ede0499fb4a2c00bfc00d5db8d9ed5139': Rewrite input transport using sockets.
This commit is contained in:
@@ -130,21 +130,21 @@ AInputQueue::~AInputQueue() {
|
||||
void AInputQueue::attachLooper(ALooper* looper, int ident,
|
||||
ALooper_callbackFunc callback, void* data) {
|
||||
mLooper = static_cast<android::Looper*>(looper);
|
||||
mLooper->addFd(mConsumer.getChannel()->getReceivePipeFd(),
|
||||
mLooper->addFd(mConsumer.getChannel()->getFd(),
|
||||
ident, ALOOPER_EVENT_INPUT, callback, data);
|
||||
mLooper->addFd(mDispatchKeyRead,
|
||||
ident, ALOOPER_EVENT_INPUT, callback, data);
|
||||
}
|
||||
|
||||
void AInputQueue::detachLooper() {
|
||||
mLooper->removeFd(mConsumer.getChannel()->getReceivePipeFd());
|
||||
mLooper->removeFd(mConsumer.getChannel()->getFd());
|
||||
mLooper->removeFd(mDispatchKeyRead);
|
||||
}
|
||||
|
||||
int32_t AInputQueue::hasEvents() {
|
||||
struct pollfd pfd[2];
|
||||
|
||||
pfd[0].fd = mConsumer.getChannel()->getReceivePipeFd();
|
||||
pfd[0].fd = mConsumer.getChannel()->getFd();
|
||||
pfd[0].events = POLLIN;
|
||||
pfd[0].revents = 0;
|
||||
pfd[1].fd = mDispatchKeyRead;
|
||||
@@ -200,16 +200,9 @@ int32_t AInputQueue::getEvent(AInputEvent** outEvent) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t res = mConsumer.receiveDispatchSignal();
|
||||
if (res != android::OK) {
|
||||
ALOGE("channel '%s' ~ Failed to receive dispatch signal. status=%d",
|
||||
mConsumer.getChannel()->getName().string(), res);
|
||||
return -1;
|
||||
}
|
||||
|
||||
InputEvent* myEvent = NULL;
|
||||
res = mConsumer.consume(this, &myEvent);
|
||||
status_t res = mConsumer.consume(this, &myEvent);
|
||||
if (res != android::OK) {
|
||||
ALOGW("channel '%s' ~ Failed to consume input event. status=%d",
|
||||
mConsumer.getChannel()->getName().string(), res);
|
||||
@@ -481,11 +474,6 @@ struct NativeCode : public ANativeActivity {
|
||||
android_view_InputChannel_getInputChannel(env, _channel);
|
||||
if (ic != NULL) {
|
||||
nativeInputQueue = new AInputQueue(ic, mainWorkWrite);
|
||||
if (nativeInputQueue->getConsumer().initialize() != android::OK) {
|
||||
delete nativeInputQueue;
|
||||
nativeInputQueue = NULL;
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
} else {
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
@@ -199,32 +199,16 @@ static void android_view_InputChannel_nativeReadFromParcel(JNIEnv* env, jobject
|
||||
bool isInitialized = parcel->readInt32();
|
||||
if (isInitialized) {
|
||||
String8 name = parcel->readString8();
|
||||
int32_t parcelAshmemFd = parcel->readFileDescriptor();
|
||||
int32_t ashmemFd = dup(parcelAshmemFd);
|
||||
if (ashmemFd < 0) {
|
||||
ALOGE("Error %d dup ashmem fd %d.", errno, parcelAshmemFd);
|
||||
}
|
||||
int32_t parcelReceivePipeFd = parcel->readFileDescriptor();
|
||||
int32_t receivePipeFd = dup(parcelReceivePipeFd);
|
||||
if (receivePipeFd < 0) {
|
||||
ALOGE("Error %d dup receive pipe fd %d.", errno, parcelReceivePipeFd);
|
||||
}
|
||||
int32_t parcelSendPipeFd = parcel->readFileDescriptor();
|
||||
int32_t sendPipeFd = dup(parcelSendPipeFd);
|
||||
if (sendPipeFd < 0) {
|
||||
ALOGE("Error %d dup send pipe fd %d.", errno, parcelSendPipeFd);
|
||||
}
|
||||
if (ashmemFd < 0 || receivePipeFd < 0 || sendPipeFd < 0) {
|
||||
if (ashmemFd >= 0) ::close(ashmemFd);
|
||||
if (receivePipeFd >= 0) ::close(receivePipeFd);
|
||||
if (sendPipeFd >= 0) ::close(sendPipeFd);
|
||||
int32_t rawFd = parcel->readFileDescriptor();
|
||||
int32_t dupFd = dup(rawFd);
|
||||
if (rawFd < 0) {
|
||||
ALOGE("Error %d dup channel fd %d.", errno, rawFd);
|
||||
jniThrowRuntimeException(env,
|
||||
"Could not read input channel file descriptors from parcel.");
|
||||
return;
|
||||
}
|
||||
|
||||
InputChannel* inputChannel = new InputChannel(name, ashmemFd,
|
||||
receivePipeFd, sendPipeFd);
|
||||
InputChannel* inputChannel = new InputChannel(name, dupFd);
|
||||
NativeInputChannel* nativeInputChannel = new NativeInputChannel(inputChannel);
|
||||
|
||||
android_view_InputChannel_setNativeInputChannel(env, obj, nativeInputChannel);
|
||||
@@ -243,9 +227,7 @@ static void android_view_InputChannel_nativeWriteToParcel(JNIEnv* env, jobject o
|
||||
|
||||
parcel->writeInt32(1);
|
||||
parcel->writeString8(inputChannel->getName());
|
||||
parcel->writeDupFileDescriptor(inputChannel->getAshmemFd());
|
||||
parcel->writeDupFileDescriptor(inputChannel->getReceivePipeFd());
|
||||
parcel->writeDupFileDescriptor(inputChannel->getSendPipeFd());
|
||||
parcel->writeDupFileDescriptor(inputChannel->getFd());
|
||||
} else {
|
||||
parcel->writeInt32(0);
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ NativeInputEventReceiver::~NativeInputEventReceiver() {
|
||||
ALOGD("channel '%s' ~ Disposing input event receiver.", getInputChannelName());
|
||||
#endif
|
||||
|
||||
mLooper->removeFd(mInputConsumer.getChannel()->getReceivePipeFd());
|
||||
mLooper->removeFd(mInputConsumer.getChannel()->getFd());
|
||||
if (mEventInProgress) {
|
||||
mInputConsumer.sendFinishedSignal(false); // ignoring result
|
||||
}
|
||||
@@ -93,14 +93,7 @@ NativeInputEventReceiver::~NativeInputEventReceiver() {
|
||||
}
|
||||
|
||||
status_t NativeInputEventReceiver::initialize() {
|
||||
status_t result = mInputConsumer.initialize();
|
||||
if (result) {
|
||||
ALOGW("Failed to initialize input consumer for input channel '%s', status=%d",
|
||||
getInputChannelName(), result);
|
||||
return result;
|
||||
}
|
||||
|
||||
int32_t receiveFd = mInputConsumer.getChannel()->getReceivePipeFd();
|
||||
int32_t receiveFd = mInputConsumer.getChannel()->getFd();
|
||||
mLooper->addFd(receiveFd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
|
||||
return OK;
|
||||
}
|
||||
@@ -139,13 +132,6 @@ int NativeInputEventReceiver::handleReceiveCallback(int receiveFd, int events, v
|
||||
return 1;
|
||||
}
|
||||
|
||||
status_t status = r->mInputConsumer.receiveDispatchSignal();
|
||||
if (status) {
|
||||
ALOGE("channel '%s' ~ Failed to receive dispatch signal. status=%d",
|
||||
r->getInputChannelName(), status);
|
||||
return 0; // remove the callback
|
||||
}
|
||||
|
||||
if (r->mEventInProgress) {
|
||||
ALOGW("channel '%s' ~ Publisher sent spurious dispatch signal.",
|
||||
r->getInputChannelName());
|
||||
@@ -153,7 +139,7 @@ int NativeInputEventReceiver::handleReceiveCallback(int receiveFd, int events, v
|
||||
}
|
||||
|
||||
InputEvent* inputEvent;
|
||||
status = r->mInputConsumer.consume(&r->mInputEventFactory, &inputEvent);
|
||||
status_t status = r->mInputConsumer.consume(&r->mInputEventFactory, &inputEvent);
|
||||
if (status) {
|
||||
ALOGW("channel '%s' ~ Failed to consume input event. status=%d",
|
||||
r->getInputChannelName(), status);
|
||||
|
||||
@@ -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;
|
||||
|
||||
int32_t type;
|
||||
|
||||
struct SampleData {
|
||||
nsecs_t eventTime;
|
||||
PointerCoords coords[0]; // variable length
|
||||
enum {
|
||||
TYPE_KEY = 1,
|
||||
TYPE_MOTION = 2,
|
||||
TYPE_FINISHED = 3,
|
||||
};
|
||||
|
||||
int32_t deviceId;
|
||||
int32_t source;
|
||||
struct Header {
|
||||
uint32_t type;
|
||||
uint32_t padding; // 8 byte alignment for the body that follows
|
||||
} header;
|
||||
|
||||
union {
|
||||
struct {
|
||||
union Body {
|
||||
struct Key {
|
||||
nsecs_t eventTime;
|
||||
int32_t deviceId;
|
||||
int32_t source;
|
||||
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;
|
||||
};
|
||||
|
||||
/* 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.
|
||||
*/
|
||||
static inline size_t sampleDataStride(size_t pointerCount) {
|
||||
return sizeof(InputMessage::SampleData) + pointerCount * sizeof(PointerCoords);
|
||||
}
|
||||
struct Finished {
|
||||
bool handled;
|
||||
|
||||
/* 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);
|
||||
}
|
||||
inline size_t size() const {
|
||||
return sizeof(Finished);
|
||||
}
|
||||
} finished;
|
||||
} body;
|
||||
|
||||
bool isValid(size_t actualSize) const;
|
||||
size_t size() const;
|
||||
};
|
||||
|
||||
/*
|
||||
* Publishes input events to an anonymous shared memory buffer.
|
||||
* Uses atomic operations to coordinate shared access with a single concurrent consumer.
|
||||
* 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.
|
||||
*/
|
||||
class InputChannel : public RefBase {
|
||||
protected:
|
||||
virtual ~InputChannel();
|
||||
|
||||
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 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.
|
||||
* Other 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();
|
||||
|
||||
private:
|
||||
sp<InputChannel> mChannel;
|
||||
|
||||
size_t mAshmemSize;
|
||||
InputMessage* mSharedMessage;
|
||||
|
||||
void populateKeyEvent(KeyEvent* keyEvent) const;
|
||||
void populateMotionEvent(MotionEvent* motionEvent) const;
|
||||
};
|
||||
|
||||
} // namespace android
|
||||
|
||||
@@ -7,322 +7,186 @@
|
||||
|
||||
//#define LOG_NDEBUG 0
|
||||
|
||||
// Log debug messages about channel signalling (send signal, receive signal)
|
||||
#define DEBUG_CHANNEL_SIGNALS 0
|
||||
// Log debug messages about channel messages (send message, receive message)
|
||||
#define DEBUG_CHANNEL_MESSAGES 0
|
||||
|
||||
// Log debug messages whenever InputChannel objects are created/destroyed
|
||||
#define DEBUG_CHANNEL_LIFECYCLE 0
|
||||
|
||||
// Log debug messages about transport actions (initialize, reset, publish, ...)
|
||||
#define DEBUG_TRANSPORT_ACTIONS 0
|
||||
// Log debug messages about transport actions
|
||||
|
||||
|
||||
#include <cutils/ashmem.h>
|
||||
#include <cutils/log.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <ui/InputTransport.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
|
||||
namespace android {
|
||||
|
||||
#define ROUND_UP(value, boundary) (((value) + (boundary) - 1) & ~((boundary) - 1))
|
||||
#define MIN_HISTORY_DEPTH 20
|
||||
// --- InputMessage ---
|
||||
|
||||
// Must be at least sizeof(InputMessage) + sufficient space for pointer data
|
||||
static const int DEFAULT_MESSAGE_BUFFER_SIZE = ROUND_UP(
|
||||
sizeof(InputMessage) + MIN_HISTORY_DEPTH
|
||||
* (sizeof(InputMessage::SampleData) + MAX_POINTERS * sizeof(PointerCoords)),
|
||||
4096);
|
||||
bool InputMessage::isValid(size_t actualSize) const {
|
||||
if (size() == actualSize) {
|
||||
switch (header.type) {
|
||||
case TYPE_KEY:
|
||||
return true;
|
||||
case TYPE_MOTION:
|
||||
return body.motion.pointerCount > 0
|
||||
&& body.motion.pointerCount <= MAX_POINTERS;
|
||||
case TYPE_FINISHED:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Signal sent by the producer to the consumer to inform it that a new message is
|
||||
// available to be consumed in the shared memory buffer.
|
||||
static const char INPUT_SIGNAL_DISPATCH = 'D';
|
||||
|
||||
// Signal sent by the consumer to the producer to inform it that it has finished
|
||||
// consuming the most recent message and it handled it.
|
||||
static const char INPUT_SIGNAL_FINISHED_HANDLED = 'f';
|
||||
|
||||
// Signal sent by the consumer to the producer to inform it that it has finished
|
||||
// consuming the most recent message but it did not handle it.
|
||||
static const char INPUT_SIGNAL_FINISHED_UNHANDLED = 'u';
|
||||
size_t InputMessage::size() const {
|
||||
switch (header.type) {
|
||||
case TYPE_KEY:
|
||||
return sizeof(Header) + body.key.size();
|
||||
case TYPE_MOTION:
|
||||
return sizeof(Header) + body.motion.size();
|
||||
case TYPE_FINISHED:
|
||||
return sizeof(Header) + body.finished.size();
|
||||
}
|
||||
return sizeof(Header);
|
||||
}
|
||||
|
||||
|
||||
// --- InputChannel ---
|
||||
|
||||
InputChannel::InputChannel(const String8& name, int32_t ashmemFd, int32_t receivePipeFd,
|
||||
int32_t sendPipeFd) :
|
||||
mName(name), mAshmemFd(ashmemFd), mReceivePipeFd(receivePipeFd), mSendPipeFd(sendPipeFd) {
|
||||
InputChannel::InputChannel(const String8& name, int fd) :
|
||||
mName(name), mFd(fd) {
|
||||
#if DEBUG_CHANNEL_LIFECYCLE
|
||||
ALOGD("Input channel constructed: name='%s', ashmemFd=%d, receivePipeFd=%d, sendPipeFd=%d",
|
||||
mName.string(), ashmemFd, receivePipeFd, sendPipeFd);
|
||||
ALOGD("Input channel constructed: name='%s', fd=%d",
|
||||
mName.string(), fd);
|
||||
#endif
|
||||
|
||||
int result = fcntl(mReceivePipeFd, F_SETFL, O_NONBLOCK);
|
||||
LOG_ALWAYS_FATAL_IF(result != 0, "channel '%s' ~ Could not make receive pipe "
|
||||
"non-blocking. errno=%d", mName.string(), errno);
|
||||
|
||||
result = fcntl(mSendPipeFd, F_SETFL, O_NONBLOCK);
|
||||
LOG_ALWAYS_FATAL_IF(result != 0, "channel '%s' ~ Could not make send pipe "
|
||||
int result = fcntl(mFd, F_SETFL, O_NONBLOCK);
|
||||
LOG_ALWAYS_FATAL_IF(result != 0, "channel '%s' ~ Could not make socket "
|
||||
"non-blocking. errno=%d", mName.string(), errno);
|
||||
}
|
||||
|
||||
InputChannel::~InputChannel() {
|
||||
#if DEBUG_CHANNEL_LIFECYCLE
|
||||
ALOGD("Input channel destroyed: name='%s', ashmemFd=%d, receivePipeFd=%d, sendPipeFd=%d",
|
||||
mName.string(), mAshmemFd, mReceivePipeFd, mSendPipeFd);
|
||||
ALOGD("Input channel destroyed: name='%s', fd=%d",
|
||||
mName.string(), mFd);
|
||||
#endif
|
||||
|
||||
::close(mAshmemFd);
|
||||
::close(mReceivePipeFd);
|
||||
::close(mSendPipeFd);
|
||||
::close(mFd);
|
||||
}
|
||||
|
||||
status_t InputChannel::openInputChannelPair(const String8& name,
|
||||
sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) {
|
||||
status_t result;
|
||||
|
||||
String8 ashmemName("InputChannel ");
|
||||
ashmemName.append(name);
|
||||
int serverAshmemFd = ashmem_create_region(ashmemName.string(), DEFAULT_MESSAGE_BUFFER_SIZE);
|
||||
if (serverAshmemFd < 0) {
|
||||
result = -errno;
|
||||
ALOGE("channel '%s' ~ Could not create shared memory region. errno=%d",
|
||||
int sockets[2];
|
||||
if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) {
|
||||
status_t result = -errno;
|
||||
ALOGE("channel '%s' ~ Could not create socket pair. errno=%d",
|
||||
name.string(), errno);
|
||||
} else {
|
||||
result = ashmem_set_prot_region(serverAshmemFd, PROT_READ | PROT_WRITE);
|
||||
if (result < 0) {
|
||||
ALOGE("channel '%s' ~ Error %d trying to set protection of ashmem fd %d.",
|
||||
name.string(), result, serverAshmemFd);
|
||||
} else {
|
||||
// Dup the file descriptor because the server and client input channel objects that
|
||||
// are returned may have different lifetimes but they share the same shared memory region.
|
||||
int clientAshmemFd;
|
||||
clientAshmemFd = dup(serverAshmemFd);
|
||||
if (clientAshmemFd < 0) {
|
||||
result = -errno;
|
||||
ALOGE("channel '%s' ~ Could not dup() shared memory region fd. errno=%d",
|
||||
name.string(), errno);
|
||||
} else {
|
||||
int forward[2];
|
||||
if (pipe(forward)) {
|
||||
result = -errno;
|
||||
ALOGE("channel '%s' ~ Could not create forward pipe. errno=%d",
|
||||
name.string(), errno);
|
||||
} else {
|
||||
int reverse[2];
|
||||
if (pipe(reverse)) {
|
||||
result = -errno;
|
||||
ALOGE("channel '%s' ~ Could not create reverse pipe. errno=%d",
|
||||
name.string(), errno);
|
||||
} else {
|
||||
String8 serverChannelName = name;
|
||||
serverChannelName.append(" (server)");
|
||||
outServerChannel = new InputChannel(serverChannelName,
|
||||
serverAshmemFd, reverse[0], forward[1]);
|
||||
|
||||
String8 clientChannelName = name;
|
||||
clientChannelName.append(" (client)");
|
||||
outClientChannel = new InputChannel(clientChannelName,
|
||||
clientAshmemFd, forward[0], reverse[1]);
|
||||
return OK;
|
||||
}
|
||||
::close(forward[0]);
|
||||
::close(forward[1]);
|
||||
}
|
||||
::close(clientAshmemFd);
|
||||
}
|
||||
}
|
||||
::close(serverAshmemFd);
|
||||
outServerChannel.clear();
|
||||
outClientChannel.clear();
|
||||
return result;
|
||||
}
|
||||
|
||||
outServerChannel.clear();
|
||||
outClientChannel.clear();
|
||||
return result;
|
||||
String8 serverChannelName = name;
|
||||
serverChannelName.append(" (server)");
|
||||
outServerChannel = new InputChannel(serverChannelName, sockets[0]);
|
||||
|
||||
String8 clientChannelName = name;
|
||||
clientChannelName.append(" (client)");
|
||||
outClientChannel = new InputChannel(clientChannelName, sockets[1]);
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t InputChannel::sendSignal(char signal) {
|
||||
status_t InputChannel::sendMessage(const InputMessage* msg) {
|
||||
size_t msgLength = msg->size();
|
||||
ssize_t nWrite;
|
||||
do {
|
||||
nWrite = ::write(mSendPipeFd, & signal, 1);
|
||||
nWrite = ::send(mFd, msg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);
|
||||
} while (nWrite == -1 && errno == EINTR);
|
||||
|
||||
if (nWrite == 1) {
|
||||
#if DEBUG_CHANNEL_SIGNALS
|
||||
ALOGD("channel '%s' ~ sent signal '%c'", mName.string(), signal);
|
||||
if (nWrite < 0) {
|
||||
int error = errno;
|
||||
#if DEBUG_CHANNEL_MESSAGES
|
||||
ALOGD("channel '%s' ~ error sending message of type %d, errno=%d", mName.string(),
|
||||
msg->header.type, error);
|
||||
#endif
|
||||
return OK;
|
||||
if (error == EAGAIN || error == EWOULDBLOCK) {
|
||||
return WOULD_BLOCK;
|
||||
}
|
||||
if (error == EPIPE || error == ENOTCONN) {
|
||||
return DEAD_OBJECT;
|
||||
}
|
||||
return -error;
|
||||
}
|
||||
|
||||
#if DEBUG_CHANNEL_SIGNALS
|
||||
ALOGD("channel '%s' ~ error sending signal '%c', errno=%d", mName.string(), signal, errno);
|
||||
#endif
|
||||
return -errno;
|
||||
}
|
||||
|
||||
status_t InputChannel::receiveSignal(char* outSignal) {
|
||||
ssize_t nRead;
|
||||
do {
|
||||
nRead = ::read(mReceivePipeFd, outSignal, 1);
|
||||
} while (nRead == -1 && errno == EINTR);
|
||||
|
||||
if (nRead == 1) {
|
||||
#if DEBUG_CHANNEL_SIGNALS
|
||||
ALOGD("channel '%s' ~ received signal '%c'", mName.string(), *outSignal);
|
||||
#endif
|
||||
return OK;
|
||||
}
|
||||
|
||||
if (nRead == 0) { // check for EOF
|
||||
#if DEBUG_CHANNEL_SIGNALS
|
||||
ALOGD("channel '%s' ~ receive signal failed because peer was closed", mName.string());
|
||||
if (size_t(nWrite) != msgLength) {
|
||||
#if DEBUG_CHANNEL_MESSAGES
|
||||
ALOGD("channel '%s' ~ error sending message type %d, send was incomplete",
|
||||
mName.string(), msg->header.type);
|
||||
#endif
|
||||
return DEAD_OBJECT;
|
||||
}
|
||||
|
||||
if (errno == EAGAIN) {
|
||||
#if DEBUG_CHANNEL_SIGNALS
|
||||
ALOGD("channel '%s' ~ receive signal failed because no signal available", mName.string());
|
||||
#if DEBUG_CHANNEL_MESSAGES
|
||||
ALOGD("channel '%s' ~ sent message of type %d", mName.string(), msg->header.type);
|
||||
#endif
|
||||
return WOULD_BLOCK;
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t InputChannel::receiveMessage(InputMessage* msg) {
|
||||
ssize_t nRead;
|
||||
do {
|
||||
nRead = ::recv(mFd, msg, sizeof(InputMessage), MSG_DONTWAIT);
|
||||
} while (nRead == -1 && errno == EINTR);
|
||||
|
||||
if (nRead < 0) {
|
||||
int error = errno;
|
||||
#if DEBUG_CHANNEL_MESSAGES
|
||||
ALOGD("channel '%s' ~ receive message failed, errno=%d", mName.string(), errno);
|
||||
#endif
|
||||
if (error == EAGAIN || error == EWOULDBLOCK) {
|
||||
return WOULD_BLOCK;
|
||||
}
|
||||
if (error == EPIPE || error == ENOTCONN) {
|
||||
return DEAD_OBJECT;
|
||||
}
|
||||
return -error;
|
||||
}
|
||||
|
||||
#if DEBUG_CHANNEL_SIGNALS
|
||||
ALOGD("channel '%s' ~ receive signal failed, errno=%d", mName.string(), errno);
|
||||
if (nRead == 0) { // check for EOF
|
||||
#if DEBUG_CHANNEL_MESSAGES
|
||||
ALOGD("channel '%s' ~ receive message failed because peer was closed", mName.string());
|
||||
#endif
|
||||
return -errno;
|
||||
return DEAD_OBJECT;
|
||||
}
|
||||
|
||||
if (!msg->isValid(nRead)) {
|
||||
#if DEBUG_CHANNEL_MESSAGES
|
||||
ALOGD("channel '%s' ~ received invalid message", mName.string());
|
||||
#endif
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
#if DEBUG_CHANNEL_MESSAGES
|
||||
ALOGD("channel '%s' ~ received message of type %d", mName.string(), msg->header.type);
|
||||
#endif
|
||||
return OK;
|
||||
}
|
||||
|
||||
|
||||
// --- InputPublisher ---
|
||||
|
||||
InputPublisher::InputPublisher(const sp<InputChannel>& channel) :
|
||||
mChannel(channel), mSharedMessage(NULL),
|
||||
mPinned(false), mSemaphoreInitialized(false), mWasDispatched(false),
|
||||
mMotionEventSampleDataTail(NULL) {
|
||||
mChannel(channel) {
|
||||
}
|
||||
|
||||
InputPublisher::~InputPublisher() {
|
||||
reset();
|
||||
|
||||
if (mSharedMessage) {
|
||||
munmap(mSharedMessage, mAshmemSize);
|
||||
}
|
||||
}
|
||||
|
||||
status_t InputPublisher::initialize() {
|
||||
#if DEBUG_TRANSPORT_ACTIONS
|
||||
ALOGD("channel '%s' publisher ~ initialize",
|
||||
mChannel->getName().string());
|
||||
#endif
|
||||
|
||||
int ashmemFd = mChannel->getAshmemFd();
|
||||
int result = ashmem_get_size_region(ashmemFd);
|
||||
if (result < 0) {
|
||||
ALOGE("channel '%s' publisher ~ Error %d getting size of ashmem fd %d.",
|
||||
mChannel->getName().string(), result, ashmemFd);
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
mAshmemSize = (size_t) result;
|
||||
|
||||
mSharedMessage = static_cast<InputMessage*>(mmap(NULL, mAshmemSize,
|
||||
PROT_READ | PROT_WRITE, MAP_SHARED, ashmemFd, 0));
|
||||
if (! mSharedMessage) {
|
||||
ALOGE("channel '%s' publisher ~ mmap failed on ashmem fd %d.",
|
||||
mChannel->getName().string(), ashmemFd);
|
||||
return NO_MEMORY;
|
||||
}
|
||||
|
||||
mPinned = true;
|
||||
mSharedMessage->consumed = false;
|
||||
|
||||
return reset();
|
||||
}
|
||||
|
||||
status_t InputPublisher::reset() {
|
||||
#if DEBUG_TRANSPORT_ACTIONS
|
||||
ALOGD("channel '%s' publisher ~ reset",
|
||||
mChannel->getName().string());
|
||||
#endif
|
||||
|
||||
if (mPinned) {
|
||||
// Destroy the semaphore since we are about to unpin the memory region that contains it.
|
||||
int result;
|
||||
if (mSemaphoreInitialized) {
|
||||
if (mSharedMessage->consumed) {
|
||||
result = sem_post(& mSharedMessage->semaphore);
|
||||
if (result < 0) {
|
||||
ALOGE("channel '%s' publisher ~ Error %d in sem_post.",
|
||||
mChannel->getName().string(), errno);
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
result = sem_destroy(& mSharedMessage->semaphore);
|
||||
if (result < 0) {
|
||||
ALOGE("channel '%s' publisher ~ Error %d in sem_destroy.",
|
||||
mChannel->getName().string(), errno);
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
mSemaphoreInitialized = false;
|
||||
}
|
||||
|
||||
// Unpin the region since we no longer care about its contents.
|
||||
int ashmemFd = mChannel->getAshmemFd();
|
||||
result = ashmem_unpin_region(ashmemFd, 0, 0);
|
||||
if (result < 0) {
|
||||
ALOGE("channel '%s' publisher ~ Error %d unpinning ashmem fd %d.",
|
||||
mChannel->getName().string(), result, ashmemFd);
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
mPinned = false;
|
||||
}
|
||||
|
||||
mMotionEventSampleDataTail = NULL;
|
||||
mWasDispatched = false;
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t InputPublisher::publishInputEvent(
|
||||
int32_t type,
|
||||
int32_t deviceId,
|
||||
int32_t source) {
|
||||
if (mPinned) {
|
||||
ALOGE("channel '%s' publisher ~ Attempted to publish a new event but publisher has "
|
||||
"not yet been reset.", mChannel->getName().string());
|
||||
return INVALID_OPERATION;
|
||||
}
|
||||
|
||||
// Pin the region.
|
||||
// We do not check for ASHMEM_NOT_PURGED because we don't care about the previous
|
||||
// contents of the buffer so it does not matter whether it was purged in the meantime.
|
||||
int ashmemFd = mChannel->getAshmemFd();
|
||||
int result = ashmem_pin_region(ashmemFd, 0, 0);
|
||||
if (result < 0) {
|
||||
ALOGE("channel '%s' publisher ~ Error %d pinning ashmem fd %d.",
|
||||
mChannel->getName().string(), result, ashmemFd);
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
mPinned = true;
|
||||
|
||||
result = sem_init(& mSharedMessage->semaphore, 1, 1);
|
||||
if (result < 0) {
|
||||
ALOGE("channel '%s' publisher ~ Error %d in sem_init.",
|
||||
mChannel->getName().string(), errno);
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
mSemaphoreInitialized = true;
|
||||
|
||||
mSharedMessage->consumed = false;
|
||||
mSharedMessage->type = type;
|
||||
mSharedMessage->deviceId = deviceId;
|
||||
mSharedMessage->source = source;
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t InputPublisher::publishKeyEvent(
|
||||
@@ -345,20 +209,19 @@ status_t InputPublisher::publishKeyEvent(
|
||||
downTime, eventTime);
|
||||
#endif
|
||||
|
||||
status_t result = publishInputEvent(AINPUT_EVENT_TYPE_KEY, deviceId, source);
|
||||
if (result < 0) {
|
||||
return result;
|
||||
}
|
||||
|
||||
mSharedMessage->key.action = action;
|
||||
mSharedMessage->key.flags = flags;
|
||||
mSharedMessage->key.keyCode = keyCode;
|
||||
mSharedMessage->key.scanCode = scanCode;
|
||||
mSharedMessage->key.metaState = metaState;
|
||||
mSharedMessage->key.repeatCount = repeatCount;
|
||||
mSharedMessage->key.downTime = downTime;
|
||||
mSharedMessage->key.eventTime = eventTime;
|
||||
return OK;
|
||||
InputMessage msg;
|
||||
msg.header.type = InputMessage::TYPE_KEY;
|
||||
msg.body.key.deviceId = deviceId;
|
||||
msg.body.key.source = source;
|
||||
msg.body.key.action = action;
|
||||
msg.body.key.flags = flags;
|
||||
msg.body.key.keyCode = keyCode;
|
||||
msg.body.key.scanCode = scanCode;
|
||||
msg.body.key.metaState = metaState;
|
||||
msg.body.key.repeatCount = repeatCount;
|
||||
msg.body.key.downTime = downTime;
|
||||
msg.body.key.eventTime = eventTime;
|
||||
return mChannel->sendMessage(&msg);
|
||||
}
|
||||
|
||||
status_t InputPublisher::publishMotionEvent(
|
||||
@@ -395,123 +258,27 @@ status_t InputPublisher::publishMotionEvent(
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
status_t result = publishInputEvent(AINPUT_EVENT_TYPE_MOTION, deviceId, source);
|
||||
if (result < 0) {
|
||||
return result;
|
||||
}
|
||||
|
||||
mSharedMessage->motion.action = action;
|
||||
mSharedMessage->motion.flags = flags;
|
||||
mSharedMessage->motion.edgeFlags = edgeFlags;
|
||||
mSharedMessage->motion.metaState = metaState;
|
||||
mSharedMessage->motion.buttonState = buttonState;
|
||||
mSharedMessage->motion.xOffset = xOffset;
|
||||
mSharedMessage->motion.yOffset = yOffset;
|
||||
mSharedMessage->motion.xPrecision = xPrecision;
|
||||
mSharedMessage->motion.yPrecision = yPrecision;
|
||||
mSharedMessage->motion.downTime = downTime;
|
||||
mSharedMessage->motion.pointerCount = pointerCount;
|
||||
|
||||
mSharedMessage->motion.sampleCount = 1;
|
||||
mSharedMessage->motion.sampleData[0].eventTime = eventTime;
|
||||
|
||||
InputMessage msg;
|
||||
msg.header.type = InputMessage::TYPE_MOTION;
|
||||
msg.body.motion.deviceId = deviceId;
|
||||
msg.body.motion.source = source;
|
||||
msg.body.motion.action = action;
|
||||
msg.body.motion.flags = flags;
|
||||
msg.body.motion.edgeFlags = edgeFlags;
|
||||
msg.body.motion.metaState = metaState;
|
||||
msg.body.motion.buttonState = buttonState;
|
||||
msg.body.motion.xOffset = xOffset;
|
||||
msg.body.motion.yOffset = yOffset;
|
||||
msg.body.motion.xPrecision = xPrecision;
|
||||
msg.body.motion.yPrecision = yPrecision;
|
||||
msg.body.motion.downTime = downTime;
|
||||
msg.body.motion.eventTime = eventTime;
|
||||
msg.body.motion.pointerCount = pointerCount;
|
||||
for (size_t i = 0; i < pointerCount; i++) {
|
||||
mSharedMessage->motion.pointerProperties[i].copyFrom(pointerProperties[i]);
|
||||
mSharedMessage->motion.sampleData[0].coords[i].copyFrom(pointerCoords[i]);
|
||||
msg.body.motion.pointers[i].properties.copyFrom(pointerProperties[i]);
|
||||
msg.body.motion.pointers[i].coords.copyFrom(pointerCoords[i]);
|
||||
}
|
||||
|
||||
// Cache essential information about the motion event to ensure that a malicious consumer
|
||||
// cannot confuse the publisher by modifying the contents of the shared memory buffer while
|
||||
// it is being updated.
|
||||
if (action == AMOTION_EVENT_ACTION_MOVE
|
||||
|| action == AMOTION_EVENT_ACTION_HOVER_MOVE) {
|
||||
mMotionEventPointerCount = pointerCount;
|
||||
mMotionEventSampleDataStride = InputMessage::sampleDataStride(pointerCount);
|
||||
mMotionEventSampleDataTail = InputMessage::sampleDataPtrIncrement(
|
||||
mSharedMessage->motion.sampleData, mMotionEventSampleDataStride);
|
||||
} else {
|
||||
mMotionEventSampleDataTail = NULL;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t InputPublisher::appendMotionSample(
|
||||
nsecs_t eventTime,
|
||||
const PointerCoords* pointerCoords) {
|
||||
#if DEBUG_TRANSPORT_ACTIONS
|
||||
ALOGD("channel '%s' publisher ~ appendMotionSample: eventTime=%lld",
|
||||
mChannel->getName().string(), eventTime);
|
||||
#endif
|
||||
|
||||
if (! mPinned || ! mMotionEventSampleDataTail) {
|
||||
ALOGE("channel '%s' publisher ~ Cannot append motion sample because there is no current "
|
||||
"AMOTION_EVENT_ACTION_MOVE or AMOTION_EVENT_ACTION_HOVER_MOVE event.",
|
||||
mChannel->getName().string());
|
||||
return INVALID_OPERATION;
|
||||
}
|
||||
|
||||
InputMessage::SampleData* newTail = InputMessage::sampleDataPtrIncrement(
|
||||
mMotionEventSampleDataTail, mMotionEventSampleDataStride);
|
||||
size_t newBytesUsed = reinterpret_cast<char*>(newTail) -
|
||||
reinterpret_cast<char*>(mSharedMessage);
|
||||
|
||||
if (newBytesUsed > mAshmemSize) {
|
||||
#if DEBUG_TRANSPORT_ACTIONS
|
||||
ALOGD("channel '%s' publisher ~ Cannot append motion sample because the shared memory "
|
||||
"buffer is full. Buffer size: %d bytes, pointers: %d, samples: %d",
|
||||
mChannel->getName().string(),
|
||||
mAshmemSize, mMotionEventPointerCount, mSharedMessage->motion.sampleCount);
|
||||
#endif
|
||||
return NO_MEMORY;
|
||||
}
|
||||
|
||||
int result;
|
||||
if (mWasDispatched) {
|
||||
result = sem_trywait(& mSharedMessage->semaphore);
|
||||
if (result < 0) {
|
||||
if (errno == EAGAIN) {
|
||||
// Only possible source of contention is the consumer having consumed (or being in the
|
||||
// process of consuming) the message and left the semaphore count at 0.
|
||||
#if DEBUG_TRANSPORT_ACTIONS
|
||||
ALOGD("channel '%s' publisher ~ Cannot append motion sample because the message has "
|
||||
"already been consumed.", mChannel->getName().string());
|
||||
#endif
|
||||
return FAILED_TRANSACTION;
|
||||
} else {
|
||||
ALOGE("channel '%s' publisher ~ Error %d in sem_trywait.",
|
||||
mChannel->getName().string(), errno);
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mMotionEventSampleDataTail->eventTime = eventTime;
|
||||
for (size_t i = 0; i < mMotionEventPointerCount; i++) {
|
||||
mMotionEventSampleDataTail->coords[i].copyFrom(pointerCoords[i]);
|
||||
}
|
||||
mMotionEventSampleDataTail = newTail;
|
||||
|
||||
mSharedMessage->motion.sampleCount += 1;
|
||||
|
||||
if (mWasDispatched) {
|
||||
result = sem_post(& mSharedMessage->semaphore);
|
||||
if (result < 0) {
|
||||
ALOGE("channel '%s' publisher ~ Error %d in sem_post.",
|
||||
mChannel->getName().string(), errno);
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t InputPublisher::sendDispatchSignal() {
|
||||
#if DEBUG_TRANSPORT_ACTIONS
|
||||
ALOGD("channel '%s' publisher ~ sendDispatchSignal",
|
||||
mChannel->getName().string());
|
||||
#endif
|
||||
|
||||
mWasDispatched = true;
|
||||
return mChannel->sendSignal(INPUT_SIGNAL_DISPATCH);
|
||||
return mChannel->sendMessage(&msg);
|
||||
}
|
||||
|
||||
status_t InputPublisher::receiveFinishedSignal(bool* outHandled) {
|
||||
@@ -520,61 +287,28 @@ status_t InputPublisher::receiveFinishedSignal(bool* outHandled) {
|
||||
mChannel->getName().string());
|
||||
#endif
|
||||
|
||||
char signal;
|
||||
status_t result = mChannel->receiveSignal(& signal);
|
||||
InputMessage msg;
|
||||
status_t result = mChannel->receiveMessage(&msg);
|
||||
if (result) {
|
||||
*outHandled = false;
|
||||
return result;
|
||||
}
|
||||
if (signal == INPUT_SIGNAL_FINISHED_HANDLED) {
|
||||
*outHandled = true;
|
||||
} else if (signal == INPUT_SIGNAL_FINISHED_UNHANDLED) {
|
||||
*outHandled = false;
|
||||
} else {
|
||||
ALOGE("channel '%s' publisher ~ Received unexpected signal '%c' from consumer",
|
||||
mChannel->getName().string(), signal);
|
||||
if (msg.header.type != InputMessage::TYPE_FINISHED) {
|
||||
ALOGE("channel '%s' publisher ~ Received unexpected message of type %d from consumer",
|
||||
mChannel->getName().string(), msg.header.type);
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
*outHandled = msg.body.finished.handled;
|
||||
return OK;
|
||||
}
|
||||
|
||||
// --- InputConsumer ---
|
||||
|
||||
InputConsumer::InputConsumer(const sp<InputChannel>& channel) :
|
||||
mChannel(channel), mSharedMessage(NULL) {
|
||||
mChannel(channel) {
|
||||
}
|
||||
|
||||
InputConsumer::~InputConsumer() {
|
||||
if (mSharedMessage) {
|
||||
munmap(mSharedMessage, mAshmemSize);
|
||||
}
|
||||
}
|
||||
|
||||
status_t InputConsumer::initialize() {
|
||||
#if DEBUG_TRANSPORT_ACTIONS
|
||||
ALOGD("channel '%s' consumer ~ initialize",
|
||||
mChannel->getName().string());
|
||||
#endif
|
||||
|
||||
int ashmemFd = mChannel->getAshmemFd();
|
||||
int result = ashmem_get_size_region(ashmemFd);
|
||||
if (result < 0) {
|
||||
ALOGE("channel '%s' consumer ~ Error %d getting size of ashmem fd %d.",
|
||||
mChannel->getName().string(), result, ashmemFd);
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
mAshmemSize = (size_t) result;
|
||||
|
||||
mSharedMessage = static_cast<InputMessage*>(mmap(NULL, mAshmemSize,
|
||||
PROT_READ | PROT_WRITE, MAP_SHARED, ashmemFd, 0));
|
||||
if (! mSharedMessage) {
|
||||
ALOGE("channel '%s' consumer ~ mmap failed on ashmem fd %d.",
|
||||
mChannel->getName().string(), ashmemFd);
|
||||
return NO_MEMORY;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t InputConsumer::consume(InputEventFactoryInterface* factory, InputEvent** outEvent) {
|
||||
@@ -585,46 +319,28 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, InputEvent*
|
||||
|
||||
*outEvent = NULL;
|
||||
|
||||
int ashmemFd = mChannel->getAshmemFd();
|
||||
int result = ashmem_pin_region(ashmemFd, 0, 0);
|
||||
if (result != ASHMEM_NOT_PURGED) {
|
||||
if (result == ASHMEM_WAS_PURGED) {
|
||||
ALOGE("channel '%s' consumer ~ Error %d pinning ashmem fd %d because it was purged "
|
||||
"which probably indicates that the publisher and consumer are out of sync.",
|
||||
mChannel->getName().string(), result, ashmemFd);
|
||||
return INVALID_OPERATION;
|
||||
}
|
||||
|
||||
ALOGE("channel '%s' consumer ~ Error %d pinning ashmem fd %d.",
|
||||
mChannel->getName().string(), result, ashmemFd);
|
||||
return UNKNOWN_ERROR;
|
||||
InputMessage msg;
|
||||
status_t result = mChannel->receiveMessage(&msg);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (mSharedMessage->consumed) {
|
||||
ALOGE("channel '%s' consumer ~ The current message has already been consumed.",
|
||||
mChannel->getName().string());
|
||||
return INVALID_OPERATION;
|
||||
}
|
||||
|
||||
// Acquire but *never release* the semaphore. Contention on the semaphore is used to signal
|
||||
// to the publisher that the message has been consumed (or is in the process of being
|
||||
// consumed). Eventually the publisher will reinitialize the semaphore for the next message.
|
||||
result = sem_wait(& mSharedMessage->semaphore);
|
||||
if (result < 0) {
|
||||
ALOGE("channel '%s' consumer ~ Error %d in sem_wait.",
|
||||
mChannel->getName().string(), errno);
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
mSharedMessage->consumed = true;
|
||||
|
||||
switch (mSharedMessage->type) {
|
||||
case AINPUT_EVENT_TYPE_KEY: {
|
||||
switch (msg.header.type) {
|
||||
case InputMessage::TYPE_KEY: {
|
||||
KeyEvent* keyEvent = factory->createKeyEvent();
|
||||
if (! keyEvent) return NO_MEMORY;
|
||||
|
||||
populateKeyEvent(keyEvent);
|
||||
if (!keyEvent) return NO_MEMORY;
|
||||
|
||||
keyEvent->initialize(
|
||||
msg.body.key.deviceId,
|
||||
msg.body.key.source,
|
||||
msg.body.key.action,
|
||||
msg.body.key.flags,
|
||||
msg.body.key.keyCode,
|
||||
msg.body.key.scanCode,
|
||||
msg.body.key.metaState,
|
||||
msg.body.key.repeatCount,
|
||||
msg.body.key.downTime,
|
||||
msg.body.key.eventTime);
|
||||
*outEvent = keyEvent;
|
||||
break;
|
||||
}
|
||||
@@ -633,15 +349,38 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, InputEvent*
|
||||
MotionEvent* motionEvent = factory->createMotionEvent();
|
||||
if (! motionEvent) return NO_MEMORY;
|
||||
|
||||
populateMotionEvent(motionEvent);
|
||||
size_t pointerCount = msg.body.motion.pointerCount;
|
||||
PointerProperties pointerProperties[pointerCount];
|
||||
PointerCoords pointerCoords[pointerCount];
|
||||
for (size_t i = 0; i < pointerCount; i++) {
|
||||
pointerProperties[i].copyFrom(msg.body.motion.pointers[i].properties);
|
||||
pointerCoords[i].copyFrom(msg.body.motion.pointers[i].coords);
|
||||
}
|
||||
|
||||
motionEvent->initialize(
|
||||
msg.body.motion.deviceId,
|
||||
msg.body.motion.source,
|
||||
msg.body.motion.action,
|
||||
msg.body.motion.flags,
|
||||
msg.body.motion.edgeFlags,
|
||||
msg.body.motion.metaState,
|
||||
msg.body.motion.buttonState,
|
||||
msg.body.motion.xOffset,
|
||||
msg.body.motion.yOffset,
|
||||
msg.body.motion.xPrecision,
|
||||
msg.body.motion.yPrecision,
|
||||
msg.body.motion.downTime,
|
||||
msg.body.motion.eventTime,
|
||||
pointerCount,
|
||||
pointerProperties,
|
||||
pointerCoords);
|
||||
*outEvent = motionEvent;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
ALOGE("channel '%s' consumer ~ Received message of unknown type %d",
|
||||
mChannel->getName().string(), mSharedMessage->type);
|
||||
ALOGE("channel '%s' consumer ~ Received unexpected message of type %d",
|
||||
mChannel->getName().string(), msg.header.type);
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
@@ -654,74 +393,10 @@ status_t InputConsumer::sendFinishedSignal(bool handled) {
|
||||
mChannel->getName().string(), handled);
|
||||
#endif
|
||||
|
||||
return mChannel->sendSignal(handled
|
||||
? INPUT_SIGNAL_FINISHED_HANDLED
|
||||
: INPUT_SIGNAL_FINISHED_UNHANDLED);
|
||||
}
|
||||
|
||||
status_t InputConsumer::receiveDispatchSignal() {
|
||||
#if DEBUG_TRANSPORT_ACTIONS
|
||||
ALOGD("channel '%s' consumer ~ receiveDispatchSignal",
|
||||
mChannel->getName().string());
|
||||
#endif
|
||||
|
||||
char signal;
|
||||
status_t result = mChannel->receiveSignal(& signal);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
if (signal != INPUT_SIGNAL_DISPATCH) {
|
||||
ALOGE("channel '%s' consumer ~ Received unexpected signal '%c' from publisher",
|
||||
mChannel->getName().string(), signal);
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
void InputConsumer::populateKeyEvent(KeyEvent* keyEvent) const {
|
||||
keyEvent->initialize(
|
||||
mSharedMessage->deviceId,
|
||||
mSharedMessage->source,
|
||||
mSharedMessage->key.action,
|
||||
mSharedMessage->key.flags,
|
||||
mSharedMessage->key.keyCode,
|
||||
mSharedMessage->key.scanCode,
|
||||
mSharedMessage->key.metaState,
|
||||
mSharedMessage->key.repeatCount,
|
||||
mSharedMessage->key.downTime,
|
||||
mSharedMessage->key.eventTime);
|
||||
}
|
||||
|
||||
void InputConsumer::populateMotionEvent(MotionEvent* motionEvent) const {
|
||||
motionEvent->initialize(
|
||||
mSharedMessage->deviceId,
|
||||
mSharedMessage->source,
|
||||
mSharedMessage->motion.action,
|
||||
mSharedMessage->motion.flags,
|
||||
mSharedMessage->motion.edgeFlags,
|
||||
mSharedMessage->motion.metaState,
|
||||
mSharedMessage->motion.buttonState,
|
||||
mSharedMessage->motion.xOffset,
|
||||
mSharedMessage->motion.yOffset,
|
||||
mSharedMessage->motion.xPrecision,
|
||||
mSharedMessage->motion.yPrecision,
|
||||
mSharedMessage->motion.downTime,
|
||||
mSharedMessage->motion.sampleData[0].eventTime,
|
||||
mSharedMessage->motion.pointerCount,
|
||||
mSharedMessage->motion.pointerProperties,
|
||||
mSharedMessage->motion.sampleData[0].coords);
|
||||
|
||||
size_t sampleCount = mSharedMessage->motion.sampleCount;
|
||||
if (sampleCount > 1) {
|
||||
InputMessage::SampleData* sampleData = mSharedMessage->motion.sampleData;
|
||||
size_t sampleDataStride = InputMessage::sampleDataStride(
|
||||
mSharedMessage->motion.pointerCount);
|
||||
|
||||
while (--sampleCount > 0) {
|
||||
sampleData = InputMessage::sampleDataPtrIncrement(sampleData, sampleDataStride);
|
||||
motionEvent->addSample(sampleData->eventTime, sampleData->coords);
|
||||
}
|
||||
}
|
||||
InputMessage msg;
|
||||
msg.header.type = InputMessage::TYPE_FINISHED;
|
||||
msg.body.finished.handled = handled;
|
||||
return mChannel->sendMessage(&msg);
|
||||
}
|
||||
|
||||
} // namespace android
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -57,11 +57,8 @@ protected:
|
||||
clientChannel.clear();
|
||||
}
|
||||
|
||||
void Initialize();
|
||||
void PublishAndConsumeKeyEvent();
|
||||
void PublishAndConsumeMotionEvent(
|
||||
size_t samplesToAppendBeforeDispatch = 0,
|
||||
size_t samplesToAppendAfterDispatch = 0);
|
||||
void PublishAndConsumeMotionEvent();
|
||||
};
|
||||
|
||||
TEST_F(InputPublisherAndConsumerTest, GetChannel_ReturnsTheChannel) {
|
||||
@@ -69,18 +66,6 @@ TEST_F(InputPublisherAndConsumerTest, GetChannel_ReturnsTheChannel) {
|
||||
EXPECT_EQ(clientChannel.get(), mConsumer->getChannel().get());
|
||||
}
|
||||
|
||||
void InputPublisherAndConsumerTest::Initialize() {
|
||||
status_t status;
|
||||
|
||||
status = mPublisher->initialize();
|
||||
ASSERT_EQ(OK, status)
|
||||
<< "publisher initialize should return OK";
|
||||
|
||||
status = mConsumer->initialize();
|
||||
ASSERT_EQ(OK, status)
|
||||
<< "consumer initialize should return OK";
|
||||
}
|
||||
|
||||
void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() {
|
||||
status_t status;
|
||||
|
||||
@@ -100,14 +85,6 @@ void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() {
|
||||
ASSERT_EQ(OK, status)
|
||||
<< "publisher publishKeyEvent should return OK";
|
||||
|
||||
status = mPublisher->sendDispatchSignal();
|
||||
ASSERT_EQ(OK, status)
|
||||
<< "publisher sendDispatchSignal should return OK";
|
||||
|
||||
status = mConsumer->receiveDispatchSignal();
|
||||
ASSERT_EQ(OK, status)
|
||||
<< "consumer receiveDispatchSignal should return OK";
|
||||
|
||||
InputEvent* event;
|
||||
status = mConsumer->consume(& mEventFactory, & event);
|
||||
ASSERT_EQ(OK, status)
|
||||
@@ -140,14 +117,9 @@ void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() {
|
||||
<< "publisher receiveFinishedSignal should return OK";
|
||||
ASSERT_TRUE(handled)
|
||||
<< "publisher receiveFinishedSignal should have set handled to consumer's reply";
|
||||
|
||||
status = mPublisher->reset();
|
||||
ASSERT_EQ(OK, status)
|
||||
<< "publisher reset should return OK";
|
||||
}
|
||||
|
||||
void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent(
|
||||
size_t samplesToAppendBeforeDispatch, size_t samplesToAppendAfterDispatch) {
|
||||
void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() {
|
||||
status_t status;
|
||||
|
||||
const int32_t deviceId = 1;
|
||||
@@ -163,65 +135,33 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent(
|
||||
const float yPrecision = 0.5;
|
||||
const nsecs_t downTime = 3;
|
||||
const size_t pointerCount = 3;
|
||||
const nsecs_t eventTime = 4;
|
||||
PointerProperties pointerProperties[pointerCount];
|
||||
PointerCoords pointerCoords[pointerCount];
|
||||
for (size_t i = 0; i < pointerCount; i++) {
|
||||
pointerProperties[i].clear();
|
||||
pointerProperties[i].id = (i + 2) % pointerCount;
|
||||
pointerProperties[i].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
|
||||
}
|
||||
|
||||
Vector<nsecs_t> sampleEventTimes;
|
||||
Vector<PointerCoords> samplePointerCoords;
|
||||
|
||||
for (size_t i = 0; i <= samplesToAppendAfterDispatch + samplesToAppendBeforeDispatch; i++) {
|
||||
sampleEventTimes.push(i + 10);
|
||||
for (size_t j = 0; j < pointerCount; j++) {
|
||||
samplePointerCoords.push();
|
||||
PointerCoords& pc = samplePointerCoords.editTop();
|
||||
pc.clear();
|
||||
pc.setAxisValue(AMOTION_EVENT_AXIS_X, 100 * i + j);
|
||||
pc.setAxisValue(AMOTION_EVENT_AXIS_Y, 200 * i + j);
|
||||
pc.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 0.5 * i + j);
|
||||
pc.setAxisValue(AMOTION_EVENT_AXIS_SIZE, 0.7 * i + j);
|
||||
pc.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 1.5 * i + j);
|
||||
pc.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 1.7 * i + j);
|
||||
pc.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 2.5 * i + j);
|
||||
pc.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 2.7 * i + j);
|
||||
pc.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 3.5 * i + j);
|
||||
}
|
||||
pointerCoords[i].clear();
|
||||
pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_X, 100 * i);
|
||||
pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_Y, 200 * i);
|
||||
pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 0.5 * i);
|
||||
pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 0.7 * i);
|
||||
pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 1.5 * i);
|
||||
pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 1.7 * i);
|
||||
pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 2.5 * i);
|
||||
pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 2.7 * i);
|
||||
pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 3.5 * i);
|
||||
}
|
||||
|
||||
status = mPublisher->publishMotionEvent(deviceId, source, action, flags, edgeFlags,
|
||||
metaState, buttonState, xOffset, yOffset, xPrecision, yPrecision,
|
||||
downTime, sampleEventTimes[0], pointerCount,
|
||||
pointerProperties, samplePointerCoords.array());
|
||||
downTime, eventTime, pointerCount,
|
||||
pointerProperties, pointerCoords);
|
||||
ASSERT_EQ(OK, status)
|
||||
<< "publisher publishMotionEvent should return OK";
|
||||
|
||||
for (size_t i = 0; i < samplesToAppendBeforeDispatch; i++) {
|
||||
size_t sampleIndex = i + 1;
|
||||
status = mPublisher->appendMotionSample(sampleEventTimes[sampleIndex],
|
||||
samplePointerCoords.array() + sampleIndex * pointerCount);
|
||||
ASSERT_EQ(OK, status)
|
||||
<< "publisher appendMotionEvent should return OK";
|
||||
}
|
||||
|
||||
status = mPublisher->sendDispatchSignal();
|
||||
ASSERT_EQ(OK, status)
|
||||
<< "publisher sendDispatchSignal should return OK";
|
||||
|
||||
for (size_t i = 0; i < samplesToAppendAfterDispatch; i++) {
|
||||
size_t sampleIndex = i + 1 + samplesToAppendBeforeDispatch;
|
||||
status = mPublisher->appendMotionSample(sampleEventTimes[sampleIndex],
|
||||
samplePointerCoords.array() + sampleIndex * pointerCount);
|
||||
ASSERT_EQ(OK, status)
|
||||
<< "publisher appendMotionEvent should return OK";
|
||||
}
|
||||
|
||||
status = mConsumer->receiveDispatchSignal();
|
||||
ASSERT_EQ(OK, status)
|
||||
<< "consumer receiveDispatchSignal should return OK";
|
||||
|
||||
InputEvent* event;
|
||||
status = mConsumer->consume(& mEventFactory, & event);
|
||||
ASSERT_EQ(OK, status)
|
||||
@@ -232,8 +172,6 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent(
|
||||
ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, event->getType())
|
||||
<< "consumer should have returned a motion event";
|
||||
|
||||
size_t lastSampleIndex = samplesToAppendBeforeDispatch + samplesToAppendAfterDispatch;
|
||||
|
||||
MotionEvent* motionEvent = static_cast<MotionEvent*>(event);
|
||||
EXPECT_EQ(deviceId, motionEvent->getDeviceId());
|
||||
EXPECT_EQ(source, motionEvent->getSource());
|
||||
@@ -245,74 +183,36 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent(
|
||||
EXPECT_EQ(xPrecision, motionEvent->getXPrecision());
|
||||
EXPECT_EQ(yPrecision, motionEvent->getYPrecision());
|
||||
EXPECT_EQ(downTime, motionEvent->getDownTime());
|
||||
EXPECT_EQ(sampleEventTimes[lastSampleIndex], motionEvent->getEventTime());
|
||||
EXPECT_EQ(eventTime, motionEvent->getEventTime());
|
||||
EXPECT_EQ(pointerCount, motionEvent->getPointerCount());
|
||||
EXPECT_EQ(lastSampleIndex, motionEvent->getHistorySize());
|
||||
EXPECT_EQ(0U, motionEvent->getHistorySize());
|
||||
|
||||
for (size_t i = 0; i < pointerCount; i++) {
|
||||
SCOPED_TRACE(i);
|
||||
EXPECT_EQ(pointerProperties[i].id, motionEvent->getPointerId(i));
|
||||
EXPECT_EQ(pointerProperties[i].toolType, motionEvent->getToolType(i));
|
||||
}
|
||||
|
||||
for (size_t sampleIndex = 0; sampleIndex < lastSampleIndex; sampleIndex++) {
|
||||
SCOPED_TRACE(sampleIndex);
|
||||
EXPECT_EQ(sampleEventTimes[sampleIndex],
|
||||
motionEvent->getHistoricalEventTime(sampleIndex));
|
||||
for (size_t i = 0; i < pointerCount; i++) {
|
||||
SCOPED_TRACE(i);
|
||||
size_t offset = sampleIndex * pointerCount + i;
|
||||
EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_X),
|
||||
motionEvent->getHistoricalRawX(i, sampleIndex));
|
||||
EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_Y),
|
||||
motionEvent->getHistoricalRawY(i, sampleIndex));
|
||||
EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_X) + xOffset,
|
||||
motionEvent->getHistoricalX(i, sampleIndex));
|
||||
EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_Y) + yOffset,
|
||||
motionEvent->getHistoricalY(i, sampleIndex));
|
||||
EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
|
||||
motionEvent->getHistoricalPressure(i, sampleIndex));
|
||||
EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_SIZE),
|
||||
motionEvent->getHistoricalSize(i, sampleIndex));
|
||||
EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR),
|
||||
motionEvent->getHistoricalTouchMajor(i, sampleIndex));
|
||||
EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR),
|
||||
motionEvent->getHistoricalTouchMinor(i, sampleIndex));
|
||||
EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR),
|
||||
motionEvent->getHistoricalToolMajor(i, sampleIndex));
|
||||
EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR),
|
||||
motionEvent->getHistoricalToolMinor(i, sampleIndex));
|
||||
EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION),
|
||||
motionEvent->getHistoricalOrientation(i, sampleIndex));
|
||||
}
|
||||
}
|
||||
|
||||
SCOPED_TRACE(lastSampleIndex);
|
||||
EXPECT_EQ(sampleEventTimes[lastSampleIndex], motionEvent->getEventTime());
|
||||
for (size_t i = 0; i < pointerCount; i++) {
|
||||
SCOPED_TRACE(i);
|
||||
size_t offset = lastSampleIndex * pointerCount + i;
|
||||
EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_X),
|
||||
EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X),
|
||||
motionEvent->getRawX(i));
|
||||
EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_Y),
|
||||
EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y),
|
||||
motionEvent->getRawY(i));
|
||||
EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_X) + xOffset,
|
||||
EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X) + xOffset,
|
||||
motionEvent->getX(i));
|
||||
EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_Y) + yOffset,
|
||||
EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y) + yOffset,
|
||||
motionEvent->getY(i));
|
||||
EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
|
||||
EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
|
||||
motionEvent->getPressure(i));
|
||||
EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_SIZE),
|
||||
EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE),
|
||||
motionEvent->getSize(i));
|
||||
EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR),
|
||||
EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR),
|
||||
motionEvent->getTouchMajor(i));
|
||||
EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR),
|
||||
EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR),
|
||||
motionEvent->getTouchMinor(i));
|
||||
EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR),
|
||||
EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR),
|
||||
motionEvent->getToolMajor(i));
|
||||
EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR),
|
||||
EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR),
|
||||
motionEvent->getToolMinor(i));
|
||||
EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION),
|
||||
EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION),
|
||||
motionEvent->getOrientation(i));
|
||||
}
|
||||
|
||||
@@ -326,64 +226,18 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent(
|
||||
<< "publisher receiveFinishedSignal should return OK";
|
||||
ASSERT_FALSE(handled)
|
||||
<< "publisher receiveFinishedSignal should have set handled to consumer's reply";
|
||||
|
||||
status = mPublisher->reset();
|
||||
ASSERT_EQ(OK, status)
|
||||
<< "publisher reset should return OK";
|
||||
}
|
||||
|
||||
TEST_F(InputPublisherAndConsumerTest, PublishKeyEvent_EndToEnd) {
|
||||
ASSERT_NO_FATAL_FAILURE(Initialize());
|
||||
ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent());
|
||||
}
|
||||
|
||||
TEST_F(InputPublisherAndConsumerTest, PublishKeyEvent_WhenNotReset_ReturnsError) {
|
||||
status_t status;
|
||||
ASSERT_NO_FATAL_FAILURE(Initialize());
|
||||
|
||||
status = mPublisher->publishKeyEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
ASSERT_EQ(OK, status)
|
||||
<< "publisher publishKeyEvent should return OK first time";
|
||||
|
||||
status = mPublisher->publishKeyEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
ASSERT_EQ(INVALID_OPERATION, status)
|
||||
<< "publisher publishKeyEvent should return INVALID_OPERATION because "
|
||||
"the publisher was not reset";
|
||||
}
|
||||
|
||||
TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_EndToEnd) {
|
||||
ASSERT_NO_FATAL_FAILURE(Initialize());
|
||||
ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent());
|
||||
}
|
||||
|
||||
TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenNotReset_ReturnsError) {
|
||||
status_t status;
|
||||
ASSERT_NO_FATAL_FAILURE(Initialize());
|
||||
|
||||
const size_t pointerCount = 1;
|
||||
PointerProperties pointerProperties[pointerCount];
|
||||
PointerCoords pointerCoords[pointerCount];
|
||||
for (size_t i = 0; i < pointerCount; i++) {
|
||||
pointerProperties[i].clear();
|
||||
pointerCoords[i].clear();
|
||||
}
|
||||
|
||||
status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
pointerCount, pointerProperties, pointerCoords);
|
||||
ASSERT_EQ(OK, status)
|
||||
<< "publisher publishMotionEvent should return OK";
|
||||
|
||||
status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
pointerCount, pointerProperties, pointerCoords);
|
||||
ASSERT_EQ(INVALID_OPERATION, status)
|
||||
<< "publisher publishMotionEvent should return INVALID_OPERATION because ";
|
||||
"the publisher was not reset";
|
||||
}
|
||||
|
||||
TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenPointerCountLessThan1_ReturnsError) {
|
||||
status_t status;
|
||||
ASSERT_NO_FATAL_FAILURE(Initialize());
|
||||
|
||||
const size_t pointerCount = 0;
|
||||
PointerProperties pointerProperties[pointerCount];
|
||||
PointerCoords pointerCoords[pointerCount];
|
||||
@@ -396,8 +250,6 @@ TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenPointerCountLessTha
|
||||
|
||||
TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenPointerCountGreaterThanMax_ReturnsError) {
|
||||
status_t status;
|
||||
ASSERT_NO_FATAL_FAILURE(Initialize());
|
||||
|
||||
const size_t pointerCount = MAX_POINTERS + 1;
|
||||
PointerProperties pointerProperties[pointerCount];
|
||||
PointerCoords pointerCoords[pointerCount];
|
||||
@@ -413,7 +265,6 @@ TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenPointerCountGreater
|
||||
}
|
||||
|
||||
TEST_F(InputPublisherAndConsumerTest, PublishMultipleEvents_EndToEnd) {
|
||||
ASSERT_NO_FATAL_FAILURE(Initialize());
|
||||
ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent());
|
||||
ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent());
|
||||
ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent());
|
||||
@@ -421,111 +272,4 @@ TEST_F(InputPublisherAndConsumerTest, PublishMultipleEvents_EndToEnd) {
|
||||
ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent());
|
||||
}
|
||||
|
||||
TEST_F(InputPublisherAndConsumerTest, AppendMotionSample_WhenCalledBeforeDispatchSignal_AppendsSamples) {
|
||||
status_t status;
|
||||
ASSERT_NO_FATAL_FAILURE(Initialize());
|
||||
ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent(3, 0));
|
||||
}
|
||||
|
||||
TEST_F(InputPublisherAndConsumerTest, AppendMotionSample_WhenCalledAfterDispatchSignalAndNotConsumed_AppendsSamples) {
|
||||
status_t status;
|
||||
ASSERT_NO_FATAL_FAILURE(Initialize());
|
||||
ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent(0, 4));
|
||||
}
|
||||
|
||||
TEST_F(InputPublisherAndConsumerTest, AppendMotionSample_WhenNoMotionEventPublished_ReturnsError) {
|
||||
status_t status;
|
||||
ASSERT_NO_FATAL_FAILURE(Initialize());
|
||||
|
||||
PointerCoords pointerCoords[1];
|
||||
status = mPublisher->appendMotionSample(0, pointerCoords);
|
||||
ASSERT_EQ(INVALID_OPERATION, status)
|
||||
<< "publisher appendMotionSample should return INVALID_OPERATION";
|
||||
}
|
||||
|
||||
TEST_F(InputPublisherAndConsumerTest, AppendMotionSample_WhenPublishedMotionEventIsNotAMove_ReturnsError) {
|
||||
status_t status;
|
||||
ASSERT_NO_FATAL_FAILURE(Initialize());
|
||||
|
||||
const size_t pointerCount = MAX_POINTERS;
|
||||
PointerProperties pointerProperties[pointerCount];
|
||||
PointerCoords pointerCoords[pointerCount];
|
||||
for (size_t i = 0; i < pointerCount; i++) {
|
||||
pointerProperties[i].clear();
|
||||
pointerCoords[i].clear();
|
||||
}
|
||||
|
||||
status = mPublisher->publishMotionEvent(0, 0, AMOTION_EVENT_ACTION_DOWN,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, pointerCount, pointerProperties, pointerCoords);
|
||||
ASSERT_EQ(OK, status);
|
||||
|
||||
status = mPublisher->appendMotionSample(0, pointerCoords);
|
||||
ASSERT_EQ(INVALID_OPERATION, status)
|
||||
<< "publisher appendMotionSample should return INVALID_OPERATION";
|
||||
}
|
||||
|
||||
TEST_F(InputPublisherAndConsumerTest, AppendMotionSample_WhenAlreadyConsumed_ReturnsError) {
|
||||
status_t status;
|
||||
ASSERT_NO_FATAL_FAILURE(Initialize());
|
||||
|
||||
const size_t pointerCount = MAX_POINTERS;
|
||||
PointerProperties pointerProperties[pointerCount];
|
||||
PointerCoords pointerCoords[pointerCount];
|
||||
for (size_t i = 0; i < pointerCount; i++) {
|
||||
pointerProperties[i].clear();
|
||||
pointerCoords[i].clear();
|
||||
}
|
||||
|
||||
status = mPublisher->publishMotionEvent(0, 0, AMOTION_EVENT_ACTION_MOVE,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, pointerCount, pointerProperties, pointerCoords);
|
||||
ASSERT_EQ(OK, status);
|
||||
|
||||
status = mPublisher->sendDispatchSignal();
|
||||
ASSERT_EQ(OK, status);
|
||||
|
||||
status = mConsumer->receiveDispatchSignal();
|
||||
ASSERT_EQ(OK, status);
|
||||
|
||||
InputEvent* event;
|
||||
status = mConsumer->consume(& mEventFactory, & event);
|
||||
ASSERT_EQ(OK, status);
|
||||
|
||||
status = mPublisher->appendMotionSample(0, pointerCoords);
|
||||
ASSERT_EQ(status_t(FAILED_TRANSACTION), status)
|
||||
<< "publisher appendMotionSample should return FAILED_TRANSACTION";
|
||||
}
|
||||
|
||||
TEST_F(InputPublisherAndConsumerTest, AppendMotionSample_WhenBufferFull_ReturnsError) {
|
||||
status_t status;
|
||||
ASSERT_NO_FATAL_FAILURE(Initialize());
|
||||
|
||||
const size_t pointerCount = MAX_POINTERS;
|
||||
PointerProperties pointerProperties[pointerCount];
|
||||
PointerCoords pointerCoords[pointerCount];
|
||||
for (size_t i = 0; i < pointerCount; i++) {
|
||||
pointerProperties[i].clear();
|
||||
pointerCoords[i].clear();
|
||||
}
|
||||
|
||||
status = mPublisher->publishMotionEvent(0, 0, AMOTION_EVENT_ACTION_MOVE,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, pointerCount, pointerProperties, pointerCoords);
|
||||
ASSERT_EQ(OK, status);
|
||||
|
||||
for (int count = 1;; count++) {
|
||||
ASSERT_LT(count, 100000) << "should eventually reach OOM";
|
||||
|
||||
status = mPublisher->appendMotionSample(0, pointerCoords);
|
||||
if (status != OK) {
|
||||
ASSERT_GT(count, 12) << "should be able to add at least a dozen samples";
|
||||
ASSERT_EQ(NO_MEMORY, status)
|
||||
<< "publisher appendMotionSample should return NO_MEMORY when buffer is full";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
status = mPublisher->appendMotionSample(0, pointerCoords);
|
||||
ASSERT_EQ(NO_MEMORY, status)
|
||||
<< "publisher appendMotionSample should return NO_MEMORY persistently until reset";
|
||||
}
|
||||
|
||||
} // namespace android
|
||||
|
||||
@@ -196,8 +196,8 @@ InputDispatcher::~InputDispatcher() {
|
||||
drainInboundQueueLocked();
|
||||
}
|
||||
|
||||
while (mConnectionsByReceiveFd.size() != 0) {
|
||||
unregisterInputChannel(mConnectionsByReceiveFd.valueAt(0)->inputChannel);
|
||||
while (mConnectionsByFd.size() != 0) {
|
||||
unregisterInputChannel(mConnectionsByFd.valueAt(0)->inputChannel);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -888,7 +888,7 @@ void InputDispatcher::dispatchEventToCurrentInputTargetsLocked(nsecs_t currentTi
|
||||
|
||||
ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel);
|
||||
if (connectionIndex >= 0) {
|
||||
sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
|
||||
sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
|
||||
prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget);
|
||||
} else {
|
||||
#if DEBUG_FOCUS
|
||||
@@ -994,7 +994,7 @@ void InputDispatcher::resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout
|
||||
if (inputChannel.get()) {
|
||||
ssize_t connectionIndex = getConnectionIndexLocked(inputChannel);
|
||||
if (connectionIndex >= 0) {
|
||||
sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
|
||||
sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
|
||||
if (connection->status == Connection::STATUS_NORMAL) {
|
||||
CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS,
|
||||
"application not responding");
|
||||
@@ -1643,7 +1643,7 @@ bool InputDispatcher::isWindowFinishedWithPreviousInputLocked(
|
||||
const sp<InputWindowHandle>& windowHandle) {
|
||||
ssize_t connectionIndex = getConnectionIndexLocked(windowHandle->getInputChannel());
|
||||
if (connectionIndex >= 0) {
|
||||
sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
|
||||
sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
|
||||
return connection->outboundQueue.isEmpty();
|
||||
} else {
|
||||
return true;
|
||||
@@ -1957,15 +1957,6 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
|
||||
}
|
||||
}
|
||||
|
||||
// Send the dispatch signal.
|
||||
status = connection->inputPublisher.sendDispatchSignal();
|
||||
if (status) {
|
||||
ALOGE("channel '%s' ~ Could not send dispatch signal, status=%d",
|
||||
connection->getInputChannelName(), status);
|
||||
abortBrokenDispatchCycleLocked(currentTime, connection, true /*notify*/);
|
||||
return;
|
||||
}
|
||||
|
||||
// Record information about the newly started dispatch cycle.
|
||||
connection->lastEventTime = eventEntry->eventTime;
|
||||
connection->lastDispatchTime = currentTime;
|
||||
@@ -1990,17 +1981,6 @@ void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime,
|
||||
return;
|
||||
}
|
||||
|
||||
// Reset the publisher since the event has been consumed.
|
||||
// We do this now so that the publisher can release some of its internal resources
|
||||
// while waiting for the next dispatch cycle to begin.
|
||||
status_t status = connection->inputPublisher.reset();
|
||||
if (status) {
|
||||
ALOGE("channel '%s' ~ Could not reset publisher, status=%d",
|
||||
connection->getInputChannelName(), status);
|
||||
abortBrokenDispatchCycleLocked(currentTime, connection, true /*notify*/);
|
||||
return;
|
||||
}
|
||||
|
||||
// Notify other system components and prepare to start the next dispatch cycle.
|
||||
onDispatchCycleFinishedLocked(currentTime, connection, handled);
|
||||
}
|
||||
@@ -2064,21 +2044,21 @@ void InputDispatcher::drainOutboundQueueLocked(Connection* connection) {
|
||||
deactivateConnectionLocked(connection);
|
||||
}
|
||||
|
||||
int InputDispatcher::handleReceiveCallback(int receiveFd, int events, void* data) {
|
||||
int InputDispatcher::handleReceiveCallback(int fd, int events, void* data) {
|
||||
InputDispatcher* d = static_cast<InputDispatcher*>(data);
|
||||
|
||||
{ // acquire lock
|
||||
AutoMutex _l(d->mLock);
|
||||
|
||||
ssize_t connectionIndex = d->mConnectionsByReceiveFd.indexOfKey(receiveFd);
|
||||
ssize_t connectionIndex = d->mConnectionsByFd.indexOfKey(fd);
|
||||
if (connectionIndex < 0) {
|
||||
ALOGE("Received spurious receive callback for unknown input channel. "
|
||||
"fd=%d, events=0x%x", receiveFd, events);
|
||||
"fd=%d, events=0x%x", fd, events);
|
||||
return 0; // remove the callback
|
||||
}
|
||||
|
||||
bool notify;
|
||||
sp<Connection> connection = d->mConnectionsByReceiveFd.valueAt(connectionIndex);
|
||||
sp<Connection> connection = d->mConnectionsByFd.valueAt(connectionIndex);
|
||||
if (!(events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP))) {
|
||||
if (!(events & ALOOPER_EVENT_INPUT)) {
|
||||
ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event. "
|
||||
@@ -2117,9 +2097,9 @@ int InputDispatcher::handleReceiveCallback(int receiveFd, int events, void* data
|
||||
|
||||
void InputDispatcher::synthesizeCancelationEventsForAllConnectionsLocked(
|
||||
const CancelationOptions& options) {
|
||||
for (size_t i = 0; i < mConnectionsByReceiveFd.size(); i++) {
|
||||
for (size_t i = 0; i < mConnectionsByFd.size(); i++) {
|
||||
synthesizeCancelationEventsForConnectionLocked(
|
||||
mConnectionsByReceiveFd.valueAt(i), options);
|
||||
mConnectionsByFd.valueAt(i), options);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2128,7 +2108,7 @@ void InputDispatcher::synthesizeCancelationEventsForInputChannelLocked(
|
||||
ssize_t index = getConnectionIndexLocked(channel);
|
||||
if (index >= 0) {
|
||||
synthesizeCancelationEventsForConnectionLocked(
|
||||
mConnectionsByReceiveFd.valueAt(index), options);
|
||||
mConnectionsByFd.valueAt(index), options);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2968,8 +2948,8 @@ bool InputDispatcher::transferTouchFocus(const sp<InputChannel>& fromChannel,
|
||||
ssize_t fromConnectionIndex = getConnectionIndexLocked(fromChannel);
|
||||
ssize_t toConnectionIndex = getConnectionIndexLocked(toChannel);
|
||||
if (fromConnectionIndex >= 0 && toConnectionIndex >= 0) {
|
||||
sp<Connection> fromConnection = mConnectionsByReceiveFd.valueAt(fromConnectionIndex);
|
||||
sp<Connection> toConnection = mConnectionsByReceiveFd.valueAt(toConnectionIndex);
|
||||
sp<Connection> fromConnection = mConnectionsByFd.valueAt(fromConnectionIndex);
|
||||
sp<Connection> toConnection = mConnectionsByFd.valueAt(toConnectionIndex);
|
||||
|
||||
fromConnection->inputState.copyPointerStateTo(toConnection->inputState);
|
||||
CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
|
||||
@@ -3134,21 +3114,15 @@ status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChan
|
||||
}
|
||||
|
||||
sp<Connection> connection = new Connection(inputChannel, inputWindowHandle, monitor);
|
||||
status_t status = connection->initialize();
|
||||
if (status) {
|
||||
ALOGE("Failed to initialize input publisher for input channel '%s', status=%d",
|
||||
inputChannel->getName().string(), status);
|
||||
return status;
|
||||
}
|
||||
|
||||
int32_t receiveFd = inputChannel->getReceivePipeFd();
|
||||
mConnectionsByReceiveFd.add(receiveFd, connection);
|
||||
int32_t fd = inputChannel->getFd();
|
||||
mConnectionsByFd.add(fd, connection);
|
||||
|
||||
if (monitor) {
|
||||
mMonitoringChannels.push(inputChannel);
|
||||
}
|
||||
|
||||
mLooper->addFd(receiveFd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
|
||||
mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
|
||||
|
||||
runCommandsLockedInterruptible();
|
||||
} // release lock
|
||||
@@ -3184,14 +3158,14 @@ status_t InputDispatcher::unregisterInputChannelLocked(const sp<InputChannel>& i
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
|
||||
mConnectionsByReceiveFd.removeItemsAt(connectionIndex);
|
||||
sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
|
||||
mConnectionsByFd.removeItemsAt(connectionIndex);
|
||||
|
||||
if (connection->monitor) {
|
||||
removeMonitorChannelLocked(inputChannel);
|
||||
}
|
||||
|
||||
mLooper->removeFd(inputChannel->getReceivePipeFd());
|
||||
mLooper->removeFd(inputChannel->getFd());
|
||||
|
||||
nsecs_t currentTime = now();
|
||||
abortBrokenDispatchCycleLocked(currentTime, connection, notify);
|
||||
@@ -3212,9 +3186,9 @@ void InputDispatcher::removeMonitorChannelLocked(const sp<InputChannel>& inputCh
|
||||
}
|
||||
|
||||
ssize_t InputDispatcher::getConnectionIndexLocked(const sp<InputChannel>& inputChannel) {
|
||||
ssize_t connectionIndex = mConnectionsByReceiveFd.indexOfKey(inputChannel->getReceivePipeFd());
|
||||
ssize_t connectionIndex = mConnectionsByFd.indexOfKey(inputChannel->getFd());
|
||||
if (connectionIndex >= 0) {
|
||||
sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
|
||||
sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
|
||||
if (connection->inputChannel.get() == inputChannel.get()) {
|
||||
return connectionIndex;
|
||||
}
|
||||
@@ -4052,10 +4026,6 @@ InputDispatcher::Connection::Connection(const sp<InputChannel>& inputChannel,
|
||||
InputDispatcher::Connection::~Connection() {
|
||||
}
|
||||
|
||||
status_t InputDispatcher::Connection::initialize() {
|
||||
return inputPublisher.initialize();
|
||||
}
|
||||
|
||||
const char* InputDispatcher::Connection::getStatusLabel() const {
|
||||
switch (status) {
|
||||
case STATUS_NORMAL:
|
||||
|
||||
@@ -807,8 +807,6 @@ private:
|
||||
inline double getDispatchLatencyMillis(nsecs_t currentTime) const {
|
||||
return (currentTime - lastDispatchTime) / 1000000.0;
|
||||
}
|
||||
|
||||
status_t initialize();
|
||||
};
|
||||
|
||||
enum DropReason {
|
||||
@@ -862,8 +860,8 @@ private:
|
||||
|
||||
sp<InputWindowHandle> findTouchedWindowAtLocked(int32_t x, int32_t y);
|
||||
|
||||
// All registered connections mapped by receive pipe file descriptor.
|
||||
KeyedVector<int, sp<Connection> > mConnectionsByReceiveFd;
|
||||
// All registered connections mapped by channel file descriptor.
|
||||
KeyedVector<int, sp<Connection> > mConnectionsByFd;
|
||||
|
||||
ssize_t getConnectionIndexLocked(const sp<InputChannel>& inputChannel);
|
||||
|
||||
@@ -1027,7 +1025,7 @@ private:
|
||||
void abortBrokenDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
|
||||
bool notify);
|
||||
void drainOutboundQueueLocked(Connection* connection);
|
||||
static int handleReceiveCallback(int receiveFd, int events, void* data);
|
||||
static int handleReceiveCallback(int fd, int events, void* data);
|
||||
|
||||
void synthesizeCancelationEventsForAllConnectionsLocked(
|
||||
const CancelationOptions& options);
|
||||
|
||||
Reference in New Issue
Block a user