summaryrefslogtreecommitdiff
path: root/libs/ui
diff options
context:
space:
mode:
Diffstat (limited to 'libs/ui')
-rw-r--r--libs/ui/Android.mk15
-rw-r--r--libs/ui/EventHub.cpp530
-rw-r--r--libs/ui/FramebufferNativeWindow.cpp36
-rw-r--r--libs/ui/GraphicBuffer.cpp22
-rw-r--r--libs/ui/GraphicBufferAllocator.cpp19
-rw-r--r--libs/ui/Input.cpp215
-rw-r--r--libs/ui/InputDispatcher.cpp3188
-rw-r--r--libs/ui/InputManager.cpp83
-rw-r--r--libs/ui/InputReader.cpp3411
-rw-r--r--libs/ui/InputTransport.cpp695
-rw-r--r--libs/ui/PixelFormat.cpp10
-rw-r--r--libs/ui/Rect.cpp8
-rw-r--r--libs/ui/tests/Android.mk48
-rw-r--r--libs/ui/tests/InputChannel_test.cpp158
-rw-r--r--libs/ui/tests/InputDispatcher_test.cpp18
-rw-r--r--libs/ui/tests/InputPublisherAndConsumer_test.cpp471
-rw-r--r--libs/ui/tests/region/Android.mk16
-rw-r--r--libs/ui/tests/region/region.cpp (renamed from libs/ui/tests/region.cpp)0
18 files changed, 8652 insertions, 291 deletions
diff --git a/libs/ui/Android.mk b/libs/ui/Android.mk
index f7acd973ffb4..9f493483bf95 100644
--- a/libs/ui/Android.mk
+++ b/libs/ui/Android.mk
@@ -11,6 +11,11 @@ LOCAL_SRC_FILES:= \
GraphicBufferMapper.cpp \
KeyLayoutMap.cpp \
KeyCharacterMap.cpp \
+ Input.cpp \
+ InputDispatcher.cpp \
+ InputManager.cpp \
+ InputReader.cpp \
+ InputTransport.cpp \
IOverlay.cpp \
Overlay.cpp \
PixelFormat.cpp \
@@ -33,3 +38,13 @@ ifeq ($(TARGET_SIMULATOR),true)
endif
include $(BUILD_SHARED_LIBRARY)
+
+
+# Include subdirectory makefiles
+# ============================================================
+
+# If we're building with ONE_SHOT_MAKEFILE (mm, mmm), then what the framework
+# team really wants is to build the stuff defined by this makefile.
+ifeq (,$(ONE_SHOT_MAKEFILE))
+include $(call first-makefiles-under,$(LOCAL_PATH))
+endif
diff --git a/libs/ui/EventHub.cpp b/libs/ui/EventHub.cpp
index d45eaf0b262e..1d38b4ba8ff0 100644
--- a/libs/ui/EventHub.cpp
+++ b/libs/ui/EventHub.cpp
@@ -54,10 +54,12 @@
*/
#define test_bit(bit, array) (array[bit/8] & (1<<(bit%8)))
+/* this macro computes the number of bytes needed to represent a bit array of the specified size */
+#define sizeof_bit_array(bits) ((bits + 7) / 8)
+
#define ID_MASK 0x0000ffff
#define SEQ_MASK 0x7fff0000
#define SEQ_SHIFT 16
-#define id_to_index(id) ((id&ID_MASK)+1)
#ifndef ABS_MT_TOUCH_MAJOR
#define ABS_MT_TOUCH_MAJOR 0x30 /* Major axis of touching ellipse */
@@ -84,7 +86,7 @@ static inline int max(int v1, int v2)
EventHub::device_t::device_t(int32_t _id, const char* _path, const char* name)
: id(_id), path(_path), name(name), classes(0)
- , keyBitmask(NULL), layoutMap(new KeyLayoutMap()), next(NULL) {
+ , keyBitmask(NULL), layoutMap(new KeyLayoutMap()), fd(-1), next(NULL) {
}
EventHub::device_t::~device_t() {
@@ -97,6 +99,7 @@ EventHub::EventHub(void)
, mDevicesById(0), mNumDevicesById(0)
, mOpeningDevices(0), mClosingDevices(0)
, mDevices(0), mFDs(0), mFDCount(0), mOpened(false)
+ , mInputBufferIndex(0), mInputBufferCount(0), mInputDeviceIndex(0)
{
acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
#ifdef EV_SW
@@ -134,101 +137,71 @@ uint32_t EventHub::getDeviceClasses(int32_t deviceId) const
return device->classes;
}
-int EventHub::getAbsoluteInfo(int32_t deviceId, int axis, int *outMinValue,
- int* outMaxValue, int* outFlat, int* outFuzz) const
-{
+status_t EventHub::getAbsoluteAxisInfo(int32_t deviceId, int axis,
+ RawAbsoluteAxisInfo* outAxisInfo) const {
+ outAxisInfo->clear();
+
AutoMutex _l(mLock);
device_t* device = getDevice(deviceId);
if (device == NULL) return -1;
struct input_absinfo info;
- if(ioctl(mFDs[id_to_index(device->id)].fd, EVIOCGABS(axis), &info)) {
- LOGE("Error reading absolute controller %d for device %s fd %d\n",
- axis, device->name.string(), mFDs[id_to_index(device->id)].fd);
- return -1;
+ if(ioctl(device->fd, EVIOCGABS(axis), &info)) {
+ LOGW("Error reading absolute controller %d for device %s fd %d\n",
+ axis, device->name.string(), device->fd);
+ return -errno;
}
- *outMinValue = info.minimum;
- *outMaxValue = info.maximum;
- *outFlat = info.flat;
- *outFuzz = info.fuzz;
- return 0;
-}
-int EventHub::getSwitchState(int sw) const
-{
-#ifdef EV_SW
- if (sw >= 0 && sw <= SW_MAX) {
- int32_t devid = mSwitches[sw];
- if (devid != 0) {
- return getSwitchState(devid, sw);
- }
+ if (info.minimum != info.maximum) {
+ outAxisInfo->valid = true;
+ outAxisInfo->minValue = info.minimum;
+ outAxisInfo->maxValue = info.maximum;
+ outAxisInfo->flat = info.flat;
+ outAxisInfo->fuzz = info.fuzz;
}
-#endif
- return -1;
+ return OK;
}
-int EventHub::getSwitchState(int32_t deviceId, int sw) const
-{
-#ifdef EV_SW
- AutoMutex _l(mLock);
- device_t* device = getDevice(deviceId);
- if (device == NULL) return -1;
-
- if (sw >= 0 && sw <= SW_MAX) {
- uint8_t sw_bitmask[(SW_MAX+7)/8];
- memset(sw_bitmask, 0, sizeof(sw_bitmask));
- if (ioctl(mFDs[id_to_index(device->id)].fd,
- EVIOCGSW(sizeof(sw_bitmask)), sw_bitmask) >= 0) {
- return test_bit(sw, sw_bitmask) ? 1 : 0;
+int32_t EventHub::getScanCodeState(int32_t deviceId, int32_t scanCode) const {
+ if (scanCode >= 0 && scanCode <= KEY_MAX) {
+ AutoMutex _l(mLock);
+
+ device_t* device = getDevice(deviceId);
+ if (device != NULL) {
+ return getScanCodeStateLocked(device, scanCode);
}
}
-#endif
-
- return -1;
+ return AKEY_STATE_UNKNOWN;
}
-int EventHub::getScancodeState(int code) const
-{
- return getScancodeState(mFirstKeyboardId, code);
+int32_t EventHub::getScanCodeStateLocked(device_t* device, int32_t scanCode) const {
+ uint8_t key_bitmask[sizeof_bit_array(KEY_MAX + 1)];
+ memset(key_bitmask, 0, sizeof(key_bitmask));
+ if (ioctl(device->fd,
+ EVIOCGKEY(sizeof(key_bitmask)), key_bitmask) >= 0) {
+ return test_bit(scanCode, key_bitmask) ? AKEY_STATE_DOWN : AKEY_STATE_UP;
+ }
+ return AKEY_STATE_UNKNOWN;
}
-int EventHub::getScancodeState(int32_t deviceId, int code) const
-{
+int32_t EventHub::getKeyCodeState(int32_t deviceId, int32_t keyCode) const {
AutoMutex _l(mLock);
+
device_t* device = getDevice(deviceId);
- if (device == NULL) return -1;
-
- if (code >= 0 && code <= KEY_MAX) {
- uint8_t key_bitmask[(KEY_MAX+7)/8];
- memset(key_bitmask, 0, sizeof(key_bitmask));
- if (ioctl(mFDs[id_to_index(device->id)].fd,
- EVIOCGKEY(sizeof(key_bitmask)), key_bitmask) >= 0) {
- return test_bit(code, key_bitmask) ? 1 : 0;
- }
+ if (device != NULL) {
+ return getKeyCodeStateLocked(device, keyCode);
}
-
- return -1;
+ return AKEY_STATE_UNKNOWN;
}
-int EventHub::getKeycodeState(int code) const
-{
- return getKeycodeState(mFirstKeyboardId, code);
-}
-
-int EventHub::getKeycodeState(int32_t deviceId, int code) const
-{
- AutoMutex _l(mLock);
- device_t* device = getDevice(deviceId);
- if (device == NULL || device->layoutMap == NULL) return -1;
-
+int32_t EventHub::getKeyCodeStateLocked(device_t* device, int32_t keyCode) const {
Vector<int32_t> scanCodes;
- device->layoutMap->findScancodes(code, &scanCodes);
-
- uint8_t key_bitmask[(KEY_MAX+7)/8];
+ device->layoutMap->findScancodes(keyCode, &scanCodes);
+
+ uint8_t key_bitmask[sizeof_bit_array(KEY_MAX + 1)];
memset(key_bitmask, 0, sizeof(key_bitmask));
- if (ioctl(mFDs[id_to_index(device->id)].fd,
- EVIOCGKEY(sizeof(key_bitmask)), key_bitmask) >= 0) {
+ if (ioctl(device->fd, EVIOCGKEY(sizeof(key_bitmask)), key_bitmask) >= 0) {
#if 0
for (size_t i=0; i<=KEY_MAX; i++) {
LOGI("(Scan code %d: down=%d)", i, test_bit(i, key_bitmask));
@@ -239,12 +212,72 @@ int EventHub::getKeycodeState(int32_t deviceId, int code) const
int32_t sc = scanCodes.itemAt(i);
//LOGI("Code %d: down=%d", sc, test_bit(sc, key_bitmask));
if (sc >= 0 && sc <= KEY_MAX && test_bit(sc, key_bitmask)) {
- return 1;
+ return AKEY_STATE_DOWN;
}
}
+ return AKEY_STATE_UP;
}
-
- return 0;
+ return AKEY_STATE_UNKNOWN;
+}
+
+int32_t EventHub::getSwitchState(int32_t deviceId, int32_t sw) const {
+#ifdef EV_SW
+ if (sw >= 0 && sw <= SW_MAX) {
+ AutoMutex _l(mLock);
+
+ device_t* device = getDevice(deviceId);
+ if (device != NULL) {
+ return getSwitchStateLocked(device, sw);
+ }
+ }
+#endif
+ return AKEY_STATE_UNKNOWN;
+}
+
+int32_t EventHub::getSwitchStateLocked(device_t* device, int32_t sw) const {
+ uint8_t sw_bitmask[sizeof_bit_array(SW_MAX + 1)];
+ memset(sw_bitmask, 0, sizeof(sw_bitmask));
+ if (ioctl(device->fd,
+ EVIOCGSW(sizeof(sw_bitmask)), sw_bitmask) >= 0) {
+ return test_bit(sw, sw_bitmask) ? AKEY_STATE_DOWN : AKEY_STATE_UP;
+ }
+ return AKEY_STATE_UNKNOWN;
+}
+
+bool EventHub::markSupportedKeyCodes(int32_t deviceId, size_t numCodes,
+ const int32_t* keyCodes, uint8_t* outFlags) const {
+ AutoMutex _l(mLock);
+
+ device_t* device = getDevice(deviceId);
+ if (device != NULL) {
+ return markSupportedKeyCodesLocked(device, numCodes, keyCodes, outFlags);
+ }
+ return false;
+}
+
+bool EventHub::markSupportedKeyCodesLocked(device_t* device, size_t numCodes,
+ const int32_t* keyCodes, uint8_t* outFlags) const {
+ if (device->layoutMap == NULL || device->keyBitmask == NULL) {
+ return false;
+ }
+
+ Vector<int32_t> scanCodes;
+ for (size_t codeIndex = 0; codeIndex < numCodes; codeIndex++) {
+ scanCodes.clear();
+
+ status_t err = device->layoutMap->findScancodes(keyCodes[codeIndex], &scanCodes);
+ if (! err) {
+ // check the possible scan codes identified by the layout map against the
+ // map of codes actually emitted by the driver
+ for (size_t sc = 0; sc < scanCodes.size(); sc++) {
+ if (test_bit(scanCodes[sc], device->keyBitmask)) {
+ outFlags[codeIndex] = 1;
+ break;
+ }
+ }
+ }
+ }
+ return true;
}
status_t EventHub::scancodeToKeycode(int32_t deviceId, int scancode,
@@ -295,27 +328,15 @@ EventHub::device_t* EventHub::getDevice(int32_t deviceId) const
return NULL;
}
-bool EventHub::getEvent(int32_t* outDeviceId, int32_t* outType,
- int32_t* outScancode, int32_t* outKeycode, uint32_t *outFlags,
- int32_t* outValue, nsecs_t* outWhen)
+bool EventHub::getEvent(RawEvent* outEvent)
{
- *outDeviceId = 0;
- *outType = 0;
- *outScancode = 0;
- *outKeycode = 0;
- *outFlags = 0;
- *outValue = 0;
- *outWhen = 0;
-
- status_t err;
-
- fd_set readfds;
- int maxFd = -1;
- int cc;
- int i;
- int res;
- int pollres;
- struct input_event iev;
+ outEvent->deviceId = 0;
+ outEvent->type = 0;
+ outEvent->scanCode = 0;
+ outEvent->keyCode = 0;
+ outEvent->flags = 0;
+ outEvent->value = 0;
+ outEvent->when = 0;
// Note that we only allow one caller to getEvent(), so don't need
// to do locking here... only when adding/removing devices.
@@ -325,93 +346,127 @@ bool EventHub::getEvent(int32_t* outDeviceId, int32_t* outType,
mOpened = true;
}
- while(1) {
-
- // First, report any devices that had last been added/removed.
+ for (;;) {
+ // Report any devices that had last been added/removed.
if (mClosingDevices != NULL) {
device_t* device = mClosingDevices;
LOGV("Reporting device closed: id=0x%x, name=%s\n",
device->id, device->path.string());
mClosingDevices = device->next;
- *outDeviceId = device->id;
- if (*outDeviceId == mFirstKeyboardId) *outDeviceId = 0;
- *outType = DEVICE_REMOVED;
+ if (device->id == mFirstKeyboardId) {
+ outEvent->deviceId = 0;
+ } else {
+ outEvent->deviceId = device->id;
+ }
+ outEvent->type = DEVICE_REMOVED;
delete device;
return true;
}
+
if (mOpeningDevices != NULL) {
device_t* device = mOpeningDevices;
LOGV("Reporting device opened: id=0x%x, name=%s\n",
device->id, device->path.string());
mOpeningDevices = device->next;
- *outDeviceId = device->id;
- if (*outDeviceId == mFirstKeyboardId) *outDeviceId = 0;
- *outType = DEVICE_ADDED;
+ if (device->id == mFirstKeyboardId) {
+ outEvent->deviceId = 0;
+ } else {
+ outEvent->deviceId = device->id;
+ }
+ outEvent->type = DEVICE_ADDED;
return true;
}
- release_wake_lock(WAKE_LOCK_ID);
-
- pollres = poll(mFDs, mFDCount, -1);
+ // Grab the next input event.
+ for (;;) {
+ // Consume buffered input events, if any.
+ if (mInputBufferIndex < mInputBufferCount) {
+ const struct input_event& iev = mInputBufferData[mInputBufferIndex++];
+ const device_t* device = mDevices[mInputDeviceIndex];
+
+ LOGV("%s got: t0=%d, t1=%d, type=%d, code=%d, v=%d", device->path.string(),
+ (int) iev.time.tv_sec, (int) iev.time.tv_usec, iev.type, iev.code, iev.value);
+ if (device->id == mFirstKeyboardId) {
+ outEvent->deviceId = 0;
+ } else {
+ outEvent->deviceId = device->id;
+ }
+ outEvent->type = iev.type;
+ outEvent->scanCode = iev.code;
+ if (iev.type == EV_KEY) {
+ status_t err = device->layoutMap->map(iev.code,
+ & outEvent->keyCode, & outEvent->flags);
+ LOGV("iev.code=%d keyCode=%d flags=0x%08x err=%d\n",
+ iev.code, outEvent->keyCode, outEvent->flags, err);
+ if (err != 0) {
+ outEvent->keyCode = AKEYCODE_UNKNOWN;
+ outEvent->flags = 0;
+ }
+ } else {
+ outEvent->keyCode = iev.code;
+ }
+ outEvent->value = iev.value;
- acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
+ // Use an event timestamp in the same timebase as
+ // java.lang.System.nanoTime() and android.os.SystemClock.uptimeMillis()
+ // as expected by the rest of the system.
+ outEvent->when = systemTime(SYSTEM_TIME_MONOTONIC);
+ return true;
+ }
- if (pollres <= 0) {
- if (errno != EINTR) {
- LOGW("select failed (errno=%d)\n", errno);
- usleep(100000);
+ // Finish reading all events from devices identified in previous poll().
+ // This code assumes that mInputDeviceIndex is initially 0 and that the
+ // revents member of pollfd is initialized to 0 when the device is first added.
+ // Since mFDs[0] is used for inotify, we process regular events starting at index 1.
+ mInputDeviceIndex += 1;
+ if (mInputDeviceIndex >= mFDCount) {
+ mInputDeviceIndex = 0;
+ break;
}
- continue;
- }
- //printf("poll %d, returned %d\n", mFDCount, pollres);
-
- // mFDs[0] is used for inotify, so process regular events starting at mFDs[1]
- for(i = 1; i < mFDCount; i++) {
- if(mFDs[i].revents) {
- LOGV("revents for %d = 0x%08x", i, mFDs[i].revents);
- if(mFDs[i].revents & POLLIN) {
- res = read(mFDs[i].fd, &iev, sizeof(iev));
- if (res == sizeof(iev)) {
- LOGV("%s got: t0=%d, t1=%d, type=%d, code=%d, v=%d",
- mDevices[i]->path.string(),
- (int) iev.time.tv_sec, (int) iev.time.tv_usec,
- iev.type, iev.code, iev.value);
- *outDeviceId = mDevices[i]->id;
- if (*outDeviceId == mFirstKeyboardId) *outDeviceId = 0;
- *outType = iev.type;
- *outScancode = iev.code;
- if (iev.type == EV_KEY) {
- err = mDevices[i]->layoutMap->map(iev.code, outKeycode, outFlags);
- LOGV("iev.code=%d outKeycode=%d outFlags=0x%08x err=%d\n",
- iev.code, *outKeycode, *outFlags, err);
- if (err != 0) {
- *outKeycode = 0;
- *outFlags = 0;
- }
- } else {
- *outKeycode = iev.code;
- }
- *outValue = iev.value;
- *outWhen = s2ns(iev.time.tv_sec) + us2ns(iev.time.tv_usec);
- return true;
- } else {
- if (res<0) {
- LOGW("could not get event (errno=%d)", errno);
- } else {
- LOGE("could not get event (wrong size: %d)", res);
- }
- continue;
+ const struct pollfd &pfd = mFDs[mInputDeviceIndex];
+ if (pfd.revents & POLLIN) {
+ int32_t readSize = read(pfd.fd, mInputBufferData,
+ sizeof(struct input_event) * INPUT_BUFFER_SIZE);
+ if (readSize < 0) {
+ if (errno != EAGAIN && errno != EINTR) {
+ LOGW("could not get event (errno=%d)", errno);
}
+ } else if ((readSize % sizeof(struct input_event)) != 0) {
+ LOGE("could not get event (wrong size: %d)", readSize);
+ } else {
+ mInputBufferCount = readSize / sizeof(struct input_event);
+ mInputBufferIndex = 0;
}
}
}
-
+
// read_notify() will modify mFDs and mFDCount, so this must be done after
// processing all other events.
if(mFDs[0].revents & POLLIN) {
read_notify(mFDs[0].fd);
}
+
+ // Poll for events. Mind the wake lock dance!
+ // We hold a wake lock at all times except during poll(). This works due to some
+ // subtle choreography. When a device driver has pending (unread) events, it acquires
+ // a kernel wake lock. However, once the last pending event has been read, the device
+ // driver will release the kernel wake lock. To prevent the system from going to sleep
+ // when this happens, the EventHub holds onto its own user wake lock while the client
+ // is processing events. Thus the system can only sleep if there are no events
+ // pending or currently being processed.
+ release_wake_lock(WAKE_LOCK_ID);
+
+ int pollResult = poll(mFDs, mFDCount, -1);
+
+ acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
+
+ if (pollResult <= 0) {
+ if (errno != EINTR) {
+ LOGW("select failed (errno=%d)\n", errno);
+ usleep(100000);
+ }
+ }
}
}
@@ -429,6 +484,7 @@ bool EventHub::openPlatformInput(void)
mFDs = (pollfd *)calloc(1, sizeof(mFDs[0]));
mDevices = (device_t **)calloc(1, sizeof(mDevices[0]));
mFDs[0].events = POLLIN;
+ mFDs[0].revents = 0;
mDevices[0] = NULL;
#ifdef HAVE_INOTIFY
mFDs[0].fd = inotify_init();
@@ -453,37 +509,27 @@ bool EventHub::openPlatformInput(void)
return true;
}
-/*
- * Inspect the known devices to determine whether physical keys exist for the given
- * framework-domain key codes.
- */
-bool EventHub::hasKeys(size_t numCodes, int32_t* keyCodes, uint8_t* outFlags) {
- for (size_t codeIndex = 0; codeIndex < numCodes; codeIndex++) {
- outFlags[codeIndex] = 0;
-
- // check each available hardware device for support for this keycode
- Vector<int32_t> scanCodes;
- for (int n = 0; (n < mFDCount) && (outFlags[codeIndex] == 0); n++) {
- if (mDevices[n]) {
- status_t err = mDevices[n]->layoutMap->findScancodes(keyCodes[codeIndex], &scanCodes);
- if (!err) {
- // check the possible scan codes identified by the layout map against the
- // map of codes actually emitted by the driver
- for (size_t sc = 0; sc < scanCodes.size(); sc++) {
- if (test_bit(scanCodes[sc], mDevices[n]->keyBitmask)) {
- outFlags[codeIndex] = 1;
- break;
- }
- }
- }
- }
+// ----------------------------------------------------------------------------
+
+static bool containsNonZeroByte(const uint8_t* array, uint32_t startIndex, uint32_t endIndex) {
+ const uint8_t* end = array + endIndex;
+ array += startIndex;
+ while (array != end) {
+ if (*(array++) != 0) {
+ return true;
}
}
-
- return true;
+ return false;
}
-// ----------------------------------------------------------------------------
+static const int32_t GAMEPAD_KEYCODES[] = {
+ AKEYCODE_BUTTON_A, AKEYCODE_BUTTON_B, AKEYCODE_BUTTON_C,
+ AKEYCODE_BUTTON_X, AKEYCODE_BUTTON_Y, AKEYCODE_BUTTON_Z,
+ AKEYCODE_BUTTON_L1, AKEYCODE_BUTTON_R1,
+ AKEYCODE_BUTTON_L2, AKEYCODE_BUTTON_R2,
+ AKEYCODE_BUTTON_THUMBL, AKEYCODE_BUTTON_THUMBR,
+ AKEYCODE_BUTTON_START, AKEYCODE_BUTTON_SELECT, AKEYCODE_BUTTON_MODE
+};
int EventHub::open_device(const char *deviceName)
{
@@ -531,7 +577,6 @@ int EventHub::open_device(const char *deviceName)
if (strcmp(name, test) == 0) {
LOGI("ignoring event id %s driver %s\n", deviceName, test);
close(fd);
- fd = -1;
return -1;
}
}
@@ -545,6 +590,12 @@ int EventHub::open_device(const char *deviceName)
idstr[0] = '\0';
}
+ if (fcntl(fd, F_SETFL, O_NONBLOCK)) {
+ LOGE("Error %d making device file descriptor non-blocking.", errno);
+ close(fd);
+ return -1;
+ }
+
int devid = 0;
while (devid < mNumDevicesById) {
if (mDevicesById[devid].device == NULL) {
@@ -599,30 +650,32 @@ int EventHub::open_device(const char *deviceName)
return -1;
}
+ device->fd = fd;
mFDs[mFDCount].fd = fd;
mFDs[mFDCount].events = POLLIN;
+ mFDs[mFDCount].revents = 0;
- // figure out the kinds of events the device reports
+ // Figure out the kinds of events the device reports.
- // See if this is a keyboard, and classify it. Note that we only
- // consider up through the function keys; we don't want to include
- // ones after that (play cd etc) so we don't mistakenly consider a
- // controller to be a keyboard.
- uint8_t key_bitmask[(KEY_MAX+7)/8];
+ uint8_t key_bitmask[sizeof_bit_array(KEY_MAX + 1)];
memset(key_bitmask, 0, sizeof(key_bitmask));
+
LOGV("Getting keys...");
if (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(key_bitmask)), key_bitmask) >= 0) {
//LOGI("MAP\n");
- //for (int i=0; i<((KEY_MAX+7)/8); i++) {
+ //for (int i = 0; i < sizeof(key_bitmask); i++) {
// LOGI("%d: 0x%02x\n", i, key_bitmask[i]);
//}
- for (int i=0; i<((BTN_MISC+7)/8); i++) {
- if (key_bitmask[i] != 0) {
- device->classes |= CLASS_KEYBOARD;
- break;
- }
- }
- if ((device->classes & CLASS_KEYBOARD) != 0) {
+
+ // See if this is a keyboard. Ignore everything in the button range except for
+ // gamepads which are also considered keyboards.
+ if (containsNonZeroByte(key_bitmask, 0, sizeof_bit_array(BTN_MISC))
+ || containsNonZeroByte(key_bitmask, sizeof_bit_array(BTN_GAMEPAD),
+ sizeof_bit_array(BTN_DIGI))
+ || containsNonZeroByte(key_bitmask, sizeof_bit_array(KEY_OK),
+ sizeof_bit_array(KEY_MAX + 1))) {
+ device->classes |= INPUT_DEVICE_CLASS_KEYBOARD;
+
device->keyBitmask = new uint8_t[sizeof(key_bitmask)];
if (device->keyBitmask != NULL) {
memcpy(device->keyBitmask, key_bitmask, sizeof(key_bitmask));
@@ -634,53 +687,57 @@ int EventHub::open_device(const char *deviceName)
}
}
- // See if this is a trackball.
+ // See if this is a trackball (or mouse).
if (test_bit(BTN_MOUSE, key_bitmask)) {
- uint8_t rel_bitmask[(REL_MAX+7)/8];
+ uint8_t rel_bitmask[sizeof_bit_array(REL_MAX + 1)];
memset(rel_bitmask, 0, sizeof(rel_bitmask));
LOGV("Getting relative controllers...");
- if (ioctl(fd, EVIOCGBIT(EV_REL, sizeof(rel_bitmask)), rel_bitmask) >= 0)
- {
+ if (ioctl(fd, EVIOCGBIT(EV_REL, sizeof(rel_bitmask)), rel_bitmask) >= 0) {
if (test_bit(REL_X, rel_bitmask) && test_bit(REL_Y, rel_bitmask)) {
- device->classes |= CLASS_TRACKBALL;
+ device->classes |= INPUT_DEVICE_CLASS_TRACKBALL;
}
}
}
-
- uint8_t abs_bitmask[(ABS_MAX+7)/8];
+
+ // See if this is a touch pad.
+ uint8_t abs_bitmask[sizeof_bit_array(ABS_MAX + 1)];
memset(abs_bitmask, 0, sizeof(abs_bitmask));
LOGV("Getting absolute controllers...");
- ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(abs_bitmask)), abs_bitmask);
-
- // Is this a new modern multi-touch driver?
- if (test_bit(ABS_MT_TOUCH_MAJOR, abs_bitmask)
- && test_bit(ABS_MT_POSITION_X, abs_bitmask)
- && test_bit(ABS_MT_POSITION_Y, abs_bitmask)) {
- device->classes |= CLASS_TOUCHSCREEN | CLASS_TOUCHSCREEN_MT;
-
- // Is this an old style single-touch driver?
- } else if (test_bit(BTN_TOUCH, key_bitmask)
- && test_bit(ABS_X, abs_bitmask) && test_bit(ABS_Y, abs_bitmask)) {
- device->classes |= CLASS_TOUCHSCREEN;
+ if (ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(abs_bitmask)), abs_bitmask) >= 0) {
+ // Is this a new modern multi-touch driver?
+ if (test_bit(ABS_MT_POSITION_X, abs_bitmask)
+ && test_bit(ABS_MT_POSITION_Y, abs_bitmask)) {
+ device->classes |= INPUT_DEVICE_CLASS_TOUCHSCREEN | INPUT_DEVICE_CLASS_TOUCHSCREEN_MT;
+
+ // Is this an old style single-touch driver?
+ } else if (test_bit(BTN_TOUCH, key_bitmask)
+ && test_bit(ABS_X, abs_bitmask) && test_bit(ABS_Y, abs_bitmask)) {
+ device->classes |= INPUT_DEVICE_CLASS_TOUCHSCREEN;
+ }
}
#ifdef EV_SW
// figure out the switches this device reports
- uint8_t sw_bitmask[(SW_MAX+7)/8];
+ uint8_t sw_bitmask[sizeof_bit_array(SW_MAX + 1)];
memset(sw_bitmask, 0, sizeof(sw_bitmask));
+ bool hasSwitches = false;
if (ioctl(fd, EVIOCGBIT(EV_SW, sizeof(sw_bitmask)), sw_bitmask) >= 0) {
for (int i=0; i<EV_SW; i++) {
//LOGI("Device 0x%x sw %d: has=%d", device->id, i, test_bit(i, sw_bitmask));
if (test_bit(i, sw_bitmask)) {
+ hasSwitches = true;
if (mSwitches[i] == 0) {
mSwitches[i] = device->id;
}
}
}
}
+ if (hasSwitches) {
+ device->classes |= INPUT_DEVICE_CLASS_SWITCH;
+ }
#endif
- if ((device->classes&CLASS_KEYBOARD) != 0) {
+ if ((device->classes & INPUT_DEVICE_CLASS_KEYBOARD) != 0) {
char tmpfn[sizeof(name)];
char keylayoutFilename[300];
@@ -702,7 +759,10 @@ int EventHub::open_device(const char *deviceName)
"%s/usr/keylayout/%s", root, "qwerty.kl");
defaultKeymap = true;
}
- device->layoutMap->load(keylayoutFilename);
+ status_t status = device->layoutMap->load(keylayoutFilename);
+ if (status) {
+ LOGE("Error %d loading key layout.", status);
+ }
// tell the world about the devname (the descriptive name)
if (!mHaveFirstKeyboard && !defaultKeymap && strstr(name, "-keypad")) {
@@ -722,23 +782,39 @@ int EventHub::open_device(const char *deviceName)
property_set(propName, name);
// 'Q' key support = cheap test of whether this is an alpha-capable kbd
- if (hasKeycode(device, kKeyCodeQ)) {
- device->classes |= CLASS_ALPHAKEY;
+ if (hasKeycode(device, AKEYCODE_Q)) {
+ device->classes |= INPUT_DEVICE_CLASS_ALPHAKEY;
}
- // See if this has a DPAD.
- if (hasKeycode(device, kKeyCodeDpadUp) &&
- hasKeycode(device, kKeyCodeDpadDown) &&
- hasKeycode(device, kKeyCodeDpadLeft) &&
- hasKeycode(device, kKeyCodeDpadRight) &&
- hasKeycode(device, kKeyCodeDpadCenter)) {
- device->classes |= CLASS_DPAD;
+ // See if this device has a DPAD.
+ if (hasKeycode(device, AKEYCODE_DPAD_UP) &&
+ hasKeycode(device, AKEYCODE_DPAD_DOWN) &&
+ hasKeycode(device, AKEYCODE_DPAD_LEFT) &&
+ hasKeycode(device, AKEYCODE_DPAD_RIGHT) &&
+ hasKeycode(device, AKEYCODE_DPAD_CENTER)) {
+ device->classes |= INPUT_DEVICE_CLASS_DPAD;
}
+ // See if this device has a gamepad.
+ for (size_t i = 0; i < sizeof(GAMEPAD_KEYCODES); i++) {
+ if (hasKeycode(device, GAMEPAD_KEYCODES[i])) {
+ device->classes |= INPUT_DEVICE_CLASS_GAMEPAD;
+ break;
+ }
+ }
+
LOGI("New keyboard: device->id=0x%x devname='%s' propName='%s' keylayout='%s'\n",
device->id, name, propName, keylayoutFilename);
}
+ // If the device isn't recognized as something we handle, don't monitor it.
+ if (device->classes == 0) {
+ LOGV("Dropping device %s %p, id = %d\n", deviceName, device, devid);
+ close(fd);
+ delete device;
+ return -1;
+ }
+
LOGI("New device: path=%s name=%s id=0x%x (of 0x%x) index=%d fd=%d classes=0x%x\n",
deviceName, name, device->id, mNumDevicesById, mFDCount, fd, device->classes);
diff --git a/libs/ui/FramebufferNativeWindow.cpp b/libs/ui/FramebufferNativeWindow.cpp
index 52380a077c7b..6f8948d70e8d 100644
--- a/libs/ui/FramebufferNativeWindow.cpp
+++ b/libs/ui/FramebufferNativeWindow.cpp
@@ -67,7 +67,7 @@ private:
* This implements the (main) framebuffer management. This class is used
* mostly by SurfaceFlinger, but also by command line GL application.
*
- * In fact this is an implementation of android_native_window_t on top of
+ * In fact this is an implementation of ANativeWindow on top of
* the framebuffer.
*
* Currently it is pretty simple, it manages only two buffers (the front and
@@ -117,23 +117,23 @@ FramebufferNativeWindow::FramebufferNativeWindow()
LOGE_IF(err, "fb buffer 1 allocation failed w=%d, h=%d, err=%s",
fbDev->width, fbDev->height, strerror(-err));
- const_cast<uint32_t&>(android_native_window_t::flags) = fbDev->flags;
- const_cast<float&>(android_native_window_t::xdpi) = fbDev->xdpi;
- const_cast<float&>(android_native_window_t::ydpi) = fbDev->ydpi;
- const_cast<int&>(android_native_window_t::minSwapInterval) =
+ const_cast<uint32_t&>(ANativeWindow::flags) = fbDev->flags;
+ const_cast<float&>(ANativeWindow::xdpi) = fbDev->xdpi;
+ const_cast<float&>(ANativeWindow::ydpi) = fbDev->ydpi;
+ const_cast<int&>(ANativeWindow::minSwapInterval) =
fbDev->minSwapInterval;
- const_cast<int&>(android_native_window_t::maxSwapInterval) =
+ const_cast<int&>(ANativeWindow::maxSwapInterval) =
fbDev->maxSwapInterval;
} else {
LOGE("Couldn't get gralloc module");
}
- android_native_window_t::setSwapInterval = setSwapInterval;
- android_native_window_t::dequeueBuffer = dequeueBuffer;
- android_native_window_t::lockBuffer = lockBuffer;
- android_native_window_t::queueBuffer = queueBuffer;
- android_native_window_t::query = query;
- android_native_window_t::perform = perform;
+ ANativeWindow::setSwapInterval = setSwapInterval;
+ ANativeWindow::dequeueBuffer = dequeueBuffer;
+ ANativeWindow::lockBuffer = lockBuffer;
+ ANativeWindow::queueBuffer = queueBuffer;
+ ANativeWindow::query = query;
+ ANativeWindow::perform = perform;
}
FramebufferNativeWindow::~FramebufferNativeWindow()
@@ -168,13 +168,13 @@ status_t FramebufferNativeWindow::compositionComplete()
}
int FramebufferNativeWindow::setSwapInterval(
- android_native_window_t* window, int interval)
+ ANativeWindow* window, int interval)
{
framebuffer_device_t* fb = getSelf(window)->fbDev;
return fb->setSwapInterval(fb, interval);
}
-int FramebufferNativeWindow::dequeueBuffer(android_native_window_t* window,
+int FramebufferNativeWindow::dequeueBuffer(ANativeWindow* window,
android_native_buffer_t** buffer)
{
FramebufferNativeWindow* self = getSelf(window);
@@ -196,7 +196,7 @@ int FramebufferNativeWindow::dequeueBuffer(android_native_window_t* window,
return 0;
}
-int FramebufferNativeWindow::lockBuffer(android_native_window_t* window,
+int FramebufferNativeWindow::lockBuffer(ANativeWindow* window,
android_native_buffer_t* buffer)
{
FramebufferNativeWindow* self = getSelf(window);
@@ -210,7 +210,7 @@ int FramebufferNativeWindow::lockBuffer(android_native_window_t* window,
return NO_ERROR;
}
-int FramebufferNativeWindow::queueBuffer(android_native_window_t* window,
+int FramebufferNativeWindow::queueBuffer(ANativeWindow* window,
android_native_buffer_t* buffer)
{
FramebufferNativeWindow* self = getSelf(window);
@@ -224,7 +224,7 @@ int FramebufferNativeWindow::queueBuffer(android_native_window_t* window,
return res;
}
-int FramebufferNativeWindow::query(android_native_window_t* window,
+int FramebufferNativeWindow::query(ANativeWindow* window,
int what, int* value)
{
FramebufferNativeWindow* self = getSelf(window);
@@ -245,7 +245,7 @@ int FramebufferNativeWindow::query(android_native_window_t* window,
return BAD_VALUE;
}
-int FramebufferNativeWindow::perform(android_native_window_t* window,
+int FramebufferNativeWindow::perform(ANativeWindow* window,
int operation, ...)
{
switch (operation) {
diff --git a/libs/ui/GraphicBuffer.cpp b/libs/ui/GraphicBuffer.cpp
index ba1fd9c9dcfe..519c277c603a 100644
--- a/libs/ui/GraphicBuffer.cpp
+++ b/libs/ui/GraphicBuffer.cpp
@@ -38,7 +38,7 @@ namespace android {
GraphicBuffer::GraphicBuffer()
: BASE(), mOwner(ownData), mBufferMapper(GraphicBufferMapper::get()),
- mInitCheck(NO_ERROR), mVStride(0), mIndex(-1)
+ mInitCheck(NO_ERROR), mIndex(-1)
{
width =
height =
@@ -51,7 +51,7 @@ GraphicBuffer::GraphicBuffer()
GraphicBuffer::GraphicBuffer(uint32_t w, uint32_t h,
PixelFormat reqFormat, uint32_t reqUsage)
: BASE(), mOwner(ownData), mBufferMapper(GraphicBufferMapper::get()),
- mInitCheck(NO_ERROR), mVStride(0), mIndex(-1)
+ mInitCheck(NO_ERROR), mIndex(-1)
{
width =
height =
@@ -67,7 +67,7 @@ GraphicBuffer::GraphicBuffer(uint32_t w, uint32_t h,
uint32_t inStride, native_handle_t* inHandle, bool keepOwnership)
: BASE(), mOwner(keepOwnership ? ownHandle : ownNone),
mBufferMapper(GraphicBufferMapper::get()),
- mInitCheck(NO_ERROR), mVStride(0), mIndex(-1)
+ mInitCheck(NO_ERROR), mIndex(-1)
{
width = w;
height = h;
@@ -111,6 +111,9 @@ status_t GraphicBuffer::reallocate(uint32_t w, uint32_t h, PixelFormat f,
if (mOwner != ownData)
return INVALID_OPERATION;
+ if (handle && w==width && h==height && f==format && reqUsage==usage)
+ return NO_ERROR;
+
if (handle) {
GraphicBufferAllocator& allocator(GraphicBufferAllocator::get());
allocator.free(handle);
@@ -122,9 +125,6 @@ status_t GraphicBuffer::reallocate(uint32_t w, uint32_t h, PixelFormat f,
status_t GraphicBuffer::initSize(uint32_t w, uint32_t h, PixelFormat format,
uint32_t reqUsage)
{
- if (format == PIXEL_FORMAT_RGBX_8888)
- format = PIXEL_FORMAT_RGBA_8888;
-
GraphicBufferAllocator& allocator = GraphicBufferAllocator::get();
status_t err = allocator.alloc(w, h, format, reqUsage, &handle, &stride);
if (err == NO_ERROR) {
@@ -132,7 +132,6 @@ status_t GraphicBuffer::initSize(uint32_t w, uint32_t h, PixelFormat format,
this->height = h;
this->format = format;
this->usage = reqUsage;
- mVStride = 0;
}
return err;
}
@@ -173,7 +172,6 @@ status_t GraphicBuffer::lock(GGLSurface* sur, uint32_t usage)
sur->height = height;
sur->stride = stride;
sur->format = format;
- sur->vstride = mVStride;
sur->data = static_cast<GGLubyte*>(vaddr);
}
return res;
@@ -267,14 +265,6 @@ int GraphicBuffer::getIndex() const {
return mIndex;
}
-void GraphicBuffer::setVerticalStride(uint32_t vstride) {
- mVStride = vstride;
-}
-
-uint32_t GraphicBuffer::getVerticalStride() const {
- return mVStride;
-}
-
// ---------------------------------------------------------------------------
}; // namespace android
diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp
index 6ae7e741b624..d51664dded09 100644
--- a/libs/ui/GraphicBufferAllocator.cpp
+++ b/libs/ui/GraphicBufferAllocator.cpp
@@ -15,6 +15,8 @@
** limitations under the License.
*/
+#define LOG_TAG "GraphicBufferAllocator"
+
#include <cutils/log.h>
#include <utils/Singleton.h>
@@ -61,9 +63,9 @@ void GraphicBufferAllocator::dump(String8& result) const
const size_t c = list.size();
for (size_t i=0 ; i<c ; i++) {
const alloc_rec_t& rec(list.valueAt(i));
- snprintf(buffer, SIZE, "%10p: %7.2f KiB | %4u x %4u | %2d | 0x%08x\n",
+ snprintf(buffer, SIZE, "%10p: %7.2f KiB | %4u (%4u) x %4u | %2d | 0x%08x\n",
list.keyAt(i), rec.size/1024.0f,
- rec.w, rec.h, rec.format, rec.usage);
+ rec.w, rec.s, rec.h, rec.format, rec.usage);
result.append(buffer);
total += rec.size;
}
@@ -71,16 +73,13 @@ void GraphicBufferAllocator::dump(String8& result) const
result.append(buffer);
}
-static inline uint32_t clamp(uint32_t c) {
- return c>0 ? c : 1;
-}
-
status_t GraphicBufferAllocator::alloc(uint32_t w, uint32_t h, PixelFormat format,
int usage, buffer_handle_t* handle, int32_t* stride)
{
- // make sure to not allocate a 0 x 0 buffer
- w = clamp(w);
- h = clamp(h);
+ // make sure to not allocate a N x 0 or 0 x N buffer, since this is
+ // allowed from an API stand-point allocate a 1x1 buffer instead.
+ if (!w || !h)
+ w = h = 1;
// we have a h/w allocator and h/w buffer is requested
status_t err;
@@ -100,9 +99,9 @@ status_t GraphicBufferAllocator::alloc(uint32_t w, uint32_t h, PixelFormat forma
alloc_rec_t rec;
rec.w = w;
rec.h = h;
+ rec.s = *stride;
rec.format = format;
rec.usage = usage;
- rec.vaddr = 0;
rec.size = h * stride[0] * bytesPerPixel(format);
list.add(*handle, rec);
} else {
diff --git a/libs/ui/Input.cpp b/libs/ui/Input.cpp
new file mode 100644
index 000000000000..811edaf8a26f
--- /dev/null
+++ b/libs/ui/Input.cpp
@@ -0,0 +1,215 @@
+//
+// Copyright 2010 The Android Open Source Project
+//
+// Provides a pipe-based transport for native events in the NDK.
+//
+#define LOG_TAG "Input"
+
+//#define LOG_NDEBUG 0
+
+#include <ui/Input.h>
+
+namespace android {
+
+// class InputEvent
+
+void InputEvent::initialize(int32_t deviceId, int32_t source) {
+ mDeviceId = deviceId;
+ mSource = source;
+}
+
+void InputEvent::initialize(const InputEvent& from) {
+ mDeviceId = from.mDeviceId;
+ mSource = from.mSource;
+}
+
+// class KeyEvent
+
+bool KeyEvent::hasDefaultAction(int32_t keyCode) {
+ switch (keyCode) {
+ case AKEYCODE_HOME:
+ case AKEYCODE_BACK:
+ case AKEYCODE_CALL:
+ case AKEYCODE_ENDCALL:
+ case AKEYCODE_VOLUME_UP:
+ case AKEYCODE_VOLUME_DOWN:
+ case AKEYCODE_POWER:
+ case AKEYCODE_CAMERA:
+ case AKEYCODE_HEADSETHOOK:
+ case AKEYCODE_MENU:
+ case AKEYCODE_NOTIFICATION:
+ case AKEYCODE_FOCUS:
+ case AKEYCODE_SEARCH:
+ case AKEYCODE_MEDIA_PLAY_PAUSE:
+ case AKEYCODE_MEDIA_STOP:
+ case AKEYCODE_MEDIA_NEXT:
+ case AKEYCODE_MEDIA_PREVIOUS:
+ case AKEYCODE_MEDIA_REWIND:
+ case AKEYCODE_MEDIA_FAST_FORWARD:
+ case AKEYCODE_MUTE:
+ return true;
+ }
+
+ return false;
+}
+
+bool KeyEvent::hasDefaultAction() const {
+ return hasDefaultAction(getKeyCode());
+}
+
+bool KeyEvent::isSystemKey(int32_t keyCode) {
+ switch (keyCode) {
+ case AKEYCODE_MENU:
+ case AKEYCODE_SOFT_RIGHT:
+ case AKEYCODE_HOME:
+ case AKEYCODE_BACK:
+ case AKEYCODE_CALL:
+ case AKEYCODE_ENDCALL:
+ case AKEYCODE_VOLUME_UP:
+ case AKEYCODE_VOLUME_DOWN:
+ case AKEYCODE_MUTE:
+ case AKEYCODE_POWER:
+ case AKEYCODE_HEADSETHOOK:
+ case AKEYCODE_MEDIA_PLAY_PAUSE:
+ case AKEYCODE_MEDIA_STOP:
+ case AKEYCODE_MEDIA_NEXT:
+ case AKEYCODE_MEDIA_PREVIOUS:
+ case AKEYCODE_MEDIA_REWIND:
+ case AKEYCODE_MEDIA_FAST_FORWARD:
+ case AKEYCODE_CAMERA:
+ case AKEYCODE_FOCUS:
+ case AKEYCODE_SEARCH:
+ return true;
+ }
+
+ return false;
+}
+
+bool KeyEvent::isSystemKey() const {
+ return isSystemKey(getKeyCode());
+}
+
+void KeyEvent::initialize(
+ int32_t deviceId,
+ int32_t source,
+ int32_t action,
+ int32_t flags,
+ int32_t keyCode,
+ int32_t scanCode,
+ int32_t metaState,
+ int32_t repeatCount,
+ nsecs_t downTime,
+ nsecs_t eventTime) {
+ InputEvent::initialize(deviceId, source);
+ mAction = action;
+ mFlags = flags;
+ mKeyCode = keyCode;
+ mScanCode = scanCode;
+ mMetaState = metaState;
+ mRepeatCount = repeatCount;
+ mDownTime = downTime;
+ mEventTime = eventTime;
+}
+
+void KeyEvent::initialize(const KeyEvent& from) {
+ InputEvent::initialize(from);
+ mAction = from.mAction;
+ mFlags = from.mFlags;
+ mKeyCode = from.mKeyCode;
+ mScanCode = from.mScanCode;
+ mMetaState = from.mMetaState;
+ mRepeatCount = from.mRepeatCount;
+ mDownTime = from.mDownTime;
+ mEventTime = from.mEventTime;
+}
+
+// class MotionEvent
+
+void MotionEvent::initialize(
+ int32_t deviceId,
+ int32_t source,
+ int32_t action,
+ int32_t flags,
+ int32_t edgeFlags,
+ int32_t metaState,
+ float xOffset,
+ float yOffset,
+ float xPrecision,
+ float yPrecision,
+ nsecs_t downTime,
+ nsecs_t eventTime,
+ size_t pointerCount,
+ const int32_t* pointerIds,
+ const PointerCoords* pointerCoords) {
+ InputEvent::initialize(deviceId, source);
+ mAction = action;
+ mFlags = flags;
+ mEdgeFlags = edgeFlags;
+ mMetaState = metaState;
+ mXOffset = xOffset;
+ mYOffset = yOffset;
+ mXPrecision = xPrecision;
+ mYPrecision = yPrecision;
+ mDownTime = downTime;
+ mPointerIds.clear();
+ mPointerIds.appendArray(pointerIds, pointerCount);
+ mSampleEventTimes.clear();
+ mSamplePointerCoords.clear();
+ addSample(eventTime, pointerCoords);
+}
+
+void MotionEvent::addSample(
+ int64_t eventTime,
+ const PointerCoords* pointerCoords) {
+ mSampleEventTimes.push(eventTime);
+ mSamplePointerCoords.appendArray(pointerCoords, getPointerCount());
+}
+
+void MotionEvent::offsetLocation(float xOffset, float yOffset) {
+ mXOffset += xOffset;
+ mYOffset += yOffset;
+}
+
+// class InputDeviceInfo
+
+InputDeviceInfo::InputDeviceInfo() {
+ initialize(-1, String8("uninitialized device info"));
+}
+
+InputDeviceInfo::InputDeviceInfo(const InputDeviceInfo& other) :
+ mId(other.mId), mName(other.mName), mSources(other.mSources),
+ mKeyboardType(other.mKeyboardType),
+ mMotionRanges(other.mMotionRanges) {
+}
+
+InputDeviceInfo::~InputDeviceInfo() {
+}
+
+void InputDeviceInfo::initialize(int32_t id, const String8& name) {
+ mId = id;
+ mName = name;
+ mSources = 0;
+ mKeyboardType = AINPUT_KEYBOARD_TYPE_NONE;
+ mMotionRanges.clear();
+}
+
+const InputDeviceInfo::MotionRange* InputDeviceInfo::getMotionRange(int32_t rangeType) const {
+ ssize_t index = mMotionRanges.indexOfKey(rangeType);
+ return index >= 0 ? & mMotionRanges.valueAt(index) : NULL;
+}
+
+void InputDeviceInfo::addSource(uint32_t source) {
+ mSources |= source;
+}
+
+void InputDeviceInfo::addMotionRange(int32_t rangeType, float min, float max,
+ float flat, float fuzz) {
+ MotionRange range = { min, max, flat, fuzz };
+ addMotionRange(rangeType, range);
+}
+
+void InputDeviceInfo::addMotionRange(int32_t rangeType, const MotionRange& range) {
+ mMotionRanges.add(rangeType, range);
+}
+
+} // namespace android
diff --git a/libs/ui/InputDispatcher.cpp b/libs/ui/InputDispatcher.cpp
new file mode 100644
index 000000000000..b8a26b04f0e1
--- /dev/null
+++ b/libs/ui/InputDispatcher.cpp
@@ -0,0 +1,3188 @@
+//
+// Copyright 2010 The Android Open Source Project
+//
+// The input dispatcher.
+//
+#define LOG_TAG "InputDispatcher"
+
+//#define LOG_NDEBUG 0
+
+// Log detailed debug messages about each inbound event notification to the dispatcher.
+#define DEBUG_INBOUND_EVENT_DETAILS 0
+
+// Log detailed debug messages about each outbound event processed by the dispatcher.
+#define DEBUG_OUTBOUND_EVENT_DETAILS 0
+
+// Log debug messages about batching.
+#define DEBUG_BATCHING 0
+
+// Log debug messages about the dispatch cycle.
+#define DEBUG_DISPATCH_CYCLE 0
+
+// Log debug messages about registrations.
+#define DEBUG_REGISTRATION 0
+
+// Log debug messages about performance statistics.
+#define DEBUG_PERFORMANCE_STATISTICS 0
+
+// Log debug messages about input event injection.
+#define DEBUG_INJECTION 0
+
+// Log debug messages about input event throttling.
+#define DEBUG_THROTTLING 0
+
+// Log debug messages about input focus tracking.
+#define DEBUG_FOCUS 0
+
+// Log debug messages about the app switch latency optimization.
+#define DEBUG_APP_SWITCH 0
+
+#include <cutils/log.h>
+#include <ui/InputDispatcher.h>
+#include <ui/PowerManager.h>
+
+#include <stddef.h>
+#include <unistd.h>
+#include <errno.h>
+#include <limits.h>
+
+namespace android {
+
+// Delay between reporting long touch events to the power manager.
+const nsecs_t EVENT_IGNORE_DURATION = 300 * 1000000LL; // 300 ms
+
+// Default input dispatching timeout if there is no focused application or paused window
+// from which to determine an appropriate dispatching timeout.
+const nsecs_t DEFAULT_INPUT_DISPATCHING_TIMEOUT = 5000 * 1000000LL; // 5 sec
+
+// Amount of time to allow for all pending events to be processed when an app switch
+// key is on the way. This is used to preempt input dispatch and drop input events
+// when an application takes too long to respond and the user has pressed an app switch key.
+const nsecs_t APP_SWITCH_TIMEOUT = 500 * 1000000LL; // 0.5sec
+
+
+static inline nsecs_t now() {
+ return systemTime(SYSTEM_TIME_MONOTONIC);
+}
+
+static inline const char* toString(bool value) {
+ return value ? "true" : "false";
+}
+
+
+// --- InputWindow ---
+
+bool InputWindow::visibleFrameIntersects(const InputWindow* other) const {
+ return visibleFrameRight > other->visibleFrameLeft
+ && visibleFrameLeft < other->visibleFrameRight
+ && visibleFrameBottom > other->visibleFrameTop
+ && visibleFrameTop < other->visibleFrameBottom;
+}
+
+bool InputWindow::touchableAreaContainsPoint(int32_t x, int32_t y) const {
+ return x >= touchableAreaLeft && x <= touchableAreaRight
+ && y >= touchableAreaTop && y <= touchableAreaBottom;
+}
+
+
+// --- InputDispatcher ---
+
+InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy) :
+ mPolicy(policy),
+ mPendingEvent(NULL), mAppSwitchDueTime(LONG_LONG_MAX),
+ mDispatchEnabled(true), mDispatchFrozen(false),
+ mFocusedWindow(NULL), mTouchDown(false), mTouchedWindow(NULL),
+ mFocusedApplication(NULL),
+ mCurrentInputTargetsValid(false),
+ mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE) {
+ mPollLoop = new PollLoop(false);
+
+ mInboundQueue.headSentinel.refCount = -1;
+ mInboundQueue.headSentinel.type = EventEntry::TYPE_SENTINEL;
+ mInboundQueue.headSentinel.eventTime = LONG_LONG_MIN;
+
+ mInboundQueue.tailSentinel.refCount = -1;
+ mInboundQueue.tailSentinel.type = EventEntry::TYPE_SENTINEL;
+ mInboundQueue.tailSentinel.eventTime = LONG_LONG_MAX;
+
+ mKeyRepeatState.lastKeyEntry = NULL;
+
+ int32_t maxEventsPerSecond = policy->getMaxEventsPerSecond();
+ mThrottleState.minTimeBetweenEvents = 1000000000LL / maxEventsPerSecond;
+ mThrottleState.lastDeviceId = -1;
+
+#if DEBUG_THROTTLING
+ mThrottleState.originalSampleCount = 0;
+ LOGD("Throttling - Max events per second = %d", maxEventsPerSecond);
+#endif
+}
+
+InputDispatcher::~InputDispatcher() {
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ resetKeyRepeatLocked();
+ releasePendingEventLocked(true);
+ drainInboundQueueLocked();
+ }
+
+ while (mConnectionsByReceiveFd.size() != 0) {
+ unregisterInputChannel(mConnectionsByReceiveFd.valueAt(0)->inputChannel);
+ }
+}
+
+void InputDispatcher::dispatchOnce() {
+ nsecs_t keyRepeatTimeout = mPolicy->getKeyRepeatTimeout();
+ nsecs_t keyRepeatDelay = mPolicy->getKeyRepeatDelay();
+
+ nsecs_t nextWakeupTime = LONG_LONG_MAX;
+ { // acquire lock
+ AutoMutex _l(mLock);
+ dispatchOnceInnerLocked(keyRepeatTimeout, keyRepeatDelay, & nextWakeupTime);
+
+ if (runCommandsLockedInterruptible()) {
+ nextWakeupTime = LONG_LONG_MIN; // force next poll to wake up immediately
+ }
+ } // release lock
+
+ // Wait for callback or timeout or wake. (make sure we round up, not down)
+ nsecs_t currentTime = now();
+ int32_t timeoutMillis;
+ if (nextWakeupTime > currentTime) {
+ uint64_t timeout = uint64_t(nextWakeupTime - currentTime);
+ timeout = (timeout + 999999LL) / 1000000LL;
+ timeoutMillis = timeout > INT_MAX ? -1 : int32_t(timeout);
+ } else {
+ timeoutMillis = 0;
+ }
+
+ mPollLoop->pollOnce(timeoutMillis);
+}
+
+void InputDispatcher::dispatchOnceInnerLocked(nsecs_t keyRepeatTimeout,
+ nsecs_t keyRepeatDelay, nsecs_t* nextWakeupTime) {
+ nsecs_t currentTime = now();
+
+ // Reset the key repeat timer whenever we disallow key events, even if the next event
+ // is not a key. This is to ensure that we abort a key repeat if the device is just coming
+ // out of sleep.
+ if (keyRepeatTimeout < 0) {
+ resetKeyRepeatLocked();
+ }
+
+ // If dispatching is disabled, drop all events in the queue.
+ if (! mDispatchEnabled) {
+ if (mPendingEvent || ! mInboundQueue.isEmpty()) {
+ LOGI("Dropping pending events because input dispatch is disabled.");
+ releasePendingEventLocked(true);
+ drainInboundQueueLocked();
+ }
+ return;
+ }
+
+ // If dispatching is frozen, do not process timeouts or try to deliver any new events.
+ if (mDispatchFrozen) {
+#if DEBUG_FOCUS
+ LOGD("Dispatch frozen. Waiting some more.");
+#endif
+ return;
+ }
+
+ // Optimize latency of app switches.
+ // Essentially we start a short timeout when an app switch key (HOME / ENDCALL) has
+ // been pressed. When it expires, we preempt dispatch and drop all other pending events.
+ bool isAppSwitchDue = mAppSwitchDueTime <= currentTime;
+ if (mAppSwitchDueTime < *nextWakeupTime) {
+ *nextWakeupTime = mAppSwitchDueTime;
+ }
+
+ // Detect and process timeouts for all connections and determine if there are any
+ // synchronous event dispatches pending. This step is entirely non-interruptible.
+ bool havePendingSyncTarget = false;
+ size_t activeConnectionCount = mActiveConnections.size();
+ for (size_t i = 0; i < activeConnectionCount; i++) {
+ Connection* connection = mActiveConnections.itemAt(i);
+
+ if (connection->hasPendingSyncTarget()) {
+ if (isAppSwitchDue) {
+ connection->preemptSyncTarget();
+ } else {
+ havePendingSyncTarget = true;
+ }
+ }
+
+ nsecs_t connectionTimeoutTime = connection->nextTimeoutTime;
+ if (connectionTimeoutTime <= currentTime) {
+ mTimedOutConnections.add(connection);
+ } else if (connectionTimeoutTime < *nextWakeupTime) {
+ *nextWakeupTime = connectionTimeoutTime;
+ }
+ }
+
+ size_t timedOutConnectionCount = mTimedOutConnections.size();
+ for (size_t i = 0; i < timedOutConnectionCount; i++) {
+ Connection* connection = mTimedOutConnections.itemAt(i);
+ timeoutDispatchCycleLocked(currentTime, connection);
+ *nextWakeupTime = LONG_LONG_MIN; // force next poll to wake up immediately
+ }
+ mTimedOutConnections.clear();
+
+ // If we have a pending synchronous target, skip dispatch.
+ if (havePendingSyncTarget) {
+ return;
+ }
+
+ // Ready to start a new event.
+ // If we don't already have a pending event, go grab one.
+ if (! mPendingEvent) {
+ if (mInboundQueue.isEmpty()) {
+ if (isAppSwitchDue) {
+ // The inbound queue is empty so the app switch key we were waiting
+ // for will never arrive. Stop waiting for it.
+ resetPendingAppSwitchLocked(false);
+ isAppSwitchDue = false;
+ }
+
+ // Synthesize a key repeat if appropriate.
+ if (mKeyRepeatState.lastKeyEntry) {
+ if (currentTime >= mKeyRepeatState.nextRepeatTime) {
+ mPendingEvent = synthesizeKeyRepeatLocked(currentTime, keyRepeatDelay);
+ } else {
+ if (mKeyRepeatState.nextRepeatTime < *nextWakeupTime) {
+ *nextWakeupTime = mKeyRepeatState.nextRepeatTime;
+ }
+ }
+ }
+ if (! mPendingEvent) {
+ return;
+ }
+ } else {
+ // Inbound queue has at least one entry.
+ EventEntry* entry = mInboundQueue.headSentinel.next;
+
+ // Throttle the entry if it is a move event and there are no
+ // other events behind it in the queue. Due to movement batching, additional
+ // samples may be appended to this event by the time the throttling timeout
+ // expires.
+ // TODO Make this smarter and consider throttling per device independently.
+ if (entry->type == EventEntry::TYPE_MOTION) {
+ MotionEntry* motionEntry = static_cast<MotionEntry*>(entry);
+ int32_t deviceId = motionEntry->deviceId;
+ uint32_t source = motionEntry->source;
+ if (! isAppSwitchDue
+ && motionEntry->next == & mInboundQueue.tailSentinel // exactly one event
+ && motionEntry->action == AMOTION_EVENT_ACTION_MOVE
+ && deviceId == mThrottleState.lastDeviceId
+ && source == mThrottleState.lastSource) {
+ nsecs_t nextTime = mThrottleState.lastEventTime
+ + mThrottleState.minTimeBetweenEvents;
+ if (currentTime < nextTime) {
+ // Throttle it!
+#if DEBUG_THROTTLING
+ LOGD("Throttling - Delaying motion event for "
+ "device 0x%x, source 0x%08x by up to %0.3fms.",
+ deviceId, source, (nextTime - currentTime) * 0.000001);
+#endif
+ if (nextTime < *nextWakeupTime) {
+ *nextWakeupTime = nextTime;
+ }
+ if (mThrottleState.originalSampleCount == 0) {
+ mThrottleState.originalSampleCount =
+ motionEntry->countSamples();
+ }
+ return;
+ }
+ }
+
+#if DEBUG_THROTTLING
+ if (mThrottleState.originalSampleCount != 0) {
+ uint32_t count = motionEntry->countSamples();
+ LOGD("Throttling - Motion event sample count grew by %d from %d to %d.",
+ count - mThrottleState.originalSampleCount,
+ mThrottleState.originalSampleCount, count);
+ mThrottleState.originalSampleCount = 0;
+ }
+#endif
+
+ mThrottleState.lastEventTime = entry->eventTime < currentTime
+ ? entry->eventTime : currentTime;
+ mThrottleState.lastDeviceId = deviceId;
+ mThrottleState.lastSource = source;
+ }
+
+ mInboundQueue.dequeue(entry);
+ mPendingEvent = entry;
+ }
+ }
+
+ // Now we have an event to dispatch.
+ assert(mPendingEvent != NULL);
+ bool wasDispatched = false;
+ bool wasDropped = false;
+ switch (mPendingEvent->type) {
+ case EventEntry::TYPE_CONFIGURATION_CHANGED: {
+ ConfigurationChangedEntry* typedEntry =
+ static_cast<ConfigurationChangedEntry*>(mPendingEvent);
+ wasDispatched = dispatchConfigurationChangedLocked(currentTime, typedEntry);
+ break;
+ }
+
+ case EventEntry::TYPE_KEY: {
+ KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent);
+ if (isAppSwitchPendingLocked()) {
+ if (isAppSwitchKey(typedEntry->keyCode)) {
+ resetPendingAppSwitchLocked(true);
+ } else if (isAppSwitchDue) {
+ LOGI("Dropping key because of pending overdue app switch.");
+ wasDropped = true;
+ break;
+ }
+ }
+ wasDispatched = dispatchKeyLocked(currentTime, typedEntry, keyRepeatTimeout,
+ nextWakeupTime);
+ break;
+ }
+
+ case EventEntry::TYPE_MOTION: {
+ MotionEntry* typedEntry = static_cast<MotionEntry*>(mPendingEvent);
+ if (isAppSwitchDue) {
+ LOGI("Dropping motion because of pending overdue app switch.");
+ wasDropped = true;
+ break;
+ }
+ wasDispatched = dispatchMotionLocked(currentTime, typedEntry, nextWakeupTime);
+ break;
+ }
+
+ default:
+ assert(false);
+ wasDropped = true;
+ break;
+ }
+
+ if (wasDispatched || wasDropped) {
+ releasePendingEventLocked(wasDropped);
+ *nextWakeupTime = LONG_LONG_MIN; // force next poll to wake up immediately
+ }
+}
+
+bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) {
+ bool needWake = mInboundQueue.isEmpty();
+ mInboundQueue.enqueueAtTail(entry);
+
+ switch (entry->type) {
+ case EventEntry::TYPE_KEY:
+ needWake |= detectPendingAppSwitchLocked(static_cast<KeyEntry*>(entry));
+ break;
+ }
+
+ return needWake;
+}
+
+bool InputDispatcher::isAppSwitchKey(int32_t keyCode) {
+ return keyCode == AKEYCODE_HOME || keyCode == AKEYCODE_ENDCALL;
+}
+
+bool InputDispatcher::isAppSwitchPendingLocked() {
+ return mAppSwitchDueTime != LONG_LONG_MAX;
+}
+
+bool InputDispatcher::detectPendingAppSwitchLocked(KeyEntry* inboundKeyEntry) {
+ if (inboundKeyEntry->action == AKEY_EVENT_ACTION_UP
+ && ! (inboundKeyEntry->flags & AKEY_EVENT_FLAG_CANCELED)
+ && isAppSwitchKey(inboundKeyEntry->keyCode)
+ && isEventFromReliableSourceLocked(inboundKeyEntry)) {
+#if DEBUG_APP_SWITCH
+ LOGD("App switch is pending!");
+#endif
+ mAppSwitchDueTime = inboundKeyEntry->eventTime + APP_SWITCH_TIMEOUT;
+ return true; // need wake
+ }
+ return false;
+}
+
+void InputDispatcher::resetPendingAppSwitchLocked(bool handled) {
+ mAppSwitchDueTime = LONG_LONG_MAX;
+
+#if DEBUG_APP_SWITCH
+ if (handled) {
+ LOGD("App switch has arrived.");
+ } else {
+ LOGD("App switch was abandoned.");
+ }
+#endif
+}
+
+bool InputDispatcher::runCommandsLockedInterruptible() {
+ if (mCommandQueue.isEmpty()) {
+ return false;
+ }
+
+ do {
+ CommandEntry* commandEntry = mCommandQueue.dequeueAtHead();
+
+ Command command = commandEntry->command;
+ (this->*command)(commandEntry); // commands are implicitly 'LockedInterruptible'
+
+ commandEntry->connection.clear();
+ mAllocator.releaseCommandEntry(commandEntry);
+ } while (! mCommandQueue.isEmpty());
+ return true;
+}
+
+InputDispatcher::CommandEntry* InputDispatcher::postCommandLocked(Command command) {
+ CommandEntry* commandEntry = mAllocator.obtainCommandEntry(command);
+ mCommandQueue.enqueueAtTail(commandEntry);
+ return commandEntry;
+}
+
+void InputDispatcher::drainInboundQueueLocked() {
+ while (! mInboundQueue.isEmpty()) {
+ EventEntry* entry = mInboundQueue.dequeueAtHead();
+ releaseInboundEventLocked(entry, true /*wasDropped*/);
+ }
+}
+
+void InputDispatcher::releasePendingEventLocked(bool wasDropped) {
+ if (mPendingEvent) {
+ releaseInboundEventLocked(mPendingEvent, wasDropped);
+ mPendingEvent = NULL;
+ }
+}
+
+void InputDispatcher::releaseInboundEventLocked(EventEntry* entry, bool wasDropped) {
+ if (wasDropped) {
+#if DEBUG_DISPATCH_CYCLE
+ LOGD("Pending event was dropped.");
+#endif
+ setInjectionResultLocked(entry, INPUT_EVENT_INJECTION_FAILED);
+ }
+ mAllocator.releaseEventEntry(entry);
+}
+
+bool InputDispatcher::isEventFromReliableSourceLocked(EventEntry* entry) {
+ return ! entry->isInjected()
+ || entry->injectorUid == 0
+ || mPolicy->checkInjectEventsPermissionNonReentrant(
+ entry->injectorPid, entry->injectorUid);
+}
+
+void InputDispatcher::resetKeyRepeatLocked() {
+ if (mKeyRepeatState.lastKeyEntry) {
+ mAllocator.releaseKeyEntry(mKeyRepeatState.lastKeyEntry);
+ mKeyRepeatState.lastKeyEntry = NULL;
+ }
+}
+
+InputDispatcher::KeyEntry* InputDispatcher::synthesizeKeyRepeatLocked(
+ nsecs_t currentTime, nsecs_t keyRepeatDelay) {
+ KeyEntry* entry = mKeyRepeatState.lastKeyEntry;
+
+ // Reuse the repeated key entry if it is otherwise unreferenced.
+ uint32_t policyFlags = entry->policyFlags & POLICY_FLAG_RAW_MASK;
+ if (entry->refCount == 1) {
+ entry->recycle();
+ entry->eventTime = currentTime;
+ entry->policyFlags = policyFlags;
+ entry->repeatCount += 1;
+ } else {
+ KeyEntry* newEntry = mAllocator.obtainKeyEntry(currentTime,
+ entry->deviceId, entry->source, policyFlags,
+ entry->action, entry->flags, entry->keyCode, entry->scanCode,
+ entry->metaState, entry->repeatCount + 1, entry->downTime);
+
+ mKeyRepeatState.lastKeyEntry = newEntry;
+ mAllocator.releaseKeyEntry(entry);
+
+ entry = newEntry;
+ }
+ entry->syntheticRepeat = true;
+
+ // Increment reference count since we keep a reference to the event in
+ // mKeyRepeatState.lastKeyEntry in addition to the one we return.
+ entry->refCount += 1;
+
+ if (entry->repeatCount == 1) {
+ entry->flags |= AKEY_EVENT_FLAG_LONG_PRESS;
+ }
+
+ mKeyRepeatState.nextRepeatTime = currentTime + keyRepeatDelay;
+ return entry;
+}
+
+bool InputDispatcher::dispatchConfigurationChangedLocked(
+ nsecs_t currentTime, ConfigurationChangedEntry* entry) {
+#if DEBUG_OUTBOUND_EVENT_DETAILS
+ LOGD("dispatchConfigurationChanged - eventTime=%lld", entry->eventTime);
+#endif
+
+ // Reset key repeating in case a keyboard device was added or removed or something.
+ resetKeyRepeatLocked();
+
+ // Enqueue a command to run outside the lock to tell the policy that the configuration changed.
+ CommandEntry* commandEntry = postCommandLocked(
+ & InputDispatcher::doNotifyConfigurationChangedInterruptible);
+ commandEntry->eventTime = entry->eventTime;
+ return true;
+}
+
+bool InputDispatcher::dispatchKeyLocked(
+ nsecs_t currentTime, KeyEntry* entry, nsecs_t keyRepeatTimeout,
+ nsecs_t* nextWakeupTime) {
+ // Preprocessing.
+ if (! entry->dispatchInProgress) {
+ logOutboundKeyDetailsLocked("dispatchKey - ", entry);
+
+ if (entry->repeatCount == 0
+ && entry->action == AKEY_EVENT_ACTION_DOWN
+ && ! entry->isInjected()) {
+ if (mKeyRepeatState.lastKeyEntry
+ && mKeyRepeatState.lastKeyEntry->keyCode == entry->keyCode) {
+ // We have seen two identical key downs in a row which indicates that the device
+ // driver is automatically generating key repeats itself. We take note of the
+ // repeat here, but we disable our own next key repeat timer since it is clear that
+ // we will not need to synthesize key repeats ourselves.
+ entry->repeatCount = mKeyRepeatState.lastKeyEntry->repeatCount + 1;
+ resetKeyRepeatLocked();
+ mKeyRepeatState.nextRepeatTime = LONG_LONG_MAX; // don't generate repeats ourselves
+ } else {
+ // Not a repeat. Save key down state in case we do see a repeat later.
+ resetKeyRepeatLocked();
+ mKeyRepeatState.nextRepeatTime = entry->eventTime + keyRepeatTimeout;
+ }
+ mKeyRepeatState.lastKeyEntry = entry;
+ entry->refCount += 1;
+ } else if (! entry->syntheticRepeat) {
+ resetKeyRepeatLocked();
+ }
+
+ entry->dispatchInProgress = true;
+ startFindingTargetsLocked();
+ }
+
+ // Identify targets.
+ if (! mCurrentInputTargetsValid) {
+ InputWindow* window = NULL;
+ int32_t injectionResult = findFocusedWindowLocked(currentTime,
+ entry, nextWakeupTime, & window);
+ if (injectionResult == INPUT_EVENT_INJECTION_PENDING) {
+ return false;
+ }
+
+ setInjectionResultLocked(entry, injectionResult);
+ if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
+ return true;
+ }
+
+ addMonitoringTargetsLocked();
+ finishFindingTargetsLocked(window);
+ }
+
+ // Give the policy a chance to intercept the key.
+ if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) {
+ CommandEntry* commandEntry = postCommandLocked(
+ & InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible);
+ commandEntry->inputChannel = mCurrentInputChannel;
+ commandEntry->keyEntry = entry;
+ entry->refCount += 1;
+ return false; // wait for the command to run
+ }
+ if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_SKIP) {
+ return true;
+ }
+
+ // Dispatch the key.
+ dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false);
+
+ // Poke user activity.
+ pokeUserActivityLocked(entry->eventTime, mCurrentInputWindowType, POWER_MANAGER_BUTTON_EVENT);
+ return true;
+}
+
+void InputDispatcher::logOutboundKeyDetailsLocked(const char* prefix, const KeyEntry* entry) {
+#if DEBUG_OUTBOUND_EVENT_DETAILS
+ LOGD("%seventTime=%lld, deviceId=0x%x, source=0x%x, policyFlags=0x%x, "
+ "action=0x%x, flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, "
+ "downTime=%lld",
+ prefix,
+ entry->eventTime, entry->deviceId, entry->source, entry->policyFlags,
+ entry->action, entry->flags, entry->keyCode, entry->scanCode, entry->metaState,
+ entry->downTime);
+#endif
+}
+
+bool InputDispatcher::dispatchMotionLocked(
+ nsecs_t currentTime, MotionEntry* entry, nsecs_t* nextWakeupTime) {
+ // Preprocessing.
+ if (! entry->dispatchInProgress) {
+ logOutboundMotionDetailsLocked("dispatchMotion - ", entry);
+
+ entry->dispatchInProgress = true;
+ startFindingTargetsLocked();
+ }
+
+ bool isPointerEvent = entry->source & AINPUT_SOURCE_CLASS_POINTER;
+
+ // Identify targets.
+ if (! mCurrentInputTargetsValid) {
+ InputWindow* window = NULL;
+ int32_t injectionResult;
+ if (isPointerEvent) {
+ // Pointer event. (eg. touchscreen)
+ injectionResult = findTouchedWindowLocked(currentTime,
+ entry, nextWakeupTime, & window);
+ } else {
+ // Non touch event. (eg. trackball)
+ injectionResult = findFocusedWindowLocked(currentTime,
+ entry, nextWakeupTime, & window);
+ }
+ if (injectionResult == INPUT_EVENT_INJECTION_PENDING) {
+ return false;
+ }
+
+ setInjectionResultLocked(entry, injectionResult);
+ if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
+ return true;
+ }
+
+ addMonitoringTargetsLocked();
+ finishFindingTargetsLocked(window);
+ }
+
+ // Dispatch the motion.
+ dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false);
+
+ // Poke user activity.
+ int32_t eventType;
+ if (isPointerEvent) {
+ switch (entry->action) {
+ case AMOTION_EVENT_ACTION_DOWN:
+ eventType = POWER_MANAGER_TOUCH_EVENT;
+ break;
+ case AMOTION_EVENT_ACTION_UP:
+ eventType = POWER_MANAGER_TOUCH_UP_EVENT;
+ break;
+ default:
+ if (entry->eventTime - entry->downTime >= EVENT_IGNORE_DURATION) {
+ eventType = POWER_MANAGER_TOUCH_EVENT;
+ } else {
+ eventType = POWER_MANAGER_LONG_TOUCH_EVENT;
+ }
+ break;
+ }
+ } else {
+ eventType = POWER_MANAGER_BUTTON_EVENT;
+ }
+ pokeUserActivityLocked(entry->eventTime, mCurrentInputWindowType, eventType);
+ return true;
+}
+
+
+void InputDispatcher::logOutboundMotionDetailsLocked(const char* prefix, const MotionEntry* entry) {
+#if DEBUG_OUTBOUND_EVENT_DETAILS
+ LOGD("%seventTime=%lld, deviceId=0x%x, source=0x%x, policyFlags=0x%x, "
+ "action=0x%x, flags=0x%x, "
+ "metaState=0x%x, edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, downTime=%lld",
+ prefix,
+ entry->eventTime, entry->deviceId, entry->source, entry->policyFlags,
+ entry->action, entry->flags,
+ entry->metaState, entry->edgeFlags, entry->xPrecision, entry->yPrecision,
+ entry->downTime);
+
+ // Print the most recent sample that we have available, this may change due to batching.
+ size_t sampleCount = 1;
+ const MotionSample* sample = & entry->firstSample;
+ for (; sample->next != NULL; sample = sample->next) {
+ sampleCount += 1;
+ }
+ for (uint32_t i = 0; i < entry->pointerCount; i++) {
+ LOGD(" Pointer %d: id=%d, x=%f, y=%f, pressure=%f, size=%f, "
+ "touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, "
+ "orientation=%f",
+ i, entry->pointerIds[i],
+ sample->pointerCoords[i].x, sample->pointerCoords[i].y,
+ sample->pointerCoords[i].pressure, sample->pointerCoords[i].size,
+ sample->pointerCoords[i].touchMajor, sample->pointerCoords[i].touchMinor,
+ sample->pointerCoords[i].toolMajor, sample->pointerCoords[i].toolMinor,
+ sample->pointerCoords[i].orientation);
+ }
+
+ // Keep in mind that due to batching, it is possible for the number of samples actually
+ // dispatched to change before the application finally consumed them.
+ if (entry->action == AMOTION_EVENT_ACTION_MOVE) {
+ LOGD(" ... Total movement samples currently batched %d ...", sampleCount);
+ }
+#endif
+}
+
+void InputDispatcher::dispatchEventToCurrentInputTargetsLocked(nsecs_t currentTime,
+ EventEntry* eventEntry, bool resumeWithAppendedMotionSample) {
+#if DEBUG_DISPATCH_CYCLE
+ LOGD("dispatchEventToCurrentInputTargets - "
+ "resumeWithAppendedMotionSample=%s",
+ toString(resumeWithAppendedMotionSample));
+#endif
+
+ assert(eventEntry->dispatchInProgress); // should already have been set to true
+
+ for (size_t i = 0; i < mCurrentInputTargets.size(); i++) {
+ const InputTarget& inputTarget = mCurrentInputTargets.itemAt(i);
+
+ ssize_t connectionIndex = getConnectionIndex(inputTarget.inputChannel);
+ if (connectionIndex >= 0) {
+ sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
+ prepareDispatchCycleLocked(currentTime, connection, eventEntry, & inputTarget,
+ resumeWithAppendedMotionSample);
+ } else {
+ LOGW("Framework requested delivery of an input event to channel '%s' but it "
+ "is not registered with the input dispatcher.",
+ inputTarget.inputChannel->getName().string());
+ }
+ }
+}
+
+void InputDispatcher::startFindingTargetsLocked() {
+ mCurrentInputTargetsValid = false;
+ mCurrentInputTargets.clear();
+ mCurrentInputChannel.clear();
+ mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_NONE;
+}
+
+void InputDispatcher::finishFindingTargetsLocked(const InputWindow* window) {
+ mCurrentInputWindowType = window->layoutParamsType;
+ mCurrentInputChannel = window->inputChannel;
+ mCurrentInputTargetsValid = true;
+}
+
+int32_t InputDispatcher::handleTargetsNotReadyLocked(nsecs_t currentTime,
+ const EventEntry* entry, const InputApplication* application, const InputWindow* window,
+ nsecs_t* nextWakeupTime) {
+ if (application == NULL && window == NULL) {
+ if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY) {
+#if DEBUG_FOCUS
+ LOGD("Waiting for system to become ready for input.");
+#endif
+ mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY;
+ mInputTargetWaitStartTime = currentTime;
+ mInputTargetWaitTimeoutTime = LONG_LONG_MAX;
+ mInputTargetWaitTimeoutExpired = false;
+ }
+ } else {
+ if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) {
+#if DEBUG_FOCUS
+ LOGD("Waiting for application to become ready for input: name=%s, window=%s",
+ application ? application->name.string() : "<unknown>",
+ window ? window->inputChannel->getName().string() : "<unknown>");
+#endif
+ nsecs_t timeout = window ? window->dispatchingTimeout :
+ application ? application->dispatchingTimeout : DEFAULT_INPUT_DISPATCHING_TIMEOUT;
+
+ mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY;
+ mInputTargetWaitStartTime = currentTime;
+ mInputTargetWaitTimeoutTime = currentTime + timeout;
+ mInputTargetWaitTimeoutExpired = false;
+ }
+ }
+
+ if (mInputTargetWaitTimeoutExpired) {
+ return INPUT_EVENT_INJECTION_TIMED_OUT;
+ }
+
+ if (currentTime >= mInputTargetWaitTimeoutTime) {
+ LOGI("Application is not ready for input: name=%s, window=%s,"
+ "%01.1fms since event, %01.1fms since wait started",
+ application ? application->name.string() : "<unknown>",
+ window ? window->inputChannel->getName().string() : "<unknown>",
+ (currentTime - entry->eventTime) / 1000000.0,
+ (currentTime - mInputTargetWaitStartTime) / 1000000.0);
+
+ CommandEntry* commandEntry = postCommandLocked(
+ & InputDispatcher::doTargetsNotReadyTimeoutLockedInterruptible);
+ if (application) {
+ commandEntry->inputApplicationHandle = application->handle;
+ }
+ if (window) {
+ commandEntry->inputChannel = window->inputChannel;
+ }
+
+ // Force poll loop to wake up immediately on next iteration once we get the
+ // ANR response back from the policy.
+ *nextWakeupTime = LONG_LONG_MIN;
+ return INPUT_EVENT_INJECTION_PENDING;
+ } else {
+ // Force poll loop to wake up when timeout is due.
+ if (mInputTargetWaitTimeoutTime < *nextWakeupTime) {
+ *nextWakeupTime = mInputTargetWaitTimeoutTime;
+ }
+ return INPUT_EVENT_INJECTION_PENDING;
+ }
+}
+
+void InputDispatcher::resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout) {
+ if (newTimeout > 0) {
+ // Extend the timeout.
+ mInputTargetWaitTimeoutTime = now() + newTimeout;
+ } else {
+ // Give up.
+ mInputTargetWaitTimeoutExpired = true;
+ }
+}
+
+nsecs_t InputDispatcher::getTimeSpentWaitingForApplicationWhileFindingTargetsLocked(
+ nsecs_t currentTime) {
+ if (mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) {
+ return currentTime - mInputTargetWaitStartTime;
+ }
+ return 0;
+}
+
+void InputDispatcher::resetANRTimeoutsLocked() {
+#if DEBUG_FOCUS
+ LOGD("Resetting ANR timeouts.");
+#endif
+
+ // Reset timeouts for all active connections.
+ nsecs_t currentTime = now();
+ for (size_t i = 0; i < mActiveConnections.size(); i++) {
+ Connection* connection = mActiveConnections[i];
+ connection->resetTimeout(currentTime);
+ }
+
+ // Reset input target wait timeout.
+ mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_NONE;
+}
+
+int32_t InputDispatcher::findFocusedWindowLocked(nsecs_t currentTime, const EventEntry* entry,
+ nsecs_t* nextWakeupTime, InputWindow** outWindow) {
+ *outWindow = NULL;
+ mCurrentInputTargets.clear();
+
+ int32_t injectionResult;
+
+ // If there is no currently focused window and no focused application
+ // then drop the event.
+ if (! mFocusedWindow) {
+ if (mFocusedApplication) {
+#if DEBUG_FOCUS
+ LOGD("Waiting because there is no focused window but there is a "
+ "focused application that may eventually add a window: '%s'.",
+ mFocusedApplication->name.string());
+#endif
+ injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
+ mFocusedApplication, NULL, nextWakeupTime);
+ goto Unresponsive;
+ }
+
+ LOGI("Dropping event because there is no focused window or focused application.");
+ injectionResult = INPUT_EVENT_INJECTION_FAILED;
+ goto Failed;
+ }
+
+ // Check permissions.
+ if (! checkInjectionPermission(mFocusedWindow, entry->injectorPid, entry->injectorUid)) {
+ injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
+ goto Failed;
+ }
+
+ // If the currently focused window is paused then keep waiting.
+ if (mFocusedWindow->paused) {
+#if DEBUG_FOCUS
+ LOGD("Waiting because focused window is paused.");
+#endif
+ injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
+ mFocusedApplication, mFocusedWindow, nextWakeupTime);
+ goto Unresponsive;
+ }
+
+ // Success! Output targets.
+ injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
+ *outWindow = mFocusedWindow;
+ addWindowTargetLocked(mFocusedWindow, InputTarget::FLAG_SYNC,
+ getTimeSpentWaitingForApplicationWhileFindingTargetsLocked(currentTime));
+
+ // Done.
+Failed:
+Unresponsive:
+#if DEBUG_FOCUS
+ LOGD("findFocusedWindow finished: injectionResult=%d",
+ injectionResult);
+ logDispatchStateLocked();
+#endif
+ return injectionResult;
+}
+
+int32_t InputDispatcher::findTouchedWindowLocked(nsecs_t currentTime, const MotionEntry* entry,
+ nsecs_t* nextWakeupTime, InputWindow** outWindow) {
+ enum InjectionPermission {
+ INJECTION_PERMISSION_UNKNOWN,
+ INJECTION_PERMISSION_GRANTED,
+ INJECTION_PERMISSION_DENIED
+ };
+
+ *outWindow = NULL;
+ mCurrentInputTargets.clear();
+
+ nsecs_t startTime = now();
+
+ // For security reasons, we defer updating the touch state until we are sure that
+ // event injection will be allowed.
+ //
+ // FIXME In the original code, screenWasOff could never be set to true.
+ // The reason is that the POLICY_FLAG_WOKE_HERE
+ // and POLICY_FLAG_BRIGHT_HERE flags were set only when preprocessing raw
+ // EV_KEY, EV_REL and EV_ABS events. As it happens, the touch event was
+ // actually enqueued using the policyFlags that appeared in the final EV_SYN
+ // events upon which no preprocessing took place. So policyFlags was always 0.
+ // In the new native input dispatcher we're a bit more careful about event
+ // preprocessing so the touches we receive can actually have non-zero policyFlags.
+ // Unfortunately we obtain undesirable behavior.
+ //
+ // Here's what happens:
+ //
+ // When the device dims in anticipation of going to sleep, touches
+ // in windows which have FLAG_TOUCHABLE_WHEN_WAKING cause
+ // the device to brighten and reset the user activity timer.
+ // Touches on other windows (such as the launcher window)
+ // are dropped. Then after a moment, the device goes to sleep. Oops.
+ //
+ // Also notice how screenWasOff was being initialized using POLICY_FLAG_BRIGHT_HERE
+ // instead of POLICY_FLAG_WOKE_HERE...
+ //
+ bool screenWasOff = false; // original policy: policyFlags & POLICY_FLAG_BRIGHT_HERE;
+
+ int32_t action = entry->action;
+
+ // Update the touch state as needed based on the properties of the touch event.
+ int32_t injectionResult;
+ InjectionPermission injectionPermission;
+ if (action == AMOTION_EVENT_ACTION_DOWN) {
+ /* Case 1: ACTION_DOWN */
+
+ InputWindow* newTouchedWindow = NULL;
+ mTempTouchedOutsideTargets.clear();
+
+ int32_t x = int32_t(entry->firstSample.pointerCoords[0].x);
+ int32_t y = int32_t(entry->firstSample.pointerCoords[0].y);
+ InputWindow* topErrorWindow = NULL;
+ bool obscured = false;
+
+ // Traverse windows from front to back to find touched window and outside targets.
+ size_t numWindows = mWindows.size();
+ for (size_t i = 0; i < numWindows; i++) {
+ InputWindow* window = & mWindows.editItemAt(i);
+ int32_t flags = window->layoutParamsFlags;
+
+ if (flags & InputWindow::FLAG_SYSTEM_ERROR) {
+ if (! topErrorWindow) {
+ topErrorWindow = window;
+ }
+ }
+
+ if (window->visible) {
+ if (! (flags & InputWindow::FLAG_NOT_TOUCHABLE)) {
+ bool isTouchModal = (flags & (InputWindow::FLAG_NOT_FOCUSABLE
+ | InputWindow::FLAG_NOT_TOUCH_MODAL)) == 0;
+ if (isTouchModal || window->touchableAreaContainsPoint(x, y)) {
+ if (! screenWasOff || flags & InputWindow::FLAG_TOUCHABLE_WHEN_WAKING) {
+ newTouchedWindow = window;
+ obscured = isWindowObscuredLocked(window);
+ }
+ break; // found touched window, exit window loop
+ }
+ }
+
+ if (flags & InputWindow::FLAG_WATCH_OUTSIDE_TOUCH) {
+ OutsideTarget outsideTarget;
+ outsideTarget.window = window;
+ outsideTarget.obscured = isWindowObscuredLocked(window);
+ mTempTouchedOutsideTargets.push(outsideTarget);
+ }
+ }
+ }
+
+ // If there is an error window but it is not taking focus (typically because
+ // it is invisible) then wait for it. Any other focused window may in
+ // fact be in ANR state.
+ if (topErrorWindow && newTouchedWindow != topErrorWindow) {
+#if DEBUG_FOCUS
+ LOGD("Waiting because system error window is pending.");
+#endif
+ injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
+ NULL, NULL, nextWakeupTime);
+ injectionPermission = INJECTION_PERMISSION_UNKNOWN;
+ goto Unresponsive;
+ }
+
+ // If we did not find a touched window then fail.
+ if (! newTouchedWindow) {
+ if (mFocusedApplication) {
+#if DEBUG_FOCUS
+ LOGD("Waiting because there is no touched window but there is a "
+ "focused application that may eventually add a new window: '%s'.",
+ mFocusedApplication->name.string());
+#endif
+ injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
+ mFocusedApplication, NULL, nextWakeupTime);
+ injectionPermission = INJECTION_PERMISSION_UNKNOWN;
+ goto Unresponsive;
+ }
+
+ LOGI("Dropping event because there is no touched window or focused application.");
+ injectionResult = INPUT_EVENT_INJECTION_FAILED;
+ injectionPermission = INJECTION_PERMISSION_UNKNOWN;
+ goto Failed;
+ }
+
+ // Check permissions.
+ if (! checkInjectionPermission(newTouchedWindow, entry->injectorPid, entry->injectorUid)) {
+ injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
+ injectionPermission = INJECTION_PERMISSION_DENIED;
+ goto Failed;
+ }
+
+ // If the touched window is paused then keep waiting.
+ if (newTouchedWindow->paused) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+ LOGD("Waiting because touched window is paused.");
+#endif
+ injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
+ NULL, newTouchedWindow, nextWakeupTime);
+ injectionPermission = INJECTION_PERMISSION_GRANTED;
+ goto Unresponsive;
+ }
+
+ // Success! Update the touch dispatch state for real.
+ releaseTouchedWindowLocked();
+
+ mTouchedWindow = newTouchedWindow;
+ mTouchedWindowIsObscured = obscured;
+
+ if (newTouchedWindow->hasWallpaper) {
+ mTouchedWallpaperWindows.appendVector(mWallpaperWindows);
+ }
+ } else {
+ /* Case 2: Everything but ACTION_DOWN */
+
+ // Check permissions.
+ if (! checkInjectionPermission(mTouchedWindow, entry->injectorPid, entry->injectorUid)) {
+ injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
+ injectionPermission = INJECTION_PERMISSION_DENIED;
+ goto Failed;
+ }
+
+ // If the pointer is not currently down, then ignore the event.
+ if (! mTouchDown) {
+ LOGI("Dropping event because the pointer is not down.");
+ injectionResult = INPUT_EVENT_INJECTION_FAILED;
+ injectionPermission = INJECTION_PERMISSION_GRANTED;
+ goto Failed;
+ }
+
+ // If there is no currently touched window then fail.
+ if (! mTouchedWindow) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+ LOGD("Dropping event because there is no touched window to receive it.");
+#endif
+ injectionResult = INPUT_EVENT_INJECTION_FAILED;
+ injectionPermission = INJECTION_PERMISSION_GRANTED;
+ goto Failed;
+ }
+
+ // If the touched window is paused then keep waiting.
+ if (mTouchedWindow->paused) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+ LOGD("Waiting because touched window is paused.");
+#endif
+ injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
+ NULL, mTouchedWindow, nextWakeupTime);
+ injectionPermission = INJECTION_PERMISSION_GRANTED;
+ goto Unresponsive;
+ }
+ }
+
+ // Success! Output targets.
+ injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
+ injectionPermission = INJECTION_PERMISSION_GRANTED;
+
+ {
+ size_t numWallpaperWindows = mTouchedWallpaperWindows.size();
+ for (size_t i = 0; i < numWallpaperWindows; i++) {
+ addWindowTargetLocked(mTouchedWallpaperWindows[i],
+ InputTarget::FLAG_WINDOW_IS_OBSCURED, 0);
+ }
+
+ size_t numOutsideTargets = mTempTouchedOutsideTargets.size();
+ for (size_t i = 0; i < numOutsideTargets; i++) {
+ const OutsideTarget& outsideTarget = mTempTouchedOutsideTargets[i];
+ int32_t outsideTargetFlags = InputTarget::FLAG_OUTSIDE;
+ if (outsideTarget.obscured) {
+ outsideTargetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
+ }
+ addWindowTargetLocked(outsideTarget.window, outsideTargetFlags, 0);
+ }
+ mTempTouchedOutsideTargets.clear();
+
+ int32_t targetFlags = InputTarget::FLAG_SYNC;
+ if (mTouchedWindowIsObscured) {
+ targetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
+ }
+ addWindowTargetLocked(mTouchedWindow, targetFlags,
+ getTimeSpentWaitingForApplicationWhileFindingTargetsLocked(currentTime));
+ *outWindow = mTouchedWindow;
+ }
+
+Failed:
+ // Check injection permission once and for all.
+ if (injectionPermission == INJECTION_PERMISSION_UNKNOWN) {
+ if (checkInjectionPermission(action == AMOTION_EVENT_ACTION_DOWN ? NULL : mTouchedWindow,
+ entry->injectorPid, entry->injectorUid)) {
+ injectionPermission = INJECTION_PERMISSION_GRANTED;
+ } else {
+ injectionPermission = INJECTION_PERMISSION_DENIED;
+ }
+ }
+
+ // Update final pieces of touch state if the injector had permission.
+ if (injectionPermission == INJECTION_PERMISSION_GRANTED) {
+ if (action == AMOTION_EVENT_ACTION_DOWN) {
+ if (mTouchDown) {
+ // This is weird. We got a down but we thought it was already down!
+ LOGW("Pointer down received while already down.");
+ } else {
+ mTouchDown = true;
+ }
+
+ if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
+ // Since we failed to identify a target for this touch down, we may still
+ // be holding on to an earlier target from a previous touch down. Release it.
+ releaseTouchedWindowLocked();
+ }
+ } else if (action == AMOTION_EVENT_ACTION_UP) {
+ mTouchDown = false;
+ releaseTouchedWindowLocked();
+ }
+ } else {
+ LOGW("Not updating touch focus because injection was denied.");
+ }
+
+Unresponsive:
+#if DEBUG_FOCUS
+ LOGD("findTouchedWindow finished: injectionResult=%d, injectionPermission=%d",
+ injectionResult, injectionPermission);
+ logDispatchStateLocked();
+#endif
+ return injectionResult;
+}
+
+void InputDispatcher::releaseTouchedWindowLocked() {
+ mTouchedWindow = NULL;
+ mTouchedWindowIsObscured = false;
+ mTouchedWallpaperWindows.clear();
+}
+
+void InputDispatcher::addWindowTargetLocked(const InputWindow* window, int32_t targetFlags,
+ nsecs_t timeSpentWaitingForApplication) {
+ mCurrentInputTargets.push();
+
+ InputTarget& target = mCurrentInputTargets.editTop();
+ target.inputChannel = window->inputChannel;
+ target.flags = targetFlags;
+ target.timeout = window->dispatchingTimeout;
+ target.timeSpentWaitingForApplication = timeSpentWaitingForApplication;
+ target.xOffset = - window->frameLeft;
+ target.yOffset = - window->frameTop;
+}
+
+void InputDispatcher::addMonitoringTargetsLocked() {
+ for (size_t i = 0; i < mMonitoringChannels.size(); i++) {
+ mCurrentInputTargets.push();
+
+ InputTarget& target = mCurrentInputTargets.editTop();
+ target.inputChannel = mMonitoringChannels[i];
+ target.flags = 0;
+ target.timeout = -1;
+ target.timeSpentWaitingForApplication = 0;
+ target.xOffset = 0;
+ target.yOffset = 0;
+ }
+}
+
+bool InputDispatcher::checkInjectionPermission(const InputWindow* window,
+ int32_t injectorPid, int32_t injectorUid) {
+ if (injectorUid > 0 && (window == NULL || window->ownerUid != injectorUid)) {
+ bool result = mPolicy->checkInjectEventsPermissionNonReentrant(injectorPid, injectorUid);
+ if (! result) {
+ if (window) {
+ LOGW("Permission denied: injecting event from pid %d uid %d to window "
+ "with input channel %s owned by uid %d",
+ injectorPid, injectorUid, window->inputChannel->getName().string(),
+ window->ownerUid);
+ } else {
+ LOGW("Permission denied: injecting event from pid %d uid %d",
+ injectorPid, injectorUid);
+ }
+ return false;
+ }
+ }
+ return true;
+}
+
+bool InputDispatcher::isWindowObscuredLocked(const InputWindow* window) {
+ size_t numWindows = mWindows.size();
+ for (size_t i = 0; i < numWindows; i++) {
+ const InputWindow* other = & mWindows.itemAt(i);
+ if (other == window) {
+ break;
+ }
+ if (other->visible && window->visibleFrameIntersects(other)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void InputDispatcher::pokeUserActivityLocked(nsecs_t eventTime,
+ int32_t windowType, int32_t eventType) {
+ CommandEntry* commandEntry = postCommandLocked(
+ & InputDispatcher::doPokeUserActivityLockedInterruptible);
+ commandEntry->eventTime = eventTime;
+ commandEntry->windowType = windowType;
+ commandEntry->userActivityEventType = eventType;
+}
+
+void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
+ const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget,
+ bool resumeWithAppendedMotionSample) {
+#if DEBUG_DISPATCH_CYCLE
+ LOGD("channel '%s' ~ prepareDispatchCycle - flags=%d, timeout=%lldns, "
+ "xOffset=%f, yOffset=%f, resumeWithAppendedMotionSample=%s",
+ connection->getInputChannelName(), inputTarget->flags, inputTarget->timeout,
+ inputTarget->xOffset, inputTarget->yOffset,
+ toString(resumeWithAppendedMotionSample));
+#endif
+
+ // Skip this event if the connection status is not normal.
+ // We don't want to enqueue additional outbound events if the connection is broken or
+ // not responding.
+ if (connection->status != Connection::STATUS_NORMAL) {
+ LOGW("channel '%s' ~ Dropping event because the channel status is %s",
+ connection->getInputChannelName(), connection->getStatusLabel());
+
+ // If the connection is not responding but the user is poking the application anyways,
+ // retrigger the original timeout.
+ if (connection->status == Connection::STATUS_NOT_RESPONDING) {
+ timeoutDispatchCycleLocked(currentTime, connection);
+ }
+ return;
+ }
+
+ // Resume the dispatch cycle with a freshly appended motion sample.
+ // First we check that the last dispatch entry in the outbound queue is for the same
+ // motion event to which we appended the motion sample. If we find such a dispatch
+ // entry, and if it is currently in progress then we try to stream the new sample.
+ bool wasEmpty = connection->outboundQueue.isEmpty();
+
+ if (! wasEmpty && resumeWithAppendedMotionSample) {
+ DispatchEntry* motionEventDispatchEntry =
+ connection->findQueuedDispatchEntryForEvent(eventEntry);
+ if (motionEventDispatchEntry) {
+ // If the dispatch entry is not in progress, then we must be busy dispatching an
+ // earlier event. Not a problem, the motion event is on the outbound queue and will
+ // be dispatched later.
+ if (! motionEventDispatchEntry->inProgress) {
+#if DEBUG_BATCHING
+ LOGD("channel '%s' ~ Not streaming because the motion event has "
+ "not yet been dispatched. "
+ "(Waiting for earlier events to be consumed.)",
+ connection->getInputChannelName());
+#endif
+ return;
+ }
+
+ // If the dispatch entry is in progress but it already has a tail of pending
+ // motion samples, then it must mean that the shared memory buffer filled up.
+ // Not a problem, when this dispatch cycle is finished, we will eventually start
+ // a new dispatch cycle to process the tail and that tail includes the newly
+ // appended motion sample.
+ if (motionEventDispatchEntry->tailMotionSample) {
+#if DEBUG_BATCHING
+ LOGD("channel '%s' ~ Not streaming because no new samples can "
+ "be appended to the motion event in this dispatch cycle. "
+ "(Waiting for next dispatch cycle to start.)",
+ connection->getInputChannelName());
+#endif
+ return;
+ }
+
+ // The dispatch entry is in progress and is still potentially open for streaming.
+ // Try to stream the new motion sample. This might fail if the consumer has already
+ // consumed the motion event (or if the channel is broken).
+ MotionSample* appendedMotionSample = static_cast<MotionEntry*>(eventEntry)->lastSample;
+ status_t status = connection->inputPublisher.appendMotionSample(
+ appendedMotionSample->eventTime, appendedMotionSample->pointerCoords);
+ if (status == OK) {
+#if DEBUG_BATCHING
+ LOGD("channel '%s' ~ Successfully streamed new motion sample.",
+ connection->getInputChannelName());
+#endif
+ return;
+ }
+
+#if DEBUG_BATCHING
+ if (status == NO_MEMORY) {
+ LOGD("channel '%s' ~ Could not append motion sample to currently "
+ "dispatched move event because the shared memory buffer is full. "
+ "(Waiting for next dispatch cycle to start.)",
+ connection->getInputChannelName());
+ } else if (status == status_t(FAILED_TRANSACTION)) {
+ LOGD("channel '%s' ~ Could not append motion sample to currently "
+ "dispatched move event because the event has already been consumed. "
+ "(Waiting for next dispatch cycle to start.)",
+ connection->getInputChannelName());
+ } else {
+ LOGD("channel '%s' ~ Could not append motion sample to currently "
+ "dispatched move event due to an error, status=%d. "
+ "(Waiting for next dispatch cycle to start.)",
+ connection->getInputChannelName(), status);
+ }
+#endif
+ // Failed to stream. Start a new tail of pending motion samples to dispatch
+ // in the next cycle.
+ motionEventDispatchEntry->tailMotionSample = appendedMotionSample;
+ return;
+ }
+ }
+
+ // Bring the input state back in line with reality in case it drifted off during an ANR.
+ if (connection->inputState.isOutOfSync()) {
+ mTempCancelationEvents.clear();
+ connection->inputState.synthesizeCancelationEvents(& mAllocator, mTempCancelationEvents);
+ connection->inputState.resetOutOfSync();
+
+ if (! mTempCancelationEvents.isEmpty()) {
+ LOGI("channel '%s' ~ Generated %d cancelation events to bring channel back in sync "
+ "with reality.",
+ connection->getInputChannelName(), mTempCancelationEvents.size());
+
+ for (size_t i = 0; i < mTempCancelationEvents.size(); i++) {
+ EventEntry* cancelationEventEntry = mTempCancelationEvents.itemAt(i);
+ switch (cancelationEventEntry->type) {
+ case EventEntry::TYPE_KEY:
+ logOutboundKeyDetailsLocked(" ",
+ static_cast<KeyEntry*>(cancelationEventEntry));
+ break;
+ case EventEntry::TYPE_MOTION:
+ logOutboundMotionDetailsLocked(" ",
+ static_cast<MotionEntry*>(cancelationEventEntry));
+ break;
+ }
+
+ DispatchEntry* cancelationDispatchEntry =
+ mAllocator.obtainDispatchEntry(cancelationEventEntry,
+ 0, inputTarget->xOffset, inputTarget->yOffset, inputTarget->timeout);
+ connection->outboundQueue.enqueueAtTail(cancelationDispatchEntry);
+
+ mAllocator.releaseEventEntry(cancelationEventEntry);
+ }
+ }
+ }
+
+ // This is a new event.
+ // Enqueue a new dispatch entry onto the outbound queue for this connection.
+ DispatchEntry* dispatchEntry = mAllocator.obtainDispatchEntry(eventEntry, // increments ref
+ inputTarget->flags, inputTarget->xOffset, inputTarget->yOffset,
+ inputTarget->timeout);
+ if (dispatchEntry->isSyncTarget()) {
+ eventEntry->pendingSyncDispatches += 1;
+ }
+
+ // Handle the case where we could not stream a new motion sample because the consumer has
+ // already consumed the motion event (otherwise the corresponding dispatch entry would
+ // still be in the outbound queue for this connection). We set the head motion sample
+ // to the list starting with the newly appended motion sample.
+ if (resumeWithAppendedMotionSample) {
+#if DEBUG_BATCHING
+ LOGD("channel '%s' ~ Preparing a new dispatch cycle for additional motion samples "
+ "that cannot be streamed because the motion event has already been consumed.",
+ connection->getInputChannelName());
+#endif
+ MotionSample* appendedMotionSample = static_cast<MotionEntry*>(eventEntry)->lastSample;
+ dispatchEntry->headMotionSample = appendedMotionSample;
+ }
+
+ // Enqueue the dispatch entry.
+ connection->outboundQueue.enqueueAtTail(dispatchEntry);
+
+ // If the outbound queue was previously empty, start the dispatch cycle going.
+ if (wasEmpty) {
+ activateConnectionLocked(connection.get());
+ startDispatchCycleLocked(currentTime, connection,
+ inputTarget->timeSpentWaitingForApplication);
+ }
+}
+
+void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
+ const sp<Connection>& connection, nsecs_t timeSpentWaitingForApplication) {
+#if DEBUG_DISPATCH_CYCLE
+ LOGD("channel '%s' ~ startDispatchCycle",
+ connection->getInputChannelName());
+#endif
+
+ assert(connection->status == Connection::STATUS_NORMAL);
+ assert(! connection->outboundQueue.isEmpty());
+
+ DispatchEntry* dispatchEntry = connection->outboundQueue.headSentinel.next;
+ assert(! dispatchEntry->inProgress);
+
+ // Mark the dispatch entry as in progress.
+ dispatchEntry->inProgress = true;
+
+ // Update the connection's input state.
+ InputState::Consistency consistency = connection->inputState.trackEvent(
+ dispatchEntry->eventEntry);
+
+#if FILTER_INPUT_EVENTS
+ // Filter out inconsistent sequences of input events.
+ // The input system may drop or inject events in a way that could violate implicit
+ // invariants on input state and potentially cause an application to crash
+ // or think that a key or pointer is stuck down. Technically we make no guarantees
+ // of consistency but it would be nice to improve on this where possible.
+ // XXX: This code is a proof of concept only. Not ready for prime time.
+ if (consistency == InputState::TOLERABLE) {
+#if DEBUG_DISPATCH_CYCLE
+ LOGD("channel '%s' ~ Sending an event that is inconsistent with the connection's "
+ "current input state but that is likely to be tolerated by the application.",
+ connection->getInputChannelName());
+#endif
+ } else if (consistency == InputState::BROKEN) {
+ LOGI("channel '%s' ~ Dropping an event that is inconsistent with the connection's "
+ "current input state and that is likely to cause the application to crash.",
+ connection->getInputChannelName());
+ startNextDispatchCycleLocked(currentTime, connection);
+ return;
+ }
+#endif
+
+ // Publish the event.
+ status_t status;
+ switch (dispatchEntry->eventEntry->type) {
+ case EventEntry::TYPE_KEY: {
+ KeyEntry* keyEntry = static_cast<KeyEntry*>(dispatchEntry->eventEntry);
+
+ // Apply target flags.
+ int32_t action = keyEntry->action;
+ int32_t flags = keyEntry->flags;
+ if (dispatchEntry->targetFlags & InputTarget::FLAG_CANCEL) {
+ flags |= AKEY_EVENT_FLAG_CANCELED;
+ }
+
+ // Publish the key event.
+ status = connection->inputPublisher.publishKeyEvent(keyEntry->deviceId, keyEntry->source,
+ action, flags, keyEntry->keyCode, keyEntry->scanCode,
+ keyEntry->metaState, keyEntry->repeatCount, keyEntry->downTime,
+ keyEntry->eventTime);
+
+ if (status) {
+ LOGE("channel '%s' ~ Could not publish key event, "
+ "status=%d", connection->getInputChannelName(), status);
+ abortDispatchCycleLocked(currentTime, connection, true /*broken*/);
+ return;
+ }
+ break;
+ }
+
+ case EventEntry::TYPE_MOTION: {
+ MotionEntry* motionEntry = static_cast<MotionEntry*>(dispatchEntry->eventEntry);
+
+ // Apply target flags.
+ int32_t action = motionEntry->action;
+ int32_t flags = motionEntry->flags;
+ if (dispatchEntry->targetFlags & InputTarget::FLAG_OUTSIDE) {
+ action = AMOTION_EVENT_ACTION_OUTSIDE;
+ }
+ if (dispatchEntry->targetFlags & InputTarget::FLAG_CANCEL) {
+ action = AMOTION_EVENT_ACTION_CANCEL;
+ }
+ if (dispatchEntry->targetFlags & InputTarget::FLAG_WINDOW_IS_OBSCURED) {
+ flags |= AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED;
+ }
+
+ // If headMotionSample is non-NULL, then it points to the first new sample that we
+ // were unable to dispatch during the previous cycle so we resume dispatching from
+ // that point in the list of motion samples.
+ // Otherwise, we just start from the first sample of the motion event.
+ MotionSample* firstMotionSample = dispatchEntry->headMotionSample;
+ if (! firstMotionSample) {
+ firstMotionSample = & motionEntry->firstSample;
+ }
+
+ // Set the X and Y offset depending on the input source.
+ float xOffset, yOffset;
+ if (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) {
+ xOffset = dispatchEntry->xOffset;
+ yOffset = dispatchEntry->yOffset;
+ } else {
+ xOffset = 0.0f;
+ yOffset = 0.0f;
+ }
+
+ // Publish the motion event and the first motion sample.
+ status = connection->inputPublisher.publishMotionEvent(motionEntry->deviceId,
+ motionEntry->source, action, flags, motionEntry->edgeFlags, motionEntry->metaState,
+ xOffset, yOffset,
+ motionEntry->xPrecision, motionEntry->yPrecision,
+ motionEntry->downTime, firstMotionSample->eventTime,
+ motionEntry->pointerCount, motionEntry->pointerIds,
+ firstMotionSample->pointerCoords);
+
+ if (status) {
+ LOGE("channel '%s' ~ Could not publish motion event, "
+ "status=%d", connection->getInputChannelName(), status);
+ abortDispatchCycleLocked(currentTime, connection, true /*broken*/);
+ return;
+ }
+
+ // Append additional motion samples.
+ MotionSample* nextMotionSample = firstMotionSample->next;
+ for (; nextMotionSample != NULL; nextMotionSample = nextMotionSample->next) {
+ status = connection->inputPublisher.appendMotionSample(
+ nextMotionSample->eventTime, nextMotionSample->pointerCoords);
+ if (status == NO_MEMORY) {
+#if DEBUG_DISPATCH_CYCLE
+ LOGD("channel '%s' ~ Shared memory buffer full. Some motion samples will "
+ "be sent in the next dispatch cycle.",
+ connection->getInputChannelName());
+#endif
+ break;
+ }
+ if (status != OK) {
+ LOGE("channel '%s' ~ Could not append motion sample "
+ "for a reason other than out of memory, status=%d",
+ connection->getInputChannelName(), status);
+ abortDispatchCycleLocked(currentTime, connection, true /*broken*/);
+ return;
+ }
+ }
+
+ // Remember the next motion sample that we could not dispatch, in case we ran out
+ // of space in the shared memory buffer.
+ dispatchEntry->tailMotionSample = nextMotionSample;
+ break;
+ }
+
+ default: {
+ assert(false);
+ }
+ }
+
+ // Send the dispatch signal.
+ status = connection->inputPublisher.sendDispatchSignal();
+ if (status) {
+ LOGE("channel '%s' ~ Could not send dispatch signal, status=%d",
+ connection->getInputChannelName(), status);
+ abortDispatchCycleLocked(currentTime, connection, true /*broken*/);
+ return;
+ }
+
+ // Record information about the newly started dispatch cycle.
+ connection->lastEventTime = dispatchEntry->eventEntry->eventTime;
+ connection->lastDispatchTime = currentTime;
+
+ nsecs_t timeout = dispatchEntry->timeout - timeSpentWaitingForApplication;
+ connection->setNextTimeoutTime(currentTime, timeout);
+
+ // Notify other system components.
+ onDispatchCycleStartedLocked(currentTime, connection);
+}
+
+void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime,
+ const sp<Connection>& connection) {
+#if DEBUG_DISPATCH_CYCLE
+ LOGD("channel '%s' ~ finishDispatchCycle - %01.1fms since event, "
+ "%01.1fms since dispatch",
+ connection->getInputChannelName(),
+ connection->getEventLatencyMillis(currentTime),
+ connection->getDispatchLatencyMillis(currentTime));
+#endif
+
+ if (connection->status == Connection::STATUS_BROKEN
+ || connection->status == Connection::STATUS_ZOMBIE) {
+ return;
+ }
+
+ // Clear the pending timeout.
+ connection->nextTimeoutTime = LONG_LONG_MAX;
+
+ if (connection->status == Connection::STATUS_NOT_RESPONDING) {
+ // Recovering from an ANR.
+ connection->status = Connection::STATUS_NORMAL;
+
+ // Notify other system components.
+ onDispatchCycleFinishedLocked(currentTime, connection, true /*recoveredFromANR*/);
+ } else {
+ // Normal finish. Not much to do here.
+
+ // Notify other system components.
+ onDispatchCycleFinishedLocked(currentTime, connection, false /*recoveredFromANR*/);
+ }
+
+ // 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) {
+ LOGE("channel '%s' ~ Could not reset publisher, status=%d",
+ connection->getInputChannelName(), status);
+ abortDispatchCycleLocked(currentTime, connection, true /*broken*/);
+ return;
+ }
+
+ startNextDispatchCycleLocked(currentTime, connection);
+}
+
+void InputDispatcher::startNextDispatchCycleLocked(nsecs_t currentTime,
+ const sp<Connection>& connection) {
+ // Start the next dispatch cycle for this connection.
+ while (! connection->outboundQueue.isEmpty()) {
+ DispatchEntry* dispatchEntry = connection->outboundQueue.headSentinel.next;
+ if (dispatchEntry->inProgress) {
+ // Finish or resume current event in progress.
+ if (dispatchEntry->tailMotionSample) {
+ // We have a tail of undispatched motion samples.
+ // Reuse the same DispatchEntry and start a new cycle.
+ dispatchEntry->inProgress = false;
+ dispatchEntry->headMotionSample = dispatchEntry->tailMotionSample;
+ dispatchEntry->tailMotionSample = NULL;
+ startDispatchCycleLocked(currentTime, connection, 0);
+ return;
+ }
+ // Finished.
+ connection->outboundQueue.dequeueAtHead();
+ if (dispatchEntry->isSyncTarget()) {
+ decrementPendingSyncDispatchesLocked(dispatchEntry->eventEntry);
+ }
+ mAllocator.releaseDispatchEntry(dispatchEntry);
+ } else {
+ // If the head is not in progress, then we must have already dequeued the in
+ // progress event, which means we actually aborted it (due to ANR).
+ // So just start the next event for this connection.
+ startDispatchCycleLocked(currentTime, connection, 0);
+ return;
+ }
+ }
+
+ // Outbound queue is empty, deactivate the connection.
+ deactivateConnectionLocked(connection.get());
+}
+
+void InputDispatcher::timeoutDispatchCycleLocked(nsecs_t currentTime,
+ const sp<Connection>& connection) {
+#if DEBUG_DISPATCH_CYCLE
+ LOGD("channel '%s' ~ timeoutDispatchCycle",
+ connection->getInputChannelName());
+#endif
+
+ if (connection->status == Connection::STATUS_NORMAL) {
+ // Enter the not responding state.
+ connection->status = Connection::STATUS_NOT_RESPONDING;
+ connection->lastANRTime = currentTime;
+ } else if (connection->status != Connection::STATUS_NOT_RESPONDING) {
+ // Connection is broken or dead.
+ return;
+ }
+
+ // Notify other system components.
+ // This enqueues a command which will eventually call resumeAfterTimeoutDispatchCycleLocked.
+ onDispatchCycleANRLocked(currentTime, connection);
+}
+
+void InputDispatcher::resumeAfterTimeoutDispatchCycleLocked(nsecs_t currentTime,
+ const sp<Connection>& connection, nsecs_t newTimeout) {
+#if DEBUG_DISPATCH_CYCLE
+ LOGD("channel '%s' ~ resumeAfterTimeoutDispatchCycleLocked - newTimeout=%lld",
+ connection->getInputChannelName(), newTimeout);
+#endif
+
+ if (connection->status != Connection::STATUS_NOT_RESPONDING) {
+ return;
+ }
+
+ if (newTimeout > 0) {
+ // The system has decided to give the application some more time.
+ // Keep waiting synchronously and resume normal dispatch.
+ connection->status = Connection::STATUS_NORMAL;
+ connection->setNextTimeoutTime(currentTime, newTimeout);
+ } else {
+ // The system is about to throw up an ANR dialog and has requested that we abort dispatch.
+ // Reset the timeout.
+ connection->nextTimeoutTime = LONG_LONG_MAX;
+
+ // Input state will no longer be realistic.
+ connection->inputState.setOutOfSync();
+
+ if (! connection->outboundQueue.isEmpty()) {
+ // Make the current pending dispatch asynchronous (if it isn't already) so that
+ // subsequent events can be delivered to the ANR dialog or to another application.
+ DispatchEntry* currentDispatchEntry = connection->outboundQueue.headSentinel.next;
+ currentDispatchEntry->preemptSyncTarget();
+
+ // Drain all but the first entry in the outbound queue. We keep the first entry
+ // since that is the one that dispatch is stuck on. We throw away the others
+ // so that we don't spam the application with stale messages if it eventually
+ // wakes up and recovers from the ANR.
+ drainOutboundQueueLocked(connection.get(), currentDispatchEntry->next);
+ }
+ }
+}
+
+void InputDispatcher::abortDispatchCycleLocked(nsecs_t currentTime,
+ const sp<Connection>& connection, bool broken) {
+#if DEBUG_DISPATCH_CYCLE
+ LOGD("channel '%s' ~ abortDispatchCycle - broken=%s",
+ connection->getInputChannelName(), toString(broken));
+#endif
+
+ // Clear the pending timeout.
+ connection->nextTimeoutTime = LONG_LONG_MAX;
+
+ // Input state will no longer be realistic.
+ connection->inputState.setOutOfSync();
+
+ // Clear the outbound queue.
+ drainOutboundQueueLocked(connection.get(), connection->outboundQueue.headSentinel.next);
+
+ // Handle the case where the connection appears to be unrecoverably broken.
+ // Ignore already broken or zombie connections.
+ if (broken) {
+ if (connection->status == Connection::STATUS_NORMAL
+ || connection->status == Connection::STATUS_NOT_RESPONDING) {
+ connection->status = Connection::STATUS_BROKEN;
+
+ // Notify other system components.
+ onDispatchCycleBrokenLocked(currentTime, connection);
+ }
+ }
+}
+
+void InputDispatcher::drainOutboundQueueLocked(Connection* connection,
+ DispatchEntry* firstDispatchEntryToDrain) {
+ for (DispatchEntry* dispatchEntry = firstDispatchEntryToDrain;
+ dispatchEntry != & connection->outboundQueue.tailSentinel;) {
+ DispatchEntry* next = dispatchEntry->next;
+ connection->outboundQueue.dequeue(dispatchEntry);
+
+ if (dispatchEntry->isSyncTarget()) {
+ decrementPendingSyncDispatchesLocked(dispatchEntry->eventEntry);
+ }
+ mAllocator.releaseDispatchEntry(dispatchEntry);
+
+ dispatchEntry = next;
+ }
+
+ if (connection->outboundQueue.isEmpty()) {
+ deactivateConnectionLocked(connection);
+ }
+}
+
+bool InputDispatcher::handleReceiveCallback(int receiveFd, int events, void* data) {
+ InputDispatcher* d = static_cast<InputDispatcher*>(data);
+
+ { // acquire lock
+ AutoMutex _l(d->mLock);
+
+ ssize_t connectionIndex = d->mConnectionsByReceiveFd.indexOfKey(receiveFd);
+ if (connectionIndex < 0) {
+ LOGE("Received spurious receive callback for unknown input channel. "
+ "fd=%d, events=0x%x", receiveFd, events);
+ return false; // remove the callback
+ }
+
+ nsecs_t currentTime = now();
+
+ sp<Connection> connection = d->mConnectionsByReceiveFd.valueAt(connectionIndex);
+ if (events & (POLLERR | POLLHUP | POLLNVAL)) {
+ LOGE("channel '%s' ~ Consumer closed input channel or an error occurred. "
+ "events=0x%x", connection->getInputChannelName(), events);
+ d->abortDispatchCycleLocked(currentTime, connection, true /*broken*/);
+ d->runCommandsLockedInterruptible();
+ return false; // remove the callback
+ }
+
+ if (! (events & POLLIN)) {
+ LOGW("channel '%s' ~ Received spurious callback for unhandled poll event. "
+ "events=0x%x", connection->getInputChannelName(), events);
+ return true;
+ }
+
+ status_t status = connection->inputPublisher.receiveFinishedSignal();
+ if (status) {
+ LOGE("channel '%s' ~ Failed to receive finished signal. status=%d",
+ connection->getInputChannelName(), status);
+ d->abortDispatchCycleLocked(currentTime, connection, true /*broken*/);
+ d->runCommandsLockedInterruptible();
+ return false; // remove the callback
+ }
+
+ d->finishDispatchCycleLocked(currentTime, connection);
+ d->runCommandsLockedInterruptible();
+ return true;
+ } // release lock
+}
+
+void InputDispatcher::notifyConfigurationChanged(nsecs_t eventTime) {
+#if DEBUG_INBOUND_EVENT_DETAILS
+ LOGD("notifyConfigurationChanged - eventTime=%lld", eventTime);
+#endif
+
+ bool needWake;
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ ConfigurationChangedEntry* newEntry = mAllocator.obtainConfigurationChangedEntry(eventTime);
+ needWake = enqueueInboundEventLocked(newEntry);
+ } // release lock
+
+ if (needWake) {
+ mPollLoop->wake();
+ }
+}
+
+void InputDispatcher::notifyKey(nsecs_t eventTime, int32_t deviceId, int32_t source,
+ uint32_t policyFlags, int32_t action, int32_t flags,
+ int32_t keyCode, int32_t scanCode, int32_t metaState, nsecs_t downTime) {
+#if DEBUG_INBOUND_EVENT_DETAILS
+ LOGD("notifyKey - eventTime=%lld, deviceId=0x%x, source=0x%x, policyFlags=0x%x, action=0x%x, "
+ "flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, downTime=%lld",
+ eventTime, deviceId, source, policyFlags, action, flags,
+ keyCode, scanCode, metaState, downTime);
+#endif
+
+ bool needWake;
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ int32_t repeatCount = 0;
+ KeyEntry* newEntry = mAllocator.obtainKeyEntry(eventTime,
+ deviceId, source, policyFlags, action, flags, keyCode, scanCode,
+ metaState, repeatCount, downTime);
+
+ needWake = enqueueInboundEventLocked(newEntry);
+ } // release lock
+
+ if (needWake) {
+ mPollLoop->wake();
+ }
+}
+
+void InputDispatcher::notifyMotion(nsecs_t eventTime, int32_t deviceId, int32_t source,
+ uint32_t policyFlags, int32_t action, int32_t flags, int32_t metaState, int32_t edgeFlags,
+ uint32_t pointerCount, const int32_t* pointerIds, const PointerCoords* pointerCoords,
+ float xPrecision, float yPrecision, nsecs_t downTime) {
+#if DEBUG_INBOUND_EVENT_DETAILS
+ LOGD("notifyMotion - eventTime=%lld, deviceId=0x%x, source=0x%x, policyFlags=0x%x, "
+ "action=0x%x, flags=0x%x, metaState=0x%x, edgeFlags=0x%x, "
+ "xPrecision=%f, yPrecision=%f, downTime=%lld",
+ eventTime, deviceId, source, policyFlags, action, flags, metaState, edgeFlags,
+ xPrecision, yPrecision, downTime);
+ for (uint32_t i = 0; i < pointerCount; i++) {
+ LOGD(" Pointer %d: id=%d, x=%f, y=%f, pressure=%f, size=%f, "
+ "touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, "
+ "orientation=%f",
+ i, pointerIds[i], pointerCoords[i].x, pointerCoords[i].y,
+ pointerCoords[i].pressure, pointerCoords[i].size,
+ pointerCoords[i].touchMajor, pointerCoords[i].touchMinor,
+ pointerCoords[i].toolMajor, pointerCoords[i].toolMinor,
+ pointerCoords[i].orientation);
+ }
+#endif
+
+ bool needWake;
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ // Attempt batching and streaming of move events.
+ if (action == AMOTION_EVENT_ACTION_MOVE) {
+ // BATCHING CASE
+ //
+ // Try to append a move sample to the tail of the inbound queue for this device.
+ // Give up if we encounter a non-move motion event for this device since that
+ // means we cannot append any new samples until a new motion event has started.
+ for (EventEntry* entry = mInboundQueue.tailSentinel.prev;
+ entry != & mInboundQueue.headSentinel; entry = entry->prev) {
+ if (entry->type != EventEntry::TYPE_MOTION) {
+ // Keep looking for motion events.
+ continue;
+ }
+
+ MotionEntry* motionEntry = static_cast<MotionEntry*>(entry);
+ if (motionEntry->deviceId != deviceId) {
+ // Keep looking for this device.
+ continue;
+ }
+
+ if (motionEntry->action != AMOTION_EVENT_ACTION_MOVE
+ || motionEntry->pointerCount != pointerCount
+ || motionEntry->isInjected()) {
+ // Last motion event in the queue for this device is not compatible for
+ // appending new samples. Stop here.
+ goto NoBatchingOrStreaming;
+ }
+
+ // The last motion event is a move and is compatible for appending.
+ // Do the batching magic.
+ mAllocator.appendMotionSample(motionEntry, eventTime, pointerCoords);
+#if DEBUG_BATCHING
+ LOGD("Appended motion sample onto batch for most recent "
+ "motion event for this device in the inbound queue.");
+#endif
+ return; // done!
+ }
+
+ // STREAMING CASE
+ //
+ // There is no pending motion event (of any kind) for this device in the inbound queue.
+ // Search the outbound queues for a synchronously dispatched motion event for this
+ // device. If found, then we append the new sample to that event and then try to
+ // push it out to all current targets. It is possible that some targets will already
+ // have consumed the motion event. This case is automatically handled by the
+ // logic in prepareDispatchCycleLocked by tracking where resumption takes place.
+ //
+ // The reason we look for a synchronously dispatched motion event is because we
+ // want to be sure that no other motion events have been dispatched since the move.
+ // It's also convenient because it means that the input targets are still valid.
+ // This code could be improved to support streaming of asynchronously dispatched
+ // motion events (which might be significantly more efficient) but it may become
+ // a little more complicated as a result.
+ //
+ // Note: This code crucially depends on the invariant that an outbound queue always
+ // contains at most one synchronous event and it is always last (but it might
+ // not be first!).
+ if (mCurrentInputTargetsValid) {
+ for (size_t i = 0; i < mActiveConnections.size(); i++) {
+ Connection* connection = mActiveConnections.itemAt(i);
+ if (! connection->outboundQueue.isEmpty()) {
+ DispatchEntry* dispatchEntry = connection->outboundQueue.tailSentinel.prev;
+ if (dispatchEntry->isSyncTarget()) {
+ if (dispatchEntry->eventEntry->type != EventEntry::TYPE_MOTION) {
+ goto NoBatchingOrStreaming;
+ }
+
+ MotionEntry* syncedMotionEntry = static_cast<MotionEntry*>(
+ dispatchEntry->eventEntry);
+ if (syncedMotionEntry->action != AMOTION_EVENT_ACTION_MOVE
+ || syncedMotionEntry->deviceId != deviceId
+ || syncedMotionEntry->pointerCount != pointerCount
+ || syncedMotionEntry->isInjected()) {
+ goto NoBatchingOrStreaming;
+ }
+
+ // Found synced move entry. Append sample and resume dispatch.
+ mAllocator.appendMotionSample(syncedMotionEntry, eventTime,
+ pointerCoords);
+ #if DEBUG_BATCHING
+ LOGD("Appended motion sample onto batch for most recent synchronously "
+ "dispatched motion event for this device in the outbound queues.");
+ #endif
+ nsecs_t currentTime = now();
+ dispatchEventToCurrentInputTargetsLocked(currentTime, syncedMotionEntry,
+ true /*resumeWithAppendedMotionSample*/);
+
+ runCommandsLockedInterruptible();
+ return; // done!
+ }
+ }
+ }
+ }
+
+NoBatchingOrStreaming:;
+ }
+
+ // Just enqueue a new motion event.
+ MotionEntry* newEntry = mAllocator.obtainMotionEntry(eventTime,
+ deviceId, source, policyFlags, action, flags, metaState, edgeFlags,
+ xPrecision, yPrecision, downTime,
+ pointerCount, pointerIds, pointerCoords);
+
+ needWake = enqueueInboundEventLocked(newEntry);
+ } // release lock
+
+ if (needWake) {
+ mPollLoop->wake();
+ }
+}
+
+int32_t InputDispatcher::injectInputEvent(const InputEvent* event,
+ int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis) {
+#if DEBUG_INBOUND_EVENT_DETAILS
+ LOGD("injectInputEvent - eventType=%d, injectorPid=%d, injectorUid=%d, "
+ "syncMode=%d, timeoutMillis=%d",
+ event->getType(), injectorPid, injectorUid, syncMode, timeoutMillis);
+#endif
+
+ nsecs_t endTime = now() + milliseconds_to_nanoseconds(timeoutMillis);
+
+ EventEntry* injectedEntry;
+ bool needWake;
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ injectedEntry = createEntryFromInjectedInputEventLocked(event);
+ if (! injectedEntry) {
+ return INPUT_EVENT_INJECTION_FAILED;
+ }
+
+ injectedEntry->refCount += 1;
+ injectedEntry->injectorPid = injectorPid;
+ injectedEntry->injectorUid = injectorUid;
+
+ if (syncMode == INPUT_EVENT_INJECTION_SYNC_NONE) {
+ injectedEntry->injectionIsAsync = true;
+ }
+
+ needWake = enqueueInboundEventLocked(injectedEntry);
+ } // release lock
+
+ if (needWake) {
+ mPollLoop->wake();
+ }
+
+ int32_t injectionResult;
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ if (syncMode == INPUT_EVENT_INJECTION_SYNC_NONE) {
+ injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
+ } else {
+ for (;;) {
+ injectionResult = injectedEntry->injectionResult;
+ if (injectionResult != INPUT_EVENT_INJECTION_PENDING) {
+ break;
+ }
+
+ nsecs_t remainingTimeout = endTime - now();
+ if (remainingTimeout <= 0) {
+#if DEBUG_INJECTION
+ LOGD("injectInputEvent - Timed out waiting for injection result "
+ "to become available.");
+#endif
+ injectionResult = INPUT_EVENT_INJECTION_TIMED_OUT;
+ break;
+ }
+
+ mInjectionResultAvailableCondition.waitRelative(mLock, remainingTimeout);
+ }
+
+ if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED
+ && syncMode == INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISHED) {
+ while (injectedEntry->pendingSyncDispatches != 0) {
+#if DEBUG_INJECTION
+ LOGD("injectInputEvent - Waiting for %d pending synchronous dispatches.",
+ injectedEntry->pendingSyncDispatches);
+#endif
+ nsecs_t remainingTimeout = endTime - now();
+ if (remainingTimeout <= 0) {
+#if DEBUG_INJECTION
+ LOGD("injectInputEvent - Timed out waiting for pending synchronous "
+ "dispatches to finish.");
+#endif
+ injectionResult = INPUT_EVENT_INJECTION_TIMED_OUT;
+ break;
+ }
+
+ mInjectionSyncFinishedCondition.waitRelative(mLock, remainingTimeout);
+ }
+ }
+ }
+
+ mAllocator.releaseEventEntry(injectedEntry);
+ } // release lock
+
+#if DEBUG_INJECTION
+ LOGD("injectInputEvent - Finished with result %d. "
+ "injectorPid=%d, injectorUid=%d",
+ injectionResult, injectorPid, injectorUid);
+#endif
+
+ return injectionResult;
+}
+
+void InputDispatcher::setInjectionResultLocked(EventEntry* entry, int32_t injectionResult) {
+ if (entry->isInjected()) {
+#if DEBUG_INJECTION
+ LOGD("Setting input event injection result to %d. "
+ "injectorPid=%d, injectorUid=%d",
+ injectionResult, entry->injectorPid, entry->injectorUid);
+#endif
+
+ if (entry->injectionIsAsync) {
+ // Log the outcome since the injector did not wait for the injection result.
+ switch (injectionResult) {
+ case INPUT_EVENT_INJECTION_SUCCEEDED:
+ LOGV("Asynchronous input event injection succeeded.");
+ break;
+ case INPUT_EVENT_INJECTION_FAILED:
+ LOGW("Asynchronous input event injection failed.");
+ break;
+ case INPUT_EVENT_INJECTION_PERMISSION_DENIED:
+ LOGW("Asynchronous input event injection permission denied.");
+ break;
+ case INPUT_EVENT_INJECTION_TIMED_OUT:
+ LOGW("Asynchronous input event injection timed out.");
+ break;
+ }
+ }
+
+ entry->injectionResult = injectionResult;
+ mInjectionResultAvailableCondition.broadcast();
+ }
+}
+
+void InputDispatcher::decrementPendingSyncDispatchesLocked(EventEntry* entry) {
+ entry->pendingSyncDispatches -= 1;
+
+ if (entry->isInjected() && entry->pendingSyncDispatches == 0) {
+ mInjectionSyncFinishedCondition.broadcast();
+ }
+}
+
+static bool isValidKeyAction(int32_t action) {
+ switch (action) {
+ case AKEY_EVENT_ACTION_DOWN:
+ case AKEY_EVENT_ACTION_UP:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool isValidMotionAction(int32_t action) {
+ switch (action & AMOTION_EVENT_ACTION_MASK) {
+ case AMOTION_EVENT_ACTION_DOWN:
+ case AMOTION_EVENT_ACTION_UP:
+ case AMOTION_EVENT_ACTION_CANCEL:
+ case AMOTION_EVENT_ACTION_MOVE:
+ case AMOTION_EVENT_ACTION_POINTER_DOWN:
+ case AMOTION_EVENT_ACTION_POINTER_UP:
+ case AMOTION_EVENT_ACTION_OUTSIDE:
+ return true;
+ default:
+ return false;
+ }
+}
+
+InputDispatcher::EventEntry* InputDispatcher::createEntryFromInjectedInputEventLocked(
+ const InputEvent* event) {
+ switch (event->getType()) {
+ case AINPUT_EVENT_TYPE_KEY: {
+ const KeyEvent* keyEvent = static_cast<const KeyEvent*>(event);
+ if (! isValidKeyAction(keyEvent->getAction())) {
+ LOGE("Dropping injected key event since it has invalid action code 0x%x",
+ keyEvent->getAction());
+ return NULL;
+ }
+
+ uint32_t policyFlags = POLICY_FLAG_INJECTED;
+
+ KeyEntry* keyEntry = mAllocator.obtainKeyEntry(keyEvent->getEventTime(),
+ keyEvent->getDeviceId(), keyEvent->getSource(), policyFlags,
+ keyEvent->getAction(), keyEvent->getFlags(),
+ keyEvent->getKeyCode(), keyEvent->getScanCode(), keyEvent->getMetaState(),
+ keyEvent->getRepeatCount(), keyEvent->getDownTime());
+ return keyEntry;
+ }
+
+ case AINPUT_EVENT_TYPE_MOTION: {
+ const MotionEvent* motionEvent = static_cast<const MotionEvent*>(event);
+ if (! isValidMotionAction(motionEvent->getAction())) {
+ LOGE("Dropping injected motion event since it has invalid action code 0x%x.",
+ motionEvent->getAction());
+ return NULL;
+ }
+ if (motionEvent->getPointerCount() == 0
+ || motionEvent->getPointerCount() > MAX_POINTERS) {
+ LOGE("Dropping injected motion event since it has an invalid pointer count %d.",
+ motionEvent->getPointerCount());
+ }
+
+ uint32_t policyFlags = POLICY_FLAG_INJECTED;
+
+ const nsecs_t* sampleEventTimes = motionEvent->getSampleEventTimes();
+ const PointerCoords* samplePointerCoords = motionEvent->getSamplePointerCoords();
+ size_t pointerCount = motionEvent->getPointerCount();
+
+ MotionEntry* motionEntry = mAllocator.obtainMotionEntry(*sampleEventTimes,
+ motionEvent->getDeviceId(), motionEvent->getSource(), policyFlags,
+ motionEvent->getAction(), motionEvent->getFlags(),
+ motionEvent->getMetaState(), motionEvent->getEdgeFlags(),
+ motionEvent->getXPrecision(), motionEvent->getYPrecision(),
+ motionEvent->getDownTime(), uint32_t(pointerCount),
+ motionEvent->getPointerIds(), samplePointerCoords);
+ for (size_t i = motionEvent->getHistorySize(); i > 0; i--) {
+ sampleEventTimes += 1;
+ samplePointerCoords += pointerCount;
+ mAllocator.appendMotionSample(motionEntry, *sampleEventTimes, samplePointerCoords);
+ }
+ return motionEntry;
+ }
+
+ default:
+ assert(false);
+ return NULL;
+ }
+}
+
+void InputDispatcher::setInputWindows(const Vector<InputWindow>& inputWindows) {
+#if DEBUG_FOCUS
+ LOGD("setInputWindows");
+#endif
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ sp<InputChannel> touchedWindowChannel;
+ if (mTouchedWindow) {
+ touchedWindowChannel = mTouchedWindow->inputChannel;
+ mTouchedWindow = NULL;
+ }
+ size_t numTouchedWallpapers = mTouchedWallpaperWindows.size();
+ if (numTouchedWallpapers != 0) {
+ for (size_t i = 0; i < numTouchedWallpapers; i++) {
+ mTempTouchedWallpaperChannels.push(mTouchedWallpaperWindows[i]->inputChannel);
+ }
+ mTouchedWallpaperWindows.clear();
+ }
+
+ bool hadFocusedWindow = mFocusedWindow != NULL;
+
+ mFocusedWindow = NULL;
+ mWallpaperWindows.clear();
+
+ mWindows.clear();
+ mWindows.appendVector(inputWindows);
+
+ size_t numWindows = mWindows.size();
+ for (size_t i = 0; i < numWindows; i++) {
+ InputWindow* window = & mWindows.editItemAt(i);
+ if (window->hasFocus) {
+ mFocusedWindow = window;
+ }
+
+ if (window->layoutParamsType == InputWindow::TYPE_WALLPAPER) {
+ mWallpaperWindows.push(window);
+
+ for (size_t j = 0; j < numTouchedWallpapers; j++) {
+ if (window->inputChannel == mTempTouchedWallpaperChannels[i]) {
+ mTouchedWallpaperWindows.push(window);
+ }
+ }
+ }
+
+ if (window->inputChannel == touchedWindowChannel) {
+ mTouchedWindow = window;
+ }
+ }
+
+ mTempTouchedWallpaperChannels.clear();
+
+ if ((hadFocusedWindow && ! mFocusedWindow)
+ || (mFocusedWindow && ! mFocusedWindow->visible)) {
+ preemptInputDispatchInnerLocked();
+ }
+
+#if DEBUG_FOCUS
+ logDispatchStateLocked();
+#endif
+ } // release lock
+
+ // Wake up poll loop since it may need to make new input dispatching choices.
+ mPollLoop->wake();
+}
+
+void InputDispatcher::setFocusedApplication(const InputApplication* inputApplication) {
+#if DEBUG_FOCUS
+ LOGD("setFocusedApplication");
+#endif
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ releaseFocusedApplicationLocked();
+
+ if (inputApplication) {
+ mFocusedApplicationStorage = *inputApplication;
+ mFocusedApplication = & mFocusedApplicationStorage;
+ }
+
+#if DEBUG_FOCUS
+ logDispatchStateLocked();
+#endif
+ } // release lock
+
+ // Wake up poll loop since it may need to make new input dispatching choices.
+ mPollLoop->wake();
+}
+
+void InputDispatcher::releaseFocusedApplicationLocked() {
+ if (mFocusedApplication) {
+ mFocusedApplication = NULL;
+ mFocusedApplicationStorage.handle.clear();
+ }
+}
+
+void InputDispatcher::setInputDispatchMode(bool enabled, bool frozen) {
+#if DEBUG_FOCUS
+ LOGD("setInputDispatchMode: enabled=%d, frozen=%d", enabled, frozen);
+#endif
+
+ bool changed;
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ if (mDispatchEnabled != enabled || mDispatchFrozen != frozen) {
+ if (mDispatchFrozen && ! frozen) {
+ resetANRTimeoutsLocked();
+ }
+
+ mDispatchEnabled = enabled;
+ mDispatchFrozen = frozen;
+ changed = true;
+ } else {
+ changed = false;
+ }
+
+#if DEBUG_FOCUS
+ logDispatchStateLocked();
+#endif
+ } // release lock
+
+ if (changed) {
+ // Wake up poll loop since it may need to make new input dispatching choices.
+ mPollLoop->wake();
+ }
+}
+
+void InputDispatcher::preemptInputDispatch() {
+#if DEBUG_FOCUS
+ LOGD("preemptInputDispatch");
+#endif
+
+ bool preemptedOne;
+ { // acquire lock
+ AutoMutex _l(mLock);
+ preemptedOne = preemptInputDispatchInnerLocked();
+ } // release lock
+
+ if (preemptedOne) {
+ // Wake up the poll loop so it can get a head start dispatching the next event.
+ mPollLoop->wake();
+ }
+}
+
+bool InputDispatcher::preemptInputDispatchInnerLocked() {
+ bool preemptedOne = false;
+ for (size_t i = 0; i < mActiveConnections.size(); i++) {
+ Connection* connection = mActiveConnections[i];
+ if (connection->hasPendingSyncTarget()) {
+#if DEBUG_DISPATCH_CYCLE
+ LOGD("channel '%s' ~ Preempted pending synchronous dispatch",
+ connection->getInputChannelName());
+#endif
+ connection->preemptSyncTarget();
+ preemptedOne = true;
+ }
+ }
+ return preemptedOne;
+}
+
+void InputDispatcher::logDispatchStateLocked() {
+ String8 dump;
+ dumpDispatchStateLocked(dump);
+ LOGD("%s", dump.string());
+}
+
+void InputDispatcher::dumpDispatchStateLocked(String8& dump) {
+ dump.appendFormat(" dispatchEnabled: %d\n", mDispatchEnabled);
+ dump.appendFormat(" dispatchFrozen: %d\n", mDispatchFrozen);
+
+ if (mFocusedApplication) {
+ dump.appendFormat(" focusedApplication: name='%s', dispatchingTimeout=%0.3fms\n",
+ mFocusedApplication->name.string(),
+ mFocusedApplication->dispatchingTimeout / 1000000.0);
+ } else {
+ dump.append(" focusedApplication: <null>\n");
+ }
+ dump.appendFormat(" focusedWindow: '%s'\n",
+ mFocusedWindow != NULL ? mFocusedWindow->inputChannel->getName().string() : "<null>");
+ dump.appendFormat(" touchedWindow: '%s', touchDown=%d\n",
+ mTouchedWindow != NULL ? mTouchedWindow->inputChannel->getName().string() : "<null>",
+ mTouchDown);
+ for (size_t i = 0; i < mTouchedWallpaperWindows.size(); i++) {
+ dump.appendFormat(" touchedWallpaperWindows[%d]: '%s'\n",
+ i, mTouchedWallpaperWindows[i]->inputChannel->getName().string());
+ }
+ for (size_t i = 0; i < mWindows.size(); i++) {
+ dump.appendFormat(" windows[%d]: '%s', paused=%s, hasFocus=%s, hasWallpaper=%s, "
+ "visible=%s, flags=0x%08x, type=0x%08x, "
+ "frame=[%d,%d][%d,%d], "
+ "visibleFrame=[%d,%d][%d,%d], "
+ "touchableArea=[%d,%d][%d,%d], "
+ "ownerPid=%d, ownerUid=%d, dispatchingTimeout=%0.3fms\n",
+ i, mWindows[i].inputChannel->getName().string(),
+ toString(mWindows[i].paused),
+ toString(mWindows[i].hasFocus),
+ toString(mWindows[i].hasWallpaper),
+ toString(mWindows[i].visible),
+ mWindows[i].layoutParamsFlags, mWindows[i].layoutParamsType,
+ mWindows[i].frameLeft, mWindows[i].frameTop,
+ mWindows[i].frameRight, mWindows[i].frameBottom,
+ mWindows[i].visibleFrameLeft, mWindows[i].visibleFrameTop,
+ mWindows[i].visibleFrameRight, mWindows[i].visibleFrameBottom,
+ mWindows[i].touchableAreaLeft, mWindows[i].touchableAreaTop,
+ mWindows[i].touchableAreaRight, mWindows[i].touchableAreaBottom,
+ mWindows[i].ownerPid, mWindows[i].ownerUid,
+ mWindows[i].dispatchingTimeout / 1000000.0);
+ }
+
+ for (size_t i = 0; i < mMonitoringChannels.size(); i++) {
+ const sp<InputChannel>& channel = mMonitoringChannels[i];
+ dump.appendFormat(" monitoringChannel[%d]: '%s'\n",
+ i, channel->getName().string());
+ }
+
+ for (size_t i = 0; i < mActiveConnections.size(); i++) {
+ const Connection* connection = mActiveConnections[i];
+ dump.appendFormat(" activeConnection[%d]: '%s', status=%s, hasPendingSyncTarget=%s, "
+ "inputState.isNeutral=%s, inputState.isOutOfSync=%s\n",
+ i, connection->getInputChannelName(), connection->getStatusLabel(),
+ toString(connection->hasPendingSyncTarget()),
+ toString(connection->inputState.isNeutral()),
+ toString(connection->inputState.isOutOfSync()));
+ }
+
+ if (isAppSwitchPendingLocked()) {
+ dump.appendFormat(" appSwitch: pending, due in %01.1fms\n",
+ (mAppSwitchDueTime - now()) / 1000000.0);
+ } else {
+ dump.append(" appSwitch: not pending\n");
+ }
+}
+
+status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel, bool monitor) {
+#if DEBUG_REGISTRATION
+ LOGD("channel '%s' ~ registerInputChannel - monitor=%s", inputChannel->getName().string(),
+ toString(monitor));
+#endif
+
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ if (getConnectionIndex(inputChannel) >= 0) {
+ LOGW("Attempted to register already registered input channel '%s'",
+ inputChannel->getName().string());
+ return BAD_VALUE;
+ }
+
+ sp<Connection> connection = new Connection(inputChannel);
+ status_t status = connection->initialize();
+ if (status) {
+ LOGE("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);
+
+ if (monitor) {
+ mMonitoringChannels.push(inputChannel);
+ }
+
+ mPollLoop->setCallback(receiveFd, POLLIN, handleReceiveCallback, this);
+
+ runCommandsLockedInterruptible();
+ } // release lock
+ return OK;
+}
+
+status_t InputDispatcher::unregisterInputChannel(const sp<InputChannel>& inputChannel) {
+#if DEBUG_REGISTRATION
+ LOGD("channel '%s' ~ unregisterInputChannel", inputChannel->getName().string());
+#endif
+
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ ssize_t connectionIndex = getConnectionIndex(inputChannel);
+ if (connectionIndex < 0) {
+ LOGW("Attempted to unregister already unregistered input channel '%s'",
+ inputChannel->getName().string());
+ return BAD_VALUE;
+ }
+
+ sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
+ mConnectionsByReceiveFd.removeItemsAt(connectionIndex);
+
+ connection->status = Connection::STATUS_ZOMBIE;
+
+ for (size_t i = 0; i < mMonitoringChannels.size(); i++) {
+ if (mMonitoringChannels[i] == inputChannel) {
+ mMonitoringChannels.removeAt(i);
+ break;
+ }
+ }
+
+ mPollLoop->removeCallback(inputChannel->getReceivePipeFd());
+
+ nsecs_t currentTime = now();
+ abortDispatchCycleLocked(currentTime, connection, true /*broken*/);
+
+ runCommandsLockedInterruptible();
+ } // release lock
+
+ // Wake the poll loop because removing the connection may have changed the current
+ // synchronization state.
+ mPollLoop->wake();
+ return OK;
+}
+
+ssize_t InputDispatcher::getConnectionIndex(const sp<InputChannel>& inputChannel) {
+ ssize_t connectionIndex = mConnectionsByReceiveFd.indexOfKey(inputChannel->getReceivePipeFd());
+ if (connectionIndex >= 0) {
+ sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
+ if (connection->inputChannel.get() == inputChannel.get()) {
+ return connectionIndex;
+ }
+ }
+
+ return -1;
+}
+
+void InputDispatcher::activateConnectionLocked(Connection* connection) {
+ for (size_t i = 0; i < mActiveConnections.size(); i++) {
+ if (mActiveConnections.itemAt(i) == connection) {
+ return;
+ }
+ }
+ mActiveConnections.add(connection);
+}
+
+void InputDispatcher::deactivateConnectionLocked(Connection* connection) {
+ for (size_t i = 0; i < mActiveConnections.size(); i++) {
+ if (mActiveConnections.itemAt(i) == connection) {
+ mActiveConnections.removeAt(i);
+ return;
+ }
+ }
+}
+
+void InputDispatcher::onDispatchCycleStartedLocked(
+ nsecs_t currentTime, const sp<Connection>& connection) {
+}
+
+void InputDispatcher::onDispatchCycleFinishedLocked(
+ nsecs_t currentTime, const sp<Connection>& connection, bool recoveredFromANR) {
+ if (recoveredFromANR) {
+ LOGI("channel '%s' ~ Recovered from ANR. %01.1fms since event, "
+ "%01.1fms since dispatch, %01.1fms since ANR",
+ connection->getInputChannelName(),
+ connection->getEventLatencyMillis(currentTime),
+ connection->getDispatchLatencyMillis(currentTime),
+ connection->getANRLatencyMillis(currentTime));
+
+ CommandEntry* commandEntry = postCommandLocked(
+ & InputDispatcher::doNotifyInputChannelRecoveredFromANRLockedInterruptible);
+ commandEntry->connection = connection;
+ }
+}
+
+void InputDispatcher::onDispatchCycleANRLocked(
+ nsecs_t currentTime, const sp<Connection>& connection) {
+ LOGI("channel '%s' ~ Not responding! %01.1fms since event, %01.1fms since dispatch",
+ connection->getInputChannelName(),
+ connection->getEventLatencyMillis(currentTime),
+ connection->getDispatchLatencyMillis(currentTime));
+
+ CommandEntry* commandEntry = postCommandLocked(
+ & InputDispatcher::doNotifyInputChannelANRLockedInterruptible);
+ commandEntry->connection = connection;
+}
+
+void InputDispatcher::onDispatchCycleBrokenLocked(
+ nsecs_t currentTime, const sp<Connection>& connection) {
+ LOGE("channel '%s' ~ Channel is unrecoverably broken and will be disposed!",
+ connection->getInputChannelName());
+
+ CommandEntry* commandEntry = postCommandLocked(
+ & InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible);
+ commandEntry->connection = connection;
+}
+
+void InputDispatcher::doNotifyConfigurationChangedInterruptible(
+ CommandEntry* commandEntry) {
+ mLock.unlock();
+
+ mPolicy->notifyConfigurationChanged(commandEntry->eventTime);
+
+ mLock.lock();
+}
+
+void InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible(
+ CommandEntry* commandEntry) {
+ sp<Connection> connection = commandEntry->connection;
+
+ if (connection->status != Connection::STATUS_ZOMBIE) {
+ mLock.unlock();
+
+ mPolicy->notifyInputChannelBroken(connection->inputChannel);
+
+ mLock.lock();
+ }
+}
+
+void InputDispatcher::doNotifyInputChannelANRLockedInterruptible(
+ CommandEntry* commandEntry) {
+ sp<Connection> connection = commandEntry->connection;
+
+ if (connection->status != Connection::STATUS_ZOMBIE) {
+ mLock.unlock();
+
+ nsecs_t newTimeout = mPolicy->notifyInputChannelANR(connection->inputChannel);
+
+ mLock.lock();
+
+ nsecs_t currentTime = now();
+ resumeAfterTimeoutDispatchCycleLocked(currentTime, connection, newTimeout);
+ }
+}
+
+void InputDispatcher::doNotifyInputChannelRecoveredFromANRLockedInterruptible(
+ CommandEntry* commandEntry) {
+ sp<Connection> connection = commandEntry->connection;
+
+ if (connection->status != Connection::STATUS_ZOMBIE) {
+ mLock.unlock();
+
+ mPolicy->notifyInputChannelRecoveredFromANR(connection->inputChannel);
+
+ mLock.lock();
+ }
+}
+
+void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible(
+ CommandEntry* commandEntry) {
+ KeyEntry* entry = commandEntry->keyEntry;
+ mReusableKeyEvent.initialize(entry->deviceId, entry->source, entry->action, entry->flags,
+ entry->keyCode, entry->scanCode, entry->metaState, entry->repeatCount,
+ entry->downTime, entry->eventTime);
+
+ mLock.unlock();
+
+ bool consumed = mPolicy->interceptKeyBeforeDispatching(commandEntry->inputChannel,
+ & mReusableKeyEvent, entry->policyFlags);
+
+ mLock.lock();
+
+ entry->interceptKeyResult = consumed
+ ? KeyEntry::INTERCEPT_KEY_RESULT_SKIP
+ : KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE;
+ mAllocator.releaseKeyEntry(entry);
+}
+
+void InputDispatcher::doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry) {
+ mLock.unlock();
+
+ mPolicy->pokeUserActivity(commandEntry->eventTime, commandEntry->windowType,
+ commandEntry->userActivityEventType);
+
+ mLock.lock();
+}
+
+void InputDispatcher::doTargetsNotReadyTimeoutLockedInterruptible(
+ CommandEntry* commandEntry) {
+ mLock.unlock();
+
+ nsecs_t newTimeout;
+ if (commandEntry->inputChannel.get()) {
+ newTimeout = mPolicy->notifyInputChannelANR(commandEntry->inputChannel);
+ } else if (commandEntry->inputApplicationHandle.get()) {
+ newTimeout = mPolicy->notifyANR(commandEntry->inputApplicationHandle);
+ } else {
+ newTimeout = 0;
+ }
+
+ mLock.lock();
+
+ resumeAfterTargetsNotReadyTimeoutLocked(newTimeout);
+}
+
+void InputDispatcher::dump(String8& dump) {
+ dumpDispatchStateLocked(dump);
+}
+
+
+// --- InputDispatcher::Allocator ---
+
+InputDispatcher::Allocator::Allocator() {
+}
+
+void InputDispatcher::Allocator::initializeEventEntry(EventEntry* entry, int32_t type,
+ nsecs_t eventTime) {
+ entry->type = type;
+ entry->refCount = 1;
+ entry->dispatchInProgress = false;
+ entry->eventTime = eventTime;
+ entry->injectionResult = INPUT_EVENT_INJECTION_PENDING;
+ entry->injectionIsAsync = false;
+ entry->injectorPid = -1;
+ entry->injectorUid = -1;
+ entry->pendingSyncDispatches = 0;
+}
+
+InputDispatcher::ConfigurationChangedEntry*
+InputDispatcher::Allocator::obtainConfigurationChangedEntry(nsecs_t eventTime) {
+ ConfigurationChangedEntry* entry = mConfigurationChangeEntryPool.alloc();
+ initializeEventEntry(entry, EventEntry::TYPE_CONFIGURATION_CHANGED, eventTime);
+ return entry;
+}
+
+InputDispatcher::KeyEntry* InputDispatcher::Allocator::obtainKeyEntry(nsecs_t eventTime,
+ int32_t deviceId, int32_t source, uint32_t policyFlags, int32_t action,
+ int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState,
+ int32_t repeatCount, nsecs_t downTime) {
+ KeyEntry* entry = mKeyEntryPool.alloc();
+ initializeEventEntry(entry, EventEntry::TYPE_KEY, eventTime);
+
+ entry->deviceId = deviceId;
+ entry->source = source;
+ entry->policyFlags = policyFlags;
+ entry->action = action;
+ entry->flags = flags;
+ entry->keyCode = keyCode;
+ entry->scanCode = scanCode;
+ entry->metaState = metaState;
+ entry->repeatCount = repeatCount;
+ entry->downTime = downTime;
+ entry->syntheticRepeat = false;
+ entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN;
+ return entry;
+}
+
+InputDispatcher::MotionEntry* InputDispatcher::Allocator::obtainMotionEntry(nsecs_t eventTime,
+ int32_t deviceId, int32_t source, uint32_t policyFlags, int32_t action, int32_t flags,
+ int32_t metaState, int32_t edgeFlags, float xPrecision, float yPrecision,
+ nsecs_t downTime, uint32_t pointerCount,
+ const int32_t* pointerIds, const PointerCoords* pointerCoords) {
+ MotionEntry* entry = mMotionEntryPool.alloc();
+ initializeEventEntry(entry, EventEntry::TYPE_MOTION, eventTime);
+
+ entry->eventTime = eventTime;
+ entry->deviceId = deviceId;
+ entry->source = source;
+ entry->policyFlags = policyFlags;
+ entry->action = action;
+ entry->flags = flags;
+ entry->metaState = metaState;
+ entry->edgeFlags = edgeFlags;
+ entry->xPrecision = xPrecision;
+ entry->yPrecision = yPrecision;
+ entry->downTime = downTime;
+ entry->pointerCount = pointerCount;
+ entry->firstSample.eventTime = eventTime;
+ entry->firstSample.next = NULL;
+ entry->lastSample = & entry->firstSample;
+ for (uint32_t i = 0; i < pointerCount; i++) {
+ entry->pointerIds[i] = pointerIds[i];
+ entry->firstSample.pointerCoords[i] = pointerCoords[i];
+ }
+ return entry;
+}
+
+InputDispatcher::DispatchEntry* InputDispatcher::Allocator::obtainDispatchEntry(
+ EventEntry* eventEntry,
+ int32_t targetFlags, float xOffset, float yOffset, nsecs_t timeout) {
+ DispatchEntry* entry = mDispatchEntryPool.alloc();
+ entry->eventEntry = eventEntry;
+ eventEntry->refCount += 1;
+ entry->targetFlags = targetFlags;
+ entry->xOffset = xOffset;
+ entry->yOffset = yOffset;
+ entry->timeout = timeout;
+ entry->inProgress = false;
+ entry->headMotionSample = NULL;
+ entry->tailMotionSample = NULL;
+ return entry;
+}
+
+InputDispatcher::CommandEntry* InputDispatcher::Allocator::obtainCommandEntry(Command command) {
+ CommandEntry* entry = mCommandEntryPool.alloc();
+ entry->command = command;
+ return entry;
+}
+
+void InputDispatcher::Allocator::releaseEventEntry(EventEntry* entry) {
+ switch (entry->type) {
+ case EventEntry::TYPE_CONFIGURATION_CHANGED:
+ releaseConfigurationChangedEntry(static_cast<ConfigurationChangedEntry*>(entry));
+ break;
+ case EventEntry::TYPE_KEY:
+ releaseKeyEntry(static_cast<KeyEntry*>(entry));
+ break;
+ case EventEntry::TYPE_MOTION:
+ releaseMotionEntry(static_cast<MotionEntry*>(entry));
+ break;
+ default:
+ assert(false);
+ break;
+ }
+}
+
+void InputDispatcher::Allocator::releaseConfigurationChangedEntry(
+ ConfigurationChangedEntry* entry) {
+ entry->refCount -= 1;
+ if (entry->refCount == 0) {
+ mConfigurationChangeEntryPool.free(entry);
+ } else {
+ assert(entry->refCount > 0);
+ }
+}
+
+void InputDispatcher::Allocator::releaseKeyEntry(KeyEntry* entry) {
+ entry->refCount -= 1;
+ if (entry->refCount == 0) {
+ mKeyEntryPool.free(entry);
+ } else {
+ assert(entry->refCount > 0);
+ }
+}
+
+void InputDispatcher::Allocator::releaseMotionEntry(MotionEntry* entry) {
+ entry->refCount -= 1;
+ if (entry->refCount == 0) {
+ for (MotionSample* sample = entry->firstSample.next; sample != NULL; ) {
+ MotionSample* next = sample->next;
+ mMotionSamplePool.free(sample);
+ sample = next;
+ }
+ mMotionEntryPool.free(entry);
+ } else {
+ assert(entry->refCount > 0);
+ }
+}
+
+void InputDispatcher::Allocator::releaseDispatchEntry(DispatchEntry* entry) {
+ releaseEventEntry(entry->eventEntry);
+ mDispatchEntryPool.free(entry);
+}
+
+void InputDispatcher::Allocator::releaseCommandEntry(CommandEntry* entry) {
+ mCommandEntryPool.free(entry);
+}
+
+void InputDispatcher::Allocator::appendMotionSample(MotionEntry* motionEntry,
+ nsecs_t eventTime, const PointerCoords* pointerCoords) {
+ MotionSample* sample = mMotionSamplePool.alloc();
+ sample->eventTime = eventTime;
+ uint32_t pointerCount = motionEntry->pointerCount;
+ for (uint32_t i = 0; i < pointerCount; i++) {
+ sample->pointerCoords[i] = pointerCoords[i];
+ }
+
+ sample->next = NULL;
+ motionEntry->lastSample->next = sample;
+ motionEntry->lastSample = sample;
+}
+
+
+// --- InputDispatcher::EventEntry ---
+
+void InputDispatcher::EventEntry::recycle() {
+ injectionResult = INPUT_EVENT_INJECTION_PENDING;
+ dispatchInProgress = false;
+ pendingSyncDispatches = 0;
+}
+
+
+// --- InputDispatcher::KeyEntry ---
+
+void InputDispatcher::KeyEntry::recycle() {
+ EventEntry::recycle();
+ syntheticRepeat = false;
+ interceptKeyResult = INTERCEPT_KEY_RESULT_UNKNOWN;
+}
+
+
+// --- InputDispatcher::MotionEntry ---
+
+uint32_t InputDispatcher::MotionEntry::countSamples() const {
+ uint32_t count = 1;
+ for (MotionSample* sample = firstSample.next; sample != NULL; sample = sample->next) {
+ count += 1;
+ }
+ return count;
+}
+
+
+// --- InputDispatcher::InputState ---
+
+InputDispatcher::InputState::InputState() :
+ mIsOutOfSync(false) {
+}
+
+InputDispatcher::InputState::~InputState() {
+}
+
+bool InputDispatcher::InputState::isNeutral() const {
+ return mKeyMementos.isEmpty() && mMotionMementos.isEmpty();
+}
+
+bool InputDispatcher::InputState::isOutOfSync() const {
+ return mIsOutOfSync;
+}
+
+void InputDispatcher::InputState::setOutOfSync() {
+ if (! isNeutral()) {
+ mIsOutOfSync = true;
+ }
+}
+
+void InputDispatcher::InputState::resetOutOfSync() {
+ mIsOutOfSync = false;
+}
+
+InputDispatcher::InputState::Consistency InputDispatcher::InputState::trackEvent(
+ const EventEntry* entry) {
+ switch (entry->type) {
+ case EventEntry::TYPE_KEY:
+ return trackKey(static_cast<const KeyEntry*>(entry));
+
+ case EventEntry::TYPE_MOTION:
+ return trackMotion(static_cast<const MotionEntry*>(entry));
+
+ default:
+ return CONSISTENT;
+ }
+}
+
+InputDispatcher::InputState::Consistency InputDispatcher::InputState::trackKey(
+ const KeyEntry* entry) {
+ int32_t action = entry->action;
+ for (size_t i = 0; i < mKeyMementos.size(); i++) {
+ KeyMemento& memento = mKeyMementos.editItemAt(i);
+ if (memento.deviceId == entry->deviceId
+ && memento.source == entry->source
+ && memento.keyCode == entry->keyCode
+ && memento.scanCode == entry->scanCode) {
+ switch (action) {
+ case AKEY_EVENT_ACTION_UP:
+ mKeyMementos.removeAt(i);
+ if (isNeutral()) {
+ mIsOutOfSync = false;
+ }
+ return CONSISTENT;
+
+ case AKEY_EVENT_ACTION_DOWN:
+ return TOLERABLE;
+
+ default:
+ return BROKEN;
+ }
+ }
+ }
+
+ switch (action) {
+ case AKEY_EVENT_ACTION_DOWN: {
+ mKeyMementos.push();
+ KeyMemento& memento = mKeyMementos.editTop();
+ memento.deviceId = entry->deviceId;
+ memento.source = entry->source;
+ memento.keyCode = entry->keyCode;
+ memento.scanCode = entry->scanCode;
+ memento.downTime = entry->downTime;
+ return CONSISTENT;
+ }
+
+ default:
+ return BROKEN;
+ }
+}
+
+InputDispatcher::InputState::Consistency InputDispatcher::InputState::trackMotion(
+ const MotionEntry* entry) {
+ int32_t action = entry->action & AMOTION_EVENT_ACTION_MASK;
+ for (size_t i = 0; i < mMotionMementos.size(); i++) {
+ MotionMemento& memento = mMotionMementos.editItemAt(i);
+ if (memento.deviceId == entry->deviceId
+ && memento.source == entry->source) {
+ switch (action) {
+ case AMOTION_EVENT_ACTION_UP:
+ case AMOTION_EVENT_ACTION_CANCEL:
+ mMotionMementos.removeAt(i);
+ if (isNeutral()) {
+ mIsOutOfSync = false;
+ }
+ return CONSISTENT;
+
+ case AMOTION_EVENT_ACTION_DOWN:
+ return TOLERABLE;
+
+ case AMOTION_EVENT_ACTION_POINTER_DOWN:
+ if (entry->pointerCount == memento.pointerCount + 1) {
+ memento.setPointers(entry);
+ return CONSISTENT;
+ }
+ return BROKEN;
+
+ case AMOTION_EVENT_ACTION_POINTER_UP:
+ if (entry->pointerCount == memento.pointerCount - 1) {
+ memento.setPointers(entry);
+ return CONSISTENT;
+ }
+ return BROKEN;
+
+ case AMOTION_EVENT_ACTION_MOVE:
+ if (entry->pointerCount == memento.pointerCount) {
+ return CONSISTENT;
+ }
+ return BROKEN;
+
+ default:
+ return BROKEN;
+ }
+ }
+ }
+
+ switch (action) {
+ case AMOTION_EVENT_ACTION_DOWN: {
+ mMotionMementos.push();
+ MotionMemento& memento = mMotionMementos.editTop();
+ memento.deviceId = entry->deviceId;
+ memento.source = entry->source;
+ memento.xPrecision = entry->xPrecision;
+ memento.yPrecision = entry->yPrecision;
+ memento.downTime = entry->downTime;
+ memento.setPointers(entry);
+ return CONSISTENT;
+ }
+
+ default:
+ return BROKEN;
+ }
+}
+
+void InputDispatcher::InputState::MotionMemento::setPointers(const MotionEntry* entry) {
+ pointerCount = entry->pointerCount;
+ for (uint32_t i = 0; i < entry->pointerCount; i++) {
+ pointerIds[i] = entry->pointerIds[i];
+ pointerCoords[i] = entry->lastSample->pointerCoords[i];
+ }
+}
+
+void InputDispatcher::InputState::synthesizeCancelationEvents(
+ Allocator* allocator, Vector<EventEntry*>& outEvents) const {
+ for (size_t i = 0; i < mKeyMementos.size(); i++) {
+ const KeyMemento& memento = mKeyMementos.itemAt(i);
+ outEvents.push(allocator->obtainKeyEntry(now(),
+ memento.deviceId, memento.source, 0,
+ AKEY_EVENT_ACTION_UP, AKEY_EVENT_FLAG_CANCELED,
+ memento.keyCode, memento.scanCode, 0, 0, memento.downTime));
+ }
+
+ for (size_t i = 0; i < mMotionMementos.size(); i++) {
+ const MotionMemento& memento = mMotionMementos.itemAt(i);
+ outEvents.push(allocator->obtainMotionEntry(now(),
+ memento.deviceId, memento.source, 0,
+ AMOTION_EVENT_ACTION_CANCEL, 0, 0, 0,
+ memento.xPrecision, memento.yPrecision, memento.downTime,
+ memento.pointerCount, memento.pointerIds, memento.pointerCoords));
+ }
+}
+
+void InputDispatcher::InputState::clear() {
+ mKeyMementos.clear();
+ mMotionMementos.clear();
+ mIsOutOfSync = false;
+}
+
+
+// --- InputDispatcher::Connection ---
+
+InputDispatcher::Connection::Connection(const sp<InputChannel>& inputChannel) :
+ status(STATUS_NORMAL), inputChannel(inputChannel), inputPublisher(inputChannel),
+ nextTimeoutTime(LONG_LONG_MAX),
+ lastEventTime(LONG_LONG_MAX), lastDispatchTime(LONG_LONG_MAX),
+ lastANRTime(LONG_LONG_MAX) {
+}
+
+InputDispatcher::Connection::~Connection() {
+}
+
+status_t InputDispatcher::Connection::initialize() {
+ return inputPublisher.initialize();
+}
+
+void InputDispatcher::Connection::setNextTimeoutTime(nsecs_t currentTime, nsecs_t timeout) {
+ nextTimeoutTime = (timeout >= 0) ? currentTime + timeout : LONG_LONG_MAX;
+}
+
+void InputDispatcher::Connection::resetTimeout(nsecs_t currentTime) {
+ if (outboundQueue.isEmpty()) {
+ nextTimeoutTime = LONG_LONG_MAX;
+ } else {
+ setNextTimeoutTime(currentTime, outboundQueue.headSentinel.next->timeout);
+ }
+}
+
+const char* InputDispatcher::Connection::getStatusLabel() const {
+ switch (status) {
+ case STATUS_NORMAL:
+ return "NORMAL";
+
+ case STATUS_BROKEN:
+ return "BROKEN";
+
+ case STATUS_NOT_RESPONDING:
+ return "NOT_RESPONDING";
+
+ case STATUS_ZOMBIE:
+ return "ZOMBIE";
+
+ default:
+ return "UNKNOWN";
+ }
+}
+
+InputDispatcher::DispatchEntry* InputDispatcher::Connection::findQueuedDispatchEntryForEvent(
+ const EventEntry* eventEntry) const {
+ for (DispatchEntry* dispatchEntry = outboundQueue.tailSentinel.prev;
+ dispatchEntry != & outboundQueue.headSentinel; dispatchEntry = dispatchEntry->prev) {
+ if (dispatchEntry->eventEntry == eventEntry) {
+ return dispatchEntry;
+ }
+ }
+ return NULL;
+}
+
+
+// --- InputDispatcher::CommandEntry ---
+
+InputDispatcher::CommandEntry::CommandEntry() :
+ keyEntry(NULL) {
+}
+
+InputDispatcher::CommandEntry::~CommandEntry() {
+}
+
+
+// --- InputDispatcherThread ---
+
+InputDispatcherThread::InputDispatcherThread(const sp<InputDispatcherInterface>& dispatcher) :
+ Thread(/*canCallJava*/ true), mDispatcher(dispatcher) {
+}
+
+InputDispatcherThread::~InputDispatcherThread() {
+}
+
+bool InputDispatcherThread::threadLoop() {
+ mDispatcher->dispatchOnce();
+ return true;
+}
+
+} // namespace android
diff --git a/libs/ui/InputManager.cpp b/libs/ui/InputManager.cpp
new file mode 100644
index 000000000000..09fce38d9c18
--- /dev/null
+++ b/libs/ui/InputManager.cpp
@@ -0,0 +1,83 @@
+//
+// Copyright 2010 The Android Open Source Project
+//
+// The input manager.
+//
+#define LOG_TAG "InputManager"
+
+//#define LOG_NDEBUG 0
+
+#include <cutils/log.h>
+#include <ui/InputManager.h>
+#include <ui/InputReader.h>
+#include <ui/InputDispatcher.h>
+
+namespace android {
+
+InputManager::InputManager(
+ const sp<EventHubInterface>& eventHub,
+ const sp<InputReaderPolicyInterface>& readerPolicy,
+ const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
+ mDispatcher = new InputDispatcher(dispatcherPolicy);
+ mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
+ initialize();
+}
+
+InputManager::InputManager(
+ const sp<InputReaderInterface>& reader,
+ const sp<InputDispatcherInterface>& dispatcher) :
+ mReader(reader),
+ mDispatcher(dispatcher) {
+ initialize();
+}
+
+InputManager::~InputManager() {
+ stop();
+}
+
+void InputManager::initialize() {
+ mReaderThread = new InputReaderThread(mReader);
+ mDispatcherThread = new InputDispatcherThread(mDispatcher);
+}
+
+status_t InputManager::start() {
+ status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
+ if (result) {
+ LOGE("Could not start InputDispatcher thread due to error %d.", result);
+ return result;
+ }
+
+ result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
+ if (result) {
+ LOGE("Could not start InputReader thread due to error %d.", result);
+
+ mDispatcherThread->requestExit();
+ return result;
+ }
+
+ return OK;
+}
+
+status_t InputManager::stop() {
+ status_t result = mReaderThread->requestExitAndWait();
+ if (result) {
+ LOGW("Could not stop InputReader thread due to error %d.", result);
+ }
+
+ result = mDispatcherThread->requestExitAndWait();
+ if (result) {
+ LOGW("Could not stop InputDispatcher thread due to error %d.", result);
+ }
+
+ return OK;
+}
+
+sp<InputReaderInterface> InputManager::getReader() {
+ return mReader;
+}
+
+sp<InputDispatcherInterface> InputManager::getDispatcher() {
+ return mDispatcher;
+}
+
+} // namespace android
diff --git a/libs/ui/InputReader.cpp b/libs/ui/InputReader.cpp
new file mode 100644
index 000000000000..88084c019c28
--- /dev/null
+++ b/libs/ui/InputReader.cpp
@@ -0,0 +1,3411 @@
+//
+// Copyright 2010 The Android Open Source Project
+//
+// The input reader.
+//
+#define LOG_TAG "InputReader"
+
+//#define LOG_NDEBUG 0
+
+// Log debug messages for each raw event received from the EventHub.
+#define DEBUG_RAW_EVENTS 0
+
+// Log debug messages about touch screen filtering hacks.
+#define DEBUG_HACKS 0
+
+// Log debug messages about virtual key processing.
+#define DEBUG_VIRTUAL_KEYS 0
+
+// Log debug messages about pointers.
+#define DEBUG_POINTERS 0
+
+// Log debug messages about pointer assignment calculations.
+#define DEBUG_POINTER_ASSIGNMENT 0
+
+#include <cutils/log.h>
+#include <ui/InputReader.h>
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <limits.h>
+#include <math.h>
+
+#define INDENT " "
+
+namespace android {
+
+// --- Static Functions ---
+
+template<typename T>
+inline static T abs(const T& value) {
+ return value < 0 ? - value : value;
+}
+
+template<typename T>
+inline static T min(const T& a, const T& b) {
+ return a < b ? a : b;
+}
+
+template<typename T>
+inline static void swap(T& a, T& b) {
+ T temp = a;
+ a = b;
+ b = temp;
+}
+
+inline static float avg(float x, float y) {
+ return (x + y) / 2;
+}
+
+inline static float pythag(float x, float y) {
+ return sqrtf(x * x + y * y);
+}
+
+
+int32_t updateMetaState(int32_t keyCode, bool down, int32_t oldMetaState) {
+ int32_t mask;
+ switch (keyCode) {
+ case AKEYCODE_ALT_LEFT:
+ mask = AMETA_ALT_LEFT_ON;
+ break;
+ case AKEYCODE_ALT_RIGHT:
+ mask = AMETA_ALT_RIGHT_ON;
+ break;
+ case AKEYCODE_SHIFT_LEFT:
+ mask = AMETA_SHIFT_LEFT_ON;
+ break;
+ case AKEYCODE_SHIFT_RIGHT:
+ mask = AMETA_SHIFT_RIGHT_ON;
+ break;
+ case AKEYCODE_SYM:
+ mask = AMETA_SYM_ON;
+ break;
+ default:
+ return oldMetaState;
+ }
+
+ int32_t newMetaState = down ? oldMetaState | mask : oldMetaState & ~ mask
+ & ~ (AMETA_ALT_ON | AMETA_SHIFT_ON);
+
+ if (newMetaState & (AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON)) {
+ newMetaState |= AMETA_ALT_ON;
+ }
+
+ if (newMetaState & (AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_RIGHT_ON)) {
+ newMetaState |= AMETA_SHIFT_ON;
+ }
+
+ return newMetaState;
+}
+
+static const int32_t keyCodeRotationMap[][4] = {
+ // key codes enumerated counter-clockwise with the original (unrotated) key first
+ // no rotation, 90 degree rotation, 180 degree rotation, 270 degree rotation
+ { AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT },
+ { AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN },
+ { AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT },
+ { AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP },
+};
+static const int keyCodeRotationMapSize =
+ sizeof(keyCodeRotationMap) / sizeof(keyCodeRotationMap[0]);
+
+int32_t rotateKeyCode(int32_t keyCode, int32_t orientation) {
+ if (orientation != InputReaderPolicyInterface::ROTATION_0) {
+ for (int i = 0; i < keyCodeRotationMapSize; i++) {
+ if (keyCode == keyCodeRotationMap[i][0]) {
+ return keyCodeRotationMap[i][orientation];
+ }
+ }
+ }
+ return keyCode;
+}
+
+static inline bool sourcesMatchMask(uint32_t sources, uint32_t sourceMask) {
+ return (sources & sourceMask & ~ AINPUT_SOURCE_CLASS_MASK) != 0;
+}
+
+
+// --- InputDeviceCalibration ---
+
+InputDeviceCalibration::InputDeviceCalibration() {
+}
+
+void InputDeviceCalibration::clear() {
+ mProperties.clear();
+}
+
+void InputDeviceCalibration::addProperty(const String8& key, const String8& value) {
+ mProperties.add(key, value);
+}
+
+bool InputDeviceCalibration::tryGetProperty(const String8& key, String8& outValue) const {
+ ssize_t index = mProperties.indexOfKey(key);
+ if (index < 0) {
+ return false;
+ }
+
+ outValue = mProperties.valueAt(index);
+ return true;
+}
+
+bool InputDeviceCalibration::tryGetProperty(const String8& key, int32_t& outValue) const {
+ String8 stringValue;
+ if (! tryGetProperty(key, stringValue) || stringValue.length() == 0) {
+ return false;
+ }
+
+ char* end;
+ int value = strtol(stringValue.string(), & end, 10);
+ if (*end != '\0') {
+ LOGW("Input device calibration key '%s' has invalid value '%s'. Expected an integer.",
+ key.string(), stringValue.string());
+ return false;
+ }
+ outValue = value;
+ return true;
+}
+
+bool InputDeviceCalibration::tryGetProperty(const String8& key, float& outValue) const {
+ String8 stringValue;
+ if (! tryGetProperty(key, stringValue) || stringValue.length() == 0) {
+ return false;
+ }
+
+ char* end;
+ float value = strtof(stringValue.string(), & end);
+ if (*end != '\0') {
+ LOGW("Input device calibration key '%s' has invalid value '%s'. Expected a float.",
+ key.string(), stringValue.string());
+ return false;
+ }
+ outValue = value;
+ return true;
+}
+
+
+// --- InputReader ---
+
+InputReader::InputReader(const sp<EventHubInterface>& eventHub,
+ const sp<InputReaderPolicyInterface>& policy,
+ const sp<InputDispatcherInterface>& dispatcher) :
+ mEventHub(eventHub), mPolicy(policy), mDispatcher(dispatcher),
+ mGlobalMetaState(0) {
+ configureExcludedDevices();
+ updateGlobalMetaState();
+ updateInputConfiguration();
+}
+
+InputReader::~InputReader() {
+ for (size_t i = 0; i < mDevices.size(); i++) {
+ delete mDevices.valueAt(i);
+ }
+}
+
+void InputReader::loopOnce() {
+ RawEvent rawEvent;
+ mEventHub->getEvent(& rawEvent);
+
+#if DEBUG_RAW_EVENTS
+ LOGD("Input event: device=0x%x type=0x%x scancode=%d keycode=%d value=%d",
+ rawEvent.deviceId, rawEvent.type, rawEvent.scanCode, rawEvent.keyCode,
+ rawEvent.value);
+#endif
+
+ process(& rawEvent);
+}
+
+void InputReader::process(const RawEvent* rawEvent) {
+ switch (rawEvent->type) {
+ case EventHubInterface::DEVICE_ADDED:
+ addDevice(rawEvent->when, rawEvent->deviceId);
+ break;
+
+ case EventHubInterface::DEVICE_REMOVED:
+ removeDevice(rawEvent->when, rawEvent->deviceId);
+ break;
+
+ default:
+ consumeEvent(rawEvent);
+ break;
+ }
+}
+
+void InputReader::addDevice(nsecs_t when, int32_t deviceId) {
+ String8 name = mEventHub->getDeviceName(deviceId);
+ uint32_t classes = mEventHub->getDeviceClasses(deviceId);
+
+ // Write a log message about the added device as a heading for subsequent log messages.
+ LOGI("Device added: id=0x%x, name=%s", deviceId, name.string());
+
+ InputDevice* device = createDevice(deviceId, name, classes);
+ device->configure();
+
+ if (device->isIgnored()) {
+ LOGI(INDENT "Sources: none (device is ignored)");
+ } else {
+ LOGI(INDENT "Sources: 0x%08x", device->getSources());
+ }
+
+ bool added = false;
+ { // acquire device registry writer lock
+ RWLock::AutoWLock _wl(mDeviceRegistryLock);
+
+ ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
+ if (deviceIndex < 0) {
+ mDevices.add(deviceId, device);
+ added = true;
+ }
+ } // release device registry writer lock
+
+ if (! added) {
+ LOGW("Ignoring spurious device added event for deviceId %d.", deviceId);
+ delete device;
+ return;
+ }
+
+ handleConfigurationChanged(when);
+}
+
+void InputReader::removeDevice(nsecs_t when, int32_t deviceId) {
+ bool removed = false;
+ InputDevice* device = NULL;
+ { // acquire device registry writer lock
+ RWLock::AutoWLock _wl(mDeviceRegistryLock);
+
+ ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
+ if (deviceIndex >= 0) {
+ device = mDevices.valueAt(deviceIndex);
+ mDevices.removeItemsAt(deviceIndex, 1);
+ removed = true;
+ }
+ } // release device registry writer lock
+
+ if (! removed) {
+ LOGW("Ignoring spurious device removed event for deviceId %d.", deviceId);
+ return;
+ }
+
+ // Write a log message about the removed device as a heading for subsequent log messages.
+ if (device->isIgnored()) {
+ LOGI("Device removed: id=0x%x, name=%s (ignored non-input device)",
+ device->getId(), device->getName().string());
+ } else {
+ LOGI("Device removed: id=0x%x, name=%s, sources=%08x",
+ device->getId(), device->getName().string(), device->getSources());
+ }
+
+ device->reset();
+
+ delete device;
+
+ handleConfigurationChanged(when);
+}
+
+InputDevice* InputReader::createDevice(int32_t deviceId, const String8& name, uint32_t classes) {
+ InputDevice* device = new InputDevice(this, deviceId, name);
+
+ const int32_t associatedDisplayId = 0; // FIXME: hardcoded for current single-display devices
+
+ // Switch-like devices.
+ if (classes & INPUT_DEVICE_CLASS_SWITCH) {
+ device->addMapper(new SwitchInputMapper(device));
+ }
+
+ // Keyboard-like devices.
+ uint32_t keyboardSources = 0;
+ int32_t keyboardType = AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC;
+ if (classes & INPUT_DEVICE_CLASS_KEYBOARD) {
+ keyboardSources |= AINPUT_SOURCE_KEYBOARD;
+ }
+ if (classes & INPUT_DEVICE_CLASS_ALPHAKEY) {
+ keyboardType = AINPUT_KEYBOARD_TYPE_ALPHABETIC;
+ }
+ if (classes & INPUT_DEVICE_CLASS_DPAD) {
+ keyboardSources |= AINPUT_SOURCE_DPAD;
+ }
+ if (classes & INPUT_DEVICE_CLASS_GAMEPAD) {
+ keyboardSources |= AINPUT_SOURCE_GAMEPAD;
+ }
+
+ if (keyboardSources != 0) {
+ device->addMapper(new KeyboardInputMapper(device,
+ associatedDisplayId, keyboardSources, keyboardType));
+ }
+
+ // Trackball-like devices.
+ if (classes & INPUT_DEVICE_CLASS_TRACKBALL) {
+ device->addMapper(new TrackballInputMapper(device, associatedDisplayId));
+ }
+
+ // Touchscreen-like devices.
+ if (classes & INPUT_DEVICE_CLASS_TOUCHSCREEN_MT) {
+ device->addMapper(new MultiTouchInputMapper(device, associatedDisplayId));
+ } else if (classes & INPUT_DEVICE_CLASS_TOUCHSCREEN) {
+ device->addMapper(new SingleTouchInputMapper(device, associatedDisplayId));
+ }
+
+ return device;
+}
+
+void InputReader::consumeEvent(const RawEvent* rawEvent) {
+ int32_t deviceId = rawEvent->deviceId;
+
+ { // acquire device registry reader lock
+ RWLock::AutoRLock _rl(mDeviceRegistryLock);
+
+ ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
+ if (deviceIndex < 0) {
+ LOGW("Discarding event for unknown deviceId %d.", deviceId);
+ return;
+ }
+
+ InputDevice* device = mDevices.valueAt(deviceIndex);
+ if (device->isIgnored()) {
+ //LOGD("Discarding event for ignored deviceId %d.", deviceId);
+ return;
+ }
+
+ device->process(rawEvent);
+ } // release device registry reader lock
+}
+
+void InputReader::handleConfigurationChanged(nsecs_t when) {
+ // Reset global meta state because it depends on the list of all configured devices.
+ updateGlobalMetaState();
+
+ // Update input configuration.
+ updateInputConfiguration();
+
+ // Enqueue configuration changed.
+ mDispatcher->notifyConfigurationChanged(when);
+}
+
+void InputReader::configureExcludedDevices() {
+ Vector<String8> excludedDeviceNames;
+ mPolicy->getExcludedDeviceNames(excludedDeviceNames);
+
+ for (size_t i = 0; i < excludedDeviceNames.size(); i++) {
+ mEventHub->addExcludedDevice(excludedDeviceNames[i]);
+ }
+}
+
+void InputReader::updateGlobalMetaState() {
+ { // acquire state lock
+ AutoMutex _l(mStateLock);
+
+ mGlobalMetaState = 0;
+
+ { // acquire device registry reader lock
+ RWLock::AutoRLock _rl(mDeviceRegistryLock);
+
+ for (size_t i = 0; i < mDevices.size(); i++) {
+ InputDevice* device = mDevices.valueAt(i);
+ mGlobalMetaState |= device->getMetaState();
+ }
+ } // release device registry reader lock
+ } // release state lock
+}
+
+int32_t InputReader::getGlobalMetaState() {
+ { // acquire state lock
+ AutoMutex _l(mStateLock);
+
+ return mGlobalMetaState;
+ } // release state lock
+}
+
+void InputReader::updateInputConfiguration() {
+ { // acquire state lock
+ AutoMutex _l(mStateLock);
+
+ int32_t touchScreenConfig = InputConfiguration::TOUCHSCREEN_NOTOUCH;
+ int32_t keyboardConfig = InputConfiguration::KEYBOARD_NOKEYS;
+ int32_t navigationConfig = InputConfiguration::NAVIGATION_NONAV;
+ { // acquire device registry reader lock
+ RWLock::AutoRLock _rl(mDeviceRegistryLock);
+
+ InputDeviceInfo deviceInfo;
+ for (size_t i = 0; i < mDevices.size(); i++) {
+ InputDevice* device = mDevices.valueAt(i);
+ device->getDeviceInfo(& deviceInfo);
+ uint32_t sources = deviceInfo.getSources();
+
+ if ((sources & AINPUT_SOURCE_TOUCHSCREEN) == AINPUT_SOURCE_TOUCHSCREEN) {
+ touchScreenConfig = InputConfiguration::TOUCHSCREEN_FINGER;
+ }
+ if ((sources & AINPUT_SOURCE_TRACKBALL) == AINPUT_SOURCE_TRACKBALL) {
+ navigationConfig = InputConfiguration::NAVIGATION_TRACKBALL;
+ } else if ((sources & AINPUT_SOURCE_DPAD) == AINPUT_SOURCE_DPAD) {
+ navigationConfig = InputConfiguration::NAVIGATION_DPAD;
+ }
+ if (deviceInfo.getKeyboardType() == AINPUT_KEYBOARD_TYPE_ALPHABETIC) {
+ keyboardConfig = InputConfiguration::KEYBOARD_QWERTY;
+ }
+ }
+ } // release device registry reader lock
+
+ mInputConfiguration.touchScreen = touchScreenConfig;
+ mInputConfiguration.keyboard = keyboardConfig;
+ mInputConfiguration.navigation = navigationConfig;
+ } // release state lock
+}
+
+void InputReader::getInputConfiguration(InputConfiguration* outConfiguration) {
+ { // acquire state lock
+ AutoMutex _l(mStateLock);
+
+ *outConfiguration = mInputConfiguration;
+ } // release state lock
+}
+
+status_t InputReader::getInputDeviceInfo(int32_t deviceId, InputDeviceInfo* outDeviceInfo) {
+ { // acquire device registry reader lock
+ RWLock::AutoRLock _rl(mDeviceRegistryLock);
+
+ ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
+ if (deviceIndex < 0) {
+ return NAME_NOT_FOUND;
+ }
+
+ InputDevice* device = mDevices.valueAt(deviceIndex);
+ if (device->isIgnored()) {
+ return NAME_NOT_FOUND;
+ }
+
+ device->getDeviceInfo(outDeviceInfo);
+ return OK;
+ } // release device registy reader lock
+}
+
+void InputReader::getInputDeviceIds(Vector<int32_t>& outDeviceIds) {
+ outDeviceIds.clear();
+
+ { // acquire device registry reader lock
+ RWLock::AutoRLock _rl(mDeviceRegistryLock);
+
+ size_t numDevices = mDevices.size();
+ for (size_t i = 0; i < numDevices; i++) {
+ InputDevice* device = mDevices.valueAt(i);
+ if (! device->isIgnored()) {
+ outDeviceIds.add(device->getId());
+ }
+ }
+ } // release device registy reader lock
+}
+
+int32_t InputReader::getKeyCodeState(int32_t deviceId, uint32_t sourceMask,
+ int32_t keyCode) {
+ return getState(deviceId, sourceMask, keyCode, & InputDevice::getKeyCodeState);
+}
+
+int32_t InputReader::getScanCodeState(int32_t deviceId, uint32_t sourceMask,
+ int32_t scanCode) {
+ return getState(deviceId, sourceMask, scanCode, & InputDevice::getScanCodeState);
+}
+
+int32_t InputReader::getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t switchCode) {
+ return getState(deviceId, sourceMask, switchCode, & InputDevice::getSwitchState);
+}
+
+int32_t InputReader::getState(int32_t deviceId, uint32_t sourceMask, int32_t code,
+ GetStateFunc getStateFunc) {
+ { // acquire device registry reader lock
+ RWLock::AutoRLock _rl(mDeviceRegistryLock);
+
+ int32_t result = AKEY_STATE_UNKNOWN;
+ if (deviceId >= 0) {
+ ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
+ if (deviceIndex >= 0) {
+ InputDevice* device = mDevices.valueAt(deviceIndex);
+ if (! device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) {
+ result = (device->*getStateFunc)(sourceMask, code);
+ }
+ }
+ } else {
+ size_t numDevices = mDevices.size();
+ for (size_t i = 0; i < numDevices; i++) {
+ InputDevice* device = mDevices.valueAt(i);
+ if (! device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) {
+ result = (device->*getStateFunc)(sourceMask, code);
+ if (result >= AKEY_STATE_DOWN) {
+ return result;
+ }
+ }
+ }
+ }
+ return result;
+ } // release device registy reader lock
+}
+
+bool InputReader::hasKeys(int32_t deviceId, uint32_t sourceMask,
+ size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) {
+ memset(outFlags, 0, numCodes);
+ return markSupportedKeyCodes(deviceId, sourceMask, numCodes, keyCodes, outFlags);
+}
+
+bool InputReader::markSupportedKeyCodes(int32_t deviceId, uint32_t sourceMask, size_t numCodes,
+ const int32_t* keyCodes, uint8_t* outFlags) {
+ { // acquire device registry reader lock
+ RWLock::AutoRLock _rl(mDeviceRegistryLock);
+ bool result = false;
+ if (deviceId >= 0) {
+ ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
+ if (deviceIndex >= 0) {
+ InputDevice* device = mDevices.valueAt(deviceIndex);
+ if (! device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) {
+ result = device->markSupportedKeyCodes(sourceMask,
+ numCodes, keyCodes, outFlags);
+ }
+ }
+ } else {
+ size_t numDevices = mDevices.size();
+ for (size_t i = 0; i < numDevices; i++) {
+ InputDevice* device = mDevices.valueAt(i);
+ if (! device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) {
+ result |= device->markSupportedKeyCodes(sourceMask,
+ numCodes, keyCodes, outFlags);
+ }
+ }
+ }
+ return result;
+ } // release device registy reader lock
+}
+
+void InputReader::dump(String8& dump) {
+ dumpDeviceInfo(dump);
+}
+
+static void dumpMotionRange(String8& dump,
+ const char* name, const InputDeviceInfo::MotionRange* range) {
+ if (range) {
+ dump.appendFormat(" %s = { min: %0.3f, max: %0.3f, flat: %0.3f, fuzz: %0.3f }\n",
+ name, range->min, range->max, range->flat, range->fuzz);
+ }
+}
+
+#define DUMP_MOTION_RANGE(range) \
+ dumpMotionRange(dump, #range, deviceInfo.getMotionRange(AINPUT_MOTION_RANGE_##range));
+
+void InputReader::dumpDeviceInfo(String8& dump) {
+ Vector<int32_t> deviceIds;
+ getInputDeviceIds(deviceIds);
+
+ InputDeviceInfo deviceInfo;
+ for (size_t i = 0; i < deviceIds.size(); i++) {
+ int32_t deviceId = deviceIds[i];
+
+ status_t result = getInputDeviceInfo(deviceId, & deviceInfo);
+ if (result == NAME_NOT_FOUND) {
+ continue;
+ } else if (result != OK) {
+ dump.appendFormat(" ** Unexpected error %d getting information about input devices.\n",
+ result);
+ continue;
+ }
+
+ dump.appendFormat(" Device %d: '%s'\n",
+ deviceInfo.getId(), deviceInfo.getName().string());
+ dump.appendFormat(" sources = 0x%08x\n",
+ deviceInfo.getSources());
+ dump.appendFormat(" keyboardType = %d\n",
+ deviceInfo.getKeyboardType());
+
+ dump.append(" motion ranges:\n");
+ DUMP_MOTION_RANGE(X);
+ DUMP_MOTION_RANGE(Y);
+ DUMP_MOTION_RANGE(PRESSURE);
+ DUMP_MOTION_RANGE(SIZE);
+ DUMP_MOTION_RANGE(TOUCH_MAJOR);
+ DUMP_MOTION_RANGE(TOUCH_MINOR);
+ DUMP_MOTION_RANGE(TOOL_MAJOR);
+ DUMP_MOTION_RANGE(TOOL_MINOR);
+ DUMP_MOTION_RANGE(ORIENTATION);
+ }
+}
+
+#undef DUMP_MOTION_RANGE
+
+
+// --- InputReaderThread ---
+
+InputReaderThread::InputReaderThread(const sp<InputReaderInterface>& reader) :
+ Thread(/*canCallJava*/ true), mReader(reader) {
+}
+
+InputReaderThread::~InputReaderThread() {
+}
+
+bool InputReaderThread::threadLoop() {
+ mReader->loopOnce();
+ return true;
+}
+
+
+// --- InputDevice ---
+
+InputDevice::InputDevice(InputReaderContext* context, int32_t id, const String8& name) :
+ mContext(context), mId(id), mName(name), mSources(0) {
+}
+
+InputDevice::~InputDevice() {
+ size_t numMappers = mMappers.size();
+ for (size_t i = 0; i < numMappers; i++) {
+ delete mMappers[i];
+ }
+ mMappers.clear();
+}
+
+void InputDevice::addMapper(InputMapper* mapper) {
+ mMappers.add(mapper);
+}
+
+void InputDevice::configure() {
+ if (! isIgnored()) {
+ mContext->getPolicy()->getInputDeviceCalibration(mName, mCalibration);
+ }
+
+ mSources = 0;
+
+ size_t numMappers = mMappers.size();
+ for (size_t i = 0; i < numMappers; i++) {
+ InputMapper* mapper = mMappers[i];
+ mapper->configure();
+ mSources |= mapper->getSources();
+ }
+}
+
+void InputDevice::reset() {
+ size_t numMappers = mMappers.size();
+ for (size_t i = 0; i < numMappers; i++) {
+ InputMapper* mapper = mMappers[i];
+ mapper->reset();
+ }
+}
+
+void InputDevice::process(const RawEvent* rawEvent) {
+ size_t numMappers = mMappers.size();
+ for (size_t i = 0; i < numMappers; i++) {
+ InputMapper* mapper = mMappers[i];
+ mapper->process(rawEvent);
+ }
+}
+
+void InputDevice::getDeviceInfo(InputDeviceInfo* outDeviceInfo) {
+ outDeviceInfo->initialize(mId, mName);
+
+ size_t numMappers = mMappers.size();
+ for (size_t i = 0; i < numMappers; i++) {
+ InputMapper* mapper = mMappers[i];
+ mapper->populateDeviceInfo(outDeviceInfo);
+ }
+}
+
+int32_t InputDevice::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) {
+ return getState(sourceMask, keyCode, & InputMapper::getKeyCodeState);
+}
+
+int32_t InputDevice::getScanCodeState(uint32_t sourceMask, int32_t scanCode) {
+ return getState(sourceMask, scanCode, & InputMapper::getScanCodeState);
+}
+
+int32_t InputDevice::getSwitchState(uint32_t sourceMask, int32_t switchCode) {
+ return getState(sourceMask, switchCode, & InputMapper::getSwitchState);
+}
+
+int32_t InputDevice::getState(uint32_t sourceMask, int32_t code, GetStateFunc getStateFunc) {
+ int32_t result = AKEY_STATE_UNKNOWN;
+ size_t numMappers = mMappers.size();
+ for (size_t i = 0; i < numMappers; i++) {
+ InputMapper* mapper = mMappers[i];
+ if (sourcesMatchMask(mapper->getSources(), sourceMask)) {
+ result = (mapper->*getStateFunc)(sourceMask, code);
+ if (result >= AKEY_STATE_DOWN) {
+ return result;
+ }
+ }
+ }
+ return result;
+}
+
+bool InputDevice::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
+ const int32_t* keyCodes, uint8_t* outFlags) {
+ bool result = false;
+ size_t numMappers = mMappers.size();
+ for (size_t i = 0; i < numMappers; i++) {
+ InputMapper* mapper = mMappers[i];
+ if (sourcesMatchMask(mapper->getSources(), sourceMask)) {
+ result |= mapper->markSupportedKeyCodes(sourceMask, numCodes, keyCodes, outFlags);
+ }
+ }
+ return result;
+}
+
+int32_t InputDevice::getMetaState() {
+ int32_t result = 0;
+ size_t numMappers = mMappers.size();
+ for (size_t i = 0; i < numMappers; i++) {
+ InputMapper* mapper = mMappers[i];
+ result |= mapper->getMetaState();
+ }
+ return result;
+}
+
+
+// --- InputMapper ---
+
+InputMapper::InputMapper(InputDevice* device) :
+ mDevice(device), mContext(device->getContext()) {
+}
+
+InputMapper::~InputMapper() {
+}
+
+void InputMapper::populateDeviceInfo(InputDeviceInfo* info) {
+ info->addSource(getSources());
+}
+
+void InputMapper::configure() {
+}
+
+void InputMapper::reset() {
+}
+
+int32_t InputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) {
+ return AKEY_STATE_UNKNOWN;
+}
+
+int32_t InputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) {
+ return AKEY_STATE_UNKNOWN;
+}
+
+int32_t InputMapper::getSwitchState(uint32_t sourceMask, int32_t switchCode) {
+ return AKEY_STATE_UNKNOWN;
+}
+
+bool InputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
+ const int32_t* keyCodes, uint8_t* outFlags) {
+ return false;
+}
+
+int32_t InputMapper::getMetaState() {
+ return 0;
+}
+
+bool InputMapper::applyStandardPolicyActions(nsecs_t when, int32_t policyActions) {
+ return policyActions & InputReaderPolicyInterface::ACTION_DISPATCH;
+}
+
+
+// --- SwitchInputMapper ---
+
+SwitchInputMapper::SwitchInputMapper(InputDevice* device) :
+ InputMapper(device) {
+}
+
+SwitchInputMapper::~SwitchInputMapper() {
+}
+
+uint32_t SwitchInputMapper::getSources() {
+ return 0;
+}
+
+void SwitchInputMapper::process(const RawEvent* rawEvent) {
+ switch (rawEvent->type) {
+ case EV_SW:
+ processSwitch(rawEvent->when, rawEvent->scanCode, rawEvent->value);
+ break;
+ }
+}
+
+void SwitchInputMapper::processSwitch(nsecs_t when, int32_t switchCode, int32_t switchValue) {
+ uint32_t policyFlags = 0;
+ int32_t policyActions = getPolicy()->interceptSwitch(
+ when, switchCode, switchValue, policyFlags);
+
+ applyStandardPolicyActions(when, policyActions);
+}
+
+int32_t SwitchInputMapper::getSwitchState(uint32_t sourceMask, int32_t switchCode) {
+ return getEventHub()->getSwitchState(getDeviceId(), switchCode);
+}
+
+
+// --- KeyboardInputMapper ---
+
+KeyboardInputMapper::KeyboardInputMapper(InputDevice* device, int32_t associatedDisplayId,
+ uint32_t sources, int32_t keyboardType) :
+ InputMapper(device), mAssociatedDisplayId(associatedDisplayId), mSources(sources),
+ mKeyboardType(keyboardType) {
+ initializeLocked();
+}
+
+KeyboardInputMapper::~KeyboardInputMapper() {
+}
+
+void KeyboardInputMapper::initializeLocked() {
+ mLocked.metaState = AMETA_NONE;
+ mLocked.downTime = 0;
+}
+
+uint32_t KeyboardInputMapper::getSources() {
+ return mSources;
+}
+
+void KeyboardInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
+ InputMapper::populateDeviceInfo(info);
+
+ info->setKeyboardType(mKeyboardType);
+}
+
+void KeyboardInputMapper::reset() {
+ for (;;) {
+ int32_t keyCode, scanCode;
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ // Synthesize key up event on reset if keys are currently down.
+ if (mLocked.keyDowns.isEmpty()) {
+ initializeLocked();
+ break; // done
+ }
+
+ const KeyDown& keyDown = mLocked.keyDowns.top();
+ keyCode = keyDown.keyCode;
+ scanCode = keyDown.scanCode;
+ } // release lock
+
+ nsecs_t when = systemTime(SYSTEM_TIME_MONOTONIC);
+ processKey(when, false, keyCode, scanCode, 0);
+ }
+
+ InputMapper::reset();
+ getContext()->updateGlobalMetaState();
+}
+
+void KeyboardInputMapper::process(const RawEvent* rawEvent) {
+ switch (rawEvent->type) {
+ case EV_KEY: {
+ int32_t scanCode = rawEvent->scanCode;
+ if (isKeyboardOrGamepadKey(scanCode)) {
+ processKey(rawEvent->when, rawEvent->value != 0, rawEvent->keyCode, scanCode,
+ rawEvent->flags);
+ }
+ break;
+ }
+ }
+}
+
+bool KeyboardInputMapper::isKeyboardOrGamepadKey(int32_t scanCode) {
+ return scanCode < BTN_MOUSE
+ || scanCode >= KEY_OK
+ || (scanCode >= BTN_GAMEPAD && scanCode < BTN_DIGI);
+}
+
+void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t keyCode,
+ int32_t scanCode, uint32_t policyFlags) {
+ int32_t newMetaState;
+ nsecs_t downTime;
+ bool metaStateChanged = false;
+
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ if (down) {
+ // Rotate key codes according to orientation if needed.
+ // Note: getDisplayInfo is non-reentrant so we can continue holding the lock.
+ if (mAssociatedDisplayId >= 0) {
+ int32_t orientation;
+ if (! getPolicy()->getDisplayInfo(mAssociatedDisplayId, NULL, NULL, & orientation)) {
+ return;
+ }
+
+ keyCode = rotateKeyCode(keyCode, orientation);
+ }
+
+ // Add key down.
+ ssize_t keyDownIndex = findKeyDownLocked(scanCode);
+ if (keyDownIndex >= 0) {
+ // key repeat, be sure to use same keycode as before in case of rotation
+ keyCode = mLocked.keyDowns.top().keyCode;
+ } else {
+ // key down
+ mLocked.keyDowns.push();
+ KeyDown& keyDown = mLocked.keyDowns.editTop();
+ keyDown.keyCode = keyCode;
+ keyDown.scanCode = scanCode;
+ }
+
+ mLocked.downTime = when;
+ } else {
+ // Remove key down.
+ ssize_t keyDownIndex = findKeyDownLocked(scanCode);
+ if (keyDownIndex >= 0) {
+ // key up, be sure to use same keycode as before in case of rotation
+ keyCode = mLocked.keyDowns.top().keyCode;
+ mLocked.keyDowns.removeAt(size_t(keyDownIndex));
+ } else {
+ // key was not actually down
+ LOGI("Dropping key up from device %s because the key was not down. "
+ "keyCode=%d, scanCode=%d",
+ getDeviceName().string(), keyCode, scanCode);
+ return;
+ }
+ }
+
+ int32_t oldMetaState = mLocked.metaState;
+ newMetaState = updateMetaState(keyCode, down, oldMetaState);
+ if (oldMetaState != newMetaState) {
+ mLocked.metaState = newMetaState;
+ metaStateChanged = true;
+ }
+
+ downTime = mLocked.downTime;
+ } // release lock
+
+ if (metaStateChanged) {
+ getContext()->updateGlobalMetaState();
+ }
+
+ applyPolicyAndDispatch(when, policyFlags, down, keyCode, scanCode, newMetaState, downTime);
+}
+
+void KeyboardInputMapper::applyPolicyAndDispatch(nsecs_t when, uint32_t policyFlags, bool down,
+ int32_t keyCode, int32_t scanCode, int32_t metaState, nsecs_t downTime) {
+ int32_t policyActions = getPolicy()->interceptKey(when,
+ getDeviceId(), down, keyCode, scanCode, policyFlags);
+
+ if (! applyStandardPolicyActions(when, policyActions)) {
+ return; // event dropped
+ }
+
+ int32_t keyEventAction = down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP;
+ int32_t keyEventFlags = AKEY_EVENT_FLAG_FROM_SYSTEM;
+ if (policyFlags & POLICY_FLAG_WOKE_HERE) {
+ keyEventFlags = keyEventFlags | AKEY_EVENT_FLAG_WOKE_HERE;
+ }
+
+ getDispatcher()->notifyKey(when, getDeviceId(), AINPUT_SOURCE_KEYBOARD, policyFlags,
+ keyEventAction, keyEventFlags, keyCode, scanCode, metaState, downTime);
+}
+
+ssize_t KeyboardInputMapper::findKeyDownLocked(int32_t scanCode) {
+ size_t n = mLocked.keyDowns.size();
+ for (size_t i = 0; i < n; i++) {
+ if (mLocked.keyDowns[i].scanCode == scanCode) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+int32_t KeyboardInputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) {
+ return getEventHub()->getKeyCodeState(getDeviceId(), keyCode);
+}
+
+int32_t KeyboardInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) {
+ return getEventHub()->getScanCodeState(getDeviceId(), scanCode);
+}
+
+bool KeyboardInputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
+ const int32_t* keyCodes, uint8_t* outFlags) {
+ return getEventHub()->markSupportedKeyCodes(getDeviceId(), numCodes, keyCodes, outFlags);
+}
+
+int32_t KeyboardInputMapper::getMetaState() {
+ { // acquire lock
+ AutoMutex _l(mLock);
+ return mLocked.metaState;
+ } // release lock
+}
+
+
+// --- TrackballInputMapper ---
+
+TrackballInputMapper::TrackballInputMapper(InputDevice* device, int32_t associatedDisplayId) :
+ InputMapper(device), mAssociatedDisplayId(associatedDisplayId) {
+ mXPrecision = TRACKBALL_MOVEMENT_THRESHOLD;
+ mYPrecision = TRACKBALL_MOVEMENT_THRESHOLD;
+ mXScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD;
+ mYScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD;
+
+ initializeLocked();
+}
+
+TrackballInputMapper::~TrackballInputMapper() {
+}
+
+uint32_t TrackballInputMapper::getSources() {
+ return AINPUT_SOURCE_TRACKBALL;
+}
+
+void TrackballInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
+ InputMapper::populateDeviceInfo(info);
+
+ info->addMotionRange(AINPUT_MOTION_RANGE_X, -1.0f, 1.0f, 0.0f, mXScale);
+ info->addMotionRange(AINPUT_MOTION_RANGE_Y, -1.0f, 1.0f, 0.0f, mYScale);
+}
+
+void TrackballInputMapper::initializeLocked() {
+ mAccumulator.clear();
+
+ mLocked.down = false;
+ mLocked.downTime = 0;
+}
+
+void TrackballInputMapper::reset() {
+ for (;;) {
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ if (! mLocked.down) {
+ initializeLocked();
+ break; // done
+ }
+ } // release lock
+
+ // Synthesize trackball button up event on reset.
+ nsecs_t when = systemTime(SYSTEM_TIME_MONOTONIC);
+ mAccumulator.fields = Accumulator::FIELD_BTN_MOUSE;
+ mAccumulator.btnMouse = false;
+ sync(when);
+ }
+
+ InputMapper::reset();
+}
+
+void TrackballInputMapper::process(const RawEvent* rawEvent) {
+ switch (rawEvent->type) {
+ case EV_KEY:
+ switch (rawEvent->scanCode) {
+ case BTN_MOUSE:
+ mAccumulator.fields |= Accumulator::FIELD_BTN_MOUSE;
+ mAccumulator.btnMouse = rawEvent->value != 0;
+ // Sync now since BTN_MOUSE is not necessarily followed by SYN_REPORT and
+ // we need to ensure that we report the up/down promptly.
+ sync(rawEvent->when);
+ break;
+ }
+ break;
+
+ case EV_REL:
+ switch (rawEvent->scanCode) {
+ case REL_X:
+ mAccumulator.fields |= Accumulator::FIELD_REL_X;
+ mAccumulator.relX = rawEvent->value;
+ break;
+ case REL_Y:
+ mAccumulator.fields |= Accumulator::FIELD_REL_Y;
+ mAccumulator.relY = rawEvent->value;
+ break;
+ }
+ break;
+
+ case EV_SYN:
+ switch (rawEvent->scanCode) {
+ case SYN_REPORT:
+ sync(rawEvent->when);
+ break;
+ }
+ break;
+ }
+}
+
+void TrackballInputMapper::sync(nsecs_t when) {
+ uint32_t fields = mAccumulator.fields;
+ if (fields == 0) {
+ return; // no new state changes, so nothing to do
+ }
+
+ int motionEventAction;
+ PointerCoords pointerCoords;
+ nsecs_t downTime;
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ bool downChanged = fields & Accumulator::FIELD_BTN_MOUSE;
+
+ if (downChanged) {
+ if (mAccumulator.btnMouse) {
+ mLocked.down = true;
+ mLocked.downTime = when;
+ } else {
+ mLocked.down = false;
+ }
+ }
+
+ downTime = mLocked.downTime;
+ float x = fields & Accumulator::FIELD_REL_X ? mAccumulator.relX * mXScale : 0.0f;
+ float y = fields & Accumulator::FIELD_REL_Y ? mAccumulator.relY * mYScale : 0.0f;
+
+ if (downChanged) {
+ motionEventAction = mLocked.down ? AMOTION_EVENT_ACTION_DOWN : AMOTION_EVENT_ACTION_UP;
+ } else {
+ motionEventAction = AMOTION_EVENT_ACTION_MOVE;
+ }
+
+ pointerCoords.x = x;
+ pointerCoords.y = y;
+ pointerCoords.pressure = mLocked.down ? 1.0f : 0.0f;
+ pointerCoords.size = 0;
+ pointerCoords.touchMajor = 0;
+ pointerCoords.touchMinor = 0;
+ pointerCoords.toolMajor = 0;
+ pointerCoords.toolMinor = 0;
+ pointerCoords.orientation = 0;
+
+ if (mAssociatedDisplayId >= 0 && (x != 0.0f || y != 0.0f)) {
+ // Rotate motion based on display orientation if needed.
+ // Note: getDisplayInfo is non-reentrant so we can continue holding the lock.
+ int32_t orientation;
+ if (! getPolicy()->getDisplayInfo(mAssociatedDisplayId, NULL, NULL, & orientation)) {
+ return;
+ }
+
+ float temp;
+ switch (orientation) {
+ case InputReaderPolicyInterface::ROTATION_90:
+ temp = pointerCoords.x;
+ pointerCoords.x = pointerCoords.y;
+ pointerCoords.y = - temp;
+ break;
+
+ case InputReaderPolicyInterface::ROTATION_180:
+ pointerCoords.x = - pointerCoords.x;
+ pointerCoords.y = - pointerCoords.y;
+ break;
+
+ case InputReaderPolicyInterface::ROTATION_270:
+ temp = pointerCoords.x;
+ pointerCoords.x = - pointerCoords.y;
+ pointerCoords.y = temp;
+ break;
+ }
+ }
+ } // release lock
+
+ applyPolicyAndDispatch(when, motionEventAction, & pointerCoords, downTime);
+
+ mAccumulator.clear();
+}
+
+void TrackballInputMapper::applyPolicyAndDispatch(nsecs_t when, int32_t motionEventAction,
+ PointerCoords* pointerCoords, nsecs_t downTime) {
+ uint32_t policyFlags = 0;
+ int32_t policyActions = getPolicy()->interceptGeneric(when, policyFlags);
+
+ if (! applyStandardPolicyActions(when, policyActions)) {
+ return; // event dropped
+ }
+
+ int32_t metaState = mContext->getGlobalMetaState();
+ int32_t pointerId = 0;
+
+ getDispatcher()->notifyMotion(when, getDeviceId(), AINPUT_SOURCE_TRACKBALL, policyFlags,
+ motionEventAction, 0, metaState, AMOTION_EVENT_EDGE_FLAG_NONE,
+ 1, & pointerId, pointerCoords, mXPrecision, mYPrecision, downTime);
+}
+
+int32_t TrackballInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) {
+ if (scanCode >= BTN_MOUSE && scanCode < BTN_JOYSTICK) {
+ return getEventHub()->getScanCodeState(getDeviceId(), scanCode);
+ } else {
+ return AKEY_STATE_UNKNOWN;
+ }
+}
+
+
+// --- TouchInputMapper ---
+
+TouchInputMapper::TouchInputMapper(InputDevice* device, int32_t associatedDisplayId) :
+ InputMapper(device), mAssociatedDisplayId(associatedDisplayId) {
+ mLocked.surfaceOrientation = -1;
+ mLocked.surfaceWidth = -1;
+ mLocked.surfaceHeight = -1;
+
+ initializeLocked();
+}
+
+TouchInputMapper::~TouchInputMapper() {
+}
+
+uint32_t TouchInputMapper::getSources() {
+ return mAssociatedDisplayId >= 0 ? AINPUT_SOURCE_TOUCHSCREEN : AINPUT_SOURCE_TOUCHPAD;
+}
+
+void TouchInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
+ InputMapper::populateDeviceInfo(info);
+
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ // Ensure surface information is up to date so that orientation changes are
+ // noticed immediately.
+ configureSurfaceLocked();
+
+ info->addMotionRange(AINPUT_MOTION_RANGE_X, mLocked.orientedRanges.x);
+ info->addMotionRange(AINPUT_MOTION_RANGE_Y, mLocked.orientedRanges.y);
+
+ if (mLocked.orientedRanges.havePressure) {
+ info->addMotionRange(AINPUT_MOTION_RANGE_PRESSURE,
+ mLocked.orientedRanges.pressure);
+ }
+
+ if (mLocked.orientedRanges.haveSize) {
+ info->addMotionRange(AINPUT_MOTION_RANGE_SIZE,
+ mLocked.orientedRanges.size);
+ }
+
+ if (mLocked.orientedRanges.haveTouchArea) {
+ info->addMotionRange(AINPUT_MOTION_RANGE_TOUCH_MAJOR,
+ mLocked.orientedRanges.touchMajor);
+ info->addMotionRange(AINPUT_MOTION_RANGE_TOUCH_MINOR,
+ mLocked.orientedRanges.touchMinor);
+ }
+
+ if (mLocked.orientedRanges.haveToolArea) {
+ info->addMotionRange(AINPUT_MOTION_RANGE_TOOL_MAJOR,
+ mLocked.orientedRanges.toolMajor);
+ info->addMotionRange(AINPUT_MOTION_RANGE_TOOL_MINOR,
+ mLocked.orientedRanges.toolMinor);
+ }
+
+ if (mLocked.orientedRanges.haveOrientation) {
+ info->addMotionRange(AINPUT_MOTION_RANGE_ORIENTATION,
+ mLocked.orientedRanges.orientation);
+ }
+ } // release lock
+}
+
+void TouchInputMapper::initializeLocked() {
+ mCurrentTouch.clear();
+ mLastTouch.clear();
+ mDownTime = 0;
+
+ for (uint32_t i = 0; i < MAX_POINTERS; i++) {
+ mAveragingTouchFilter.historyStart[i] = 0;
+ mAveragingTouchFilter.historyEnd[i] = 0;
+ }
+
+ mJumpyTouchFilter.jumpyPointsDropped = 0;
+
+ mLocked.currentVirtualKey.down = false;
+
+ mLocked.orientedRanges.havePressure = false;
+ mLocked.orientedRanges.haveSize = false;
+ mLocked.orientedRanges.haveTouchArea = false;
+ mLocked.orientedRanges.haveToolArea = false;
+ mLocked.orientedRanges.haveOrientation = false;
+}
+
+void TouchInputMapper::configure() {
+ InputMapper::configure();
+
+ // Configure basic parameters.
+ configureParameters();
+ logParameters();
+
+ // Configure absolute axis information.
+ configureRawAxes();
+ logRawAxes();
+
+ // Prepare input device calibration.
+ parseCalibration();
+ resolveCalibration();
+ logCalibration();
+
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ // Configure surface dimensions and orientation.
+ configureSurfaceLocked();
+ } // release lock
+}
+
+void TouchInputMapper::configureParameters() {
+ mParameters.useBadTouchFilter = getPolicy()->filterTouchEvents();
+ mParameters.useAveragingTouchFilter = getPolicy()->filterTouchEvents();
+ mParameters.useJumpyTouchFilter = getPolicy()->filterJumpyTouchEvents();
+}
+
+void TouchInputMapper::logParameters() {
+ if (mParameters.useBadTouchFilter) {
+ LOGI(INDENT "Bad touch filter enabled.");
+ }
+ if (mParameters.useAveragingTouchFilter) {
+ LOGI(INDENT "Averaging touch filter enabled.");
+ }
+ if (mParameters.useJumpyTouchFilter) {
+ LOGI(INDENT "Jumpy touch filter enabled.");
+ }
+}
+
+void TouchInputMapper::configureRawAxes() {
+ mRawAxes.x.clear();
+ mRawAxes.y.clear();
+ mRawAxes.pressure.clear();
+ mRawAxes.touchMajor.clear();
+ mRawAxes.touchMinor.clear();
+ mRawAxes.toolMajor.clear();
+ mRawAxes.toolMinor.clear();
+ mRawAxes.orientation.clear();
+}
+
+static void logAxisInfo(RawAbsoluteAxisInfo axis, const char* name) {
+ if (axis.valid) {
+ LOGI(INDENT "Raw %s axis: min=%d, max=%d, flat=%d, fuzz=%d",
+ name, axis.minValue, axis.maxValue, axis.flat, axis.fuzz);
+ } else {
+ LOGI(INDENT "Raw %s axis: unknown range", name);
+ }
+}
+
+void TouchInputMapper::logRawAxes() {
+ logAxisInfo(mRawAxes.x, "x");
+ logAxisInfo(mRawAxes.y, "y");
+ logAxisInfo(mRawAxes.pressure, "pressure");
+ logAxisInfo(mRawAxes.touchMajor, "touchMajor");
+ logAxisInfo(mRawAxes.touchMinor, "touchMinor");
+ logAxisInfo(mRawAxes.toolMajor, "toolMajor");
+ logAxisInfo(mRawAxes.toolMinor, "toolMinor");
+ logAxisInfo(mRawAxes.orientation, "orientation");
+}
+
+bool TouchInputMapper::configureSurfaceLocked() {
+ // Update orientation and dimensions if needed.
+ int32_t orientation;
+ int32_t width, height;
+ if (mAssociatedDisplayId >= 0) {
+ // Note: getDisplayInfo is non-reentrant so we can continue holding the lock.
+ if (! getPolicy()->getDisplayInfo(mAssociatedDisplayId, & width, & height, & orientation)) {
+ return false;
+ }
+ } else {
+ orientation = InputReaderPolicyInterface::ROTATION_0;
+ width = mRawAxes.x.getRange();
+ height = mRawAxes.y.getRange();
+ }
+
+ bool orientationChanged = mLocked.surfaceOrientation != orientation;
+ if (orientationChanged) {
+ mLocked.surfaceOrientation = orientation;
+ }
+
+ bool sizeChanged = mLocked.surfaceWidth != width || mLocked.surfaceHeight != height;
+ if (sizeChanged) {
+ LOGI("Device reconfigured (display size changed): id=0x%x, name=%s",
+ getDeviceId(), getDeviceName().string());
+ LOGI(INDENT "Width: %dpx", width);
+ LOGI(INDENT "Height: %dpx", height);
+
+ mLocked.surfaceWidth = width;
+ mLocked.surfaceHeight = height;
+
+ // Configure X and Y factors.
+ if (mRawAxes.x.valid && mRawAxes.y.valid) {
+ mLocked.xOrigin = mRawAxes.x.minValue;
+ mLocked.yOrigin = mRawAxes.y.minValue;
+ mLocked.xScale = float(width) / mRawAxes.x.getRange();
+ mLocked.yScale = float(height) / mRawAxes.y.getRange();
+ mLocked.xPrecision = 1.0f / mLocked.xScale;
+ mLocked.yPrecision = 1.0f / mLocked.yScale;
+
+ configureVirtualKeysLocked();
+ } else {
+ LOGW(INDENT "Touch device did not report support for X or Y axis!");
+ mLocked.xOrigin = 0;
+ mLocked.yOrigin = 0;
+ mLocked.xScale = 1.0f;
+ mLocked.yScale = 1.0f;
+ mLocked.xPrecision = 1.0f;
+ mLocked.yPrecision = 1.0f;
+ }
+
+ // Scale factor for terms that are not oriented in a particular axis.
+ // If the pixels are square then xScale == yScale otherwise we fake it
+ // by choosing an average.
+ mLocked.geometricScale = avg(mLocked.xScale, mLocked.yScale);
+
+ // Size of diagonal axis.
+ float diagonalSize = pythag(width, height);
+
+ // TouchMajor and TouchMinor factors.
+ if (mCalibration.touchAreaCalibration != Calibration::TOUCH_AREA_CALIBRATION_NONE) {
+ mLocked.orientedRanges.haveTouchArea = true;
+ mLocked.orientedRanges.touchMajor.min = 0;
+ mLocked.orientedRanges.touchMajor.max = diagonalSize;
+ mLocked.orientedRanges.touchMajor.flat = 0;
+ mLocked.orientedRanges.touchMajor.fuzz = 0;
+ mLocked.orientedRanges.touchMinor = mLocked.orientedRanges.touchMajor;
+ }
+
+ // ToolMajor and ToolMinor factors.
+ if (mCalibration.toolAreaCalibration != Calibration::TOOL_AREA_CALIBRATION_NONE) {
+ mLocked.toolAreaLinearScale = 0;
+ mLocked.toolAreaLinearBias = 0;
+ if (mCalibration.toolAreaCalibration == Calibration::TOOL_AREA_CALIBRATION_LINEAR) {
+ if (mCalibration.haveToolAreaLinearScale) {
+ mLocked.toolAreaLinearScale = mCalibration.toolAreaLinearScale;
+ } else if (mRawAxes.toolMajor.valid && mRawAxes.toolMajor.maxValue != 0) {
+ mLocked.toolAreaLinearScale = float(min(width, height))
+ / mRawAxes.toolMajor.maxValue;
+ }
+
+ if (mCalibration.haveToolAreaLinearBias) {
+ mLocked.toolAreaLinearBias = mCalibration.toolAreaLinearBias;
+ }
+ }
+
+ mLocked.orientedRanges.haveToolArea = true;
+ mLocked.orientedRanges.toolMajor.min = 0;
+ mLocked.orientedRanges.toolMajor.max = diagonalSize;
+ mLocked.orientedRanges.toolMajor.flat = 0;
+ mLocked.orientedRanges.toolMajor.fuzz = 0;
+ mLocked.orientedRanges.toolMinor = mLocked.orientedRanges.toolMajor;
+ }
+
+ // Pressure factors.
+ if (mCalibration.pressureCalibration != Calibration::PRESSURE_CALIBRATION_NONE) {
+ RawAbsoluteAxisInfo rawPressureAxis;
+ switch (mCalibration.pressureSource) {
+ case Calibration::PRESSURE_SOURCE_PRESSURE:
+ rawPressureAxis = mRawAxes.pressure;
+ break;
+ case Calibration::PRESSURE_SOURCE_TOUCH:
+ rawPressureAxis = mRawAxes.touchMajor;
+ break;
+ default:
+ rawPressureAxis.clear();
+ }
+
+ mLocked.pressureScale = 0;
+ if (mCalibration.pressureCalibration == Calibration::PRESSURE_CALIBRATION_PHYSICAL
+ || mCalibration.pressureCalibration
+ == Calibration::PRESSURE_CALIBRATION_AMPLITUDE) {
+ if (mCalibration.havePressureScale) {
+ mLocked.pressureScale = mCalibration.pressureScale;
+ } else if (rawPressureAxis.valid && rawPressureAxis.maxValue != 0) {
+ mLocked.pressureScale = 1.0f / rawPressureAxis.maxValue;
+ }
+ }
+
+ mLocked.orientedRanges.havePressure = true;
+ mLocked.orientedRanges.pressure.min = 0;
+ mLocked.orientedRanges.pressure.max = 1.0;
+ mLocked.orientedRanges.pressure.flat = 0;
+ mLocked.orientedRanges.pressure.fuzz = 0;
+ }
+
+ // Size factors.
+ if (mCalibration.sizeCalibration != Calibration::SIZE_CALIBRATION_NONE) {
+ mLocked.sizeScale = 0;
+ if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_NORMALIZED) {
+ if (mRawAxes.toolMajor.valid && mRawAxes.toolMajor.maxValue != 0) {
+ mLocked.sizeScale = 1.0f / mRawAxes.toolMajor.maxValue;
+ }
+ }
+
+ mLocked.orientedRanges.haveSize = true;
+ mLocked.orientedRanges.size.min = 0;
+ mLocked.orientedRanges.size.max = 1.0;
+ mLocked.orientedRanges.size.flat = 0;
+ mLocked.orientedRanges.size.fuzz = 0;
+ }
+
+ // Orientation
+ if (mCalibration.orientationCalibration != Calibration::ORIENTATION_CALIBRATION_NONE) {
+ mLocked.orientationScale = 0;
+ if (mCalibration.orientationCalibration
+ == Calibration::ORIENTATION_CALIBRATION_INTERPOLATED) {
+ if (mRawAxes.orientation.valid && mRawAxes.orientation.maxValue != 0) {
+ mLocked.orientationScale = float(M_PI_2) / mRawAxes.orientation.maxValue;
+ }
+ }
+
+ mLocked.orientedRanges.orientation.min = - M_PI_2;
+ mLocked.orientedRanges.orientation.max = M_PI_2;
+ mLocked.orientedRanges.orientation.flat = 0;
+ mLocked.orientedRanges.orientation.fuzz = 0;
+ }
+ }
+
+ if (orientationChanged || sizeChanged) {
+ // Compute oriented surface dimensions, precision, and scales.
+ float orientedXScale, orientedYScale;
+ switch (mLocked.surfaceOrientation) {
+ case InputReaderPolicyInterface::ROTATION_90:
+ case InputReaderPolicyInterface::ROTATION_270:
+ mLocked.orientedSurfaceWidth = mLocked.surfaceHeight;
+ mLocked.orientedSurfaceHeight = mLocked.surfaceWidth;
+ mLocked.orientedXPrecision = mLocked.yPrecision;
+ mLocked.orientedYPrecision = mLocked.xPrecision;
+ orientedXScale = mLocked.yScale;
+ orientedYScale = mLocked.xScale;
+ break;
+ default:
+ mLocked.orientedSurfaceWidth = mLocked.surfaceWidth;
+ mLocked.orientedSurfaceHeight = mLocked.surfaceHeight;
+ mLocked.orientedXPrecision = mLocked.xPrecision;
+ mLocked.orientedYPrecision = mLocked.yPrecision;
+ orientedXScale = mLocked.xScale;
+ orientedYScale = mLocked.yScale;
+ break;
+ }
+
+ // Configure position ranges.
+ mLocked.orientedRanges.x.min = 0;
+ mLocked.orientedRanges.x.max = mLocked.orientedSurfaceWidth;
+ mLocked.orientedRanges.x.flat = 0;
+ mLocked.orientedRanges.x.fuzz = orientedXScale;
+
+ mLocked.orientedRanges.y.min = 0;
+ mLocked.orientedRanges.y.max = mLocked.orientedSurfaceHeight;
+ mLocked.orientedRanges.y.flat = 0;
+ mLocked.orientedRanges.y.fuzz = orientedYScale;
+ }
+
+ if (sizeChanged) {
+ logMotionRangesLocked();
+ }
+
+ return true;
+}
+
+static void logMotionRangeInfo(InputDeviceInfo::MotionRange* range, const char* name) {
+ if (range) {
+ LOGI(INDENT "Output %s range: min=%f, max=%f, flat=%f, fuzz=%f",
+ name, range->min, range->max, range->flat, range->fuzz);
+ } else {
+ LOGI(INDENT "Output %s range: unsupported", name);
+ }
+}
+
+void TouchInputMapper::logMotionRangesLocked() {
+ logMotionRangeInfo(& mLocked.orientedRanges.x, "x");
+ logMotionRangeInfo(& mLocked.orientedRanges.y, "y");
+ logMotionRangeInfo(mLocked.orientedRanges.havePressure
+ ? & mLocked.orientedRanges.pressure : NULL, "pressure");
+ logMotionRangeInfo(mLocked.orientedRanges.haveSize
+ ? & mLocked.orientedRanges.size : NULL, "size");
+ logMotionRangeInfo(mLocked.orientedRanges.haveTouchArea
+ ? & mLocked.orientedRanges.touchMajor : NULL, "touchMajor");
+ logMotionRangeInfo(mLocked.orientedRanges.haveTouchArea
+ ? & mLocked.orientedRanges.touchMinor : NULL, "touchMinor");
+ logMotionRangeInfo(mLocked.orientedRanges.haveToolArea
+ ? & mLocked.orientedRanges.toolMajor : NULL, "toolMajor");
+ logMotionRangeInfo(mLocked.orientedRanges.haveToolArea
+ ? & mLocked.orientedRanges.toolMinor : NULL, "toolMinor");
+ logMotionRangeInfo(mLocked.orientedRanges.haveOrientation
+ ? & mLocked.orientedRanges.orientation : NULL, "orientation");
+}
+
+void TouchInputMapper::configureVirtualKeysLocked() {
+ assert(mRawAxes.x.valid && mRawAxes.y.valid);
+
+ // Note: getVirtualKeyDefinitions is non-reentrant so we can continue holding the lock.
+ Vector<VirtualKeyDefinition> virtualKeyDefinitions;
+ getPolicy()->getVirtualKeyDefinitions(getDeviceName(), virtualKeyDefinitions);
+
+ mLocked.virtualKeys.clear();
+
+ if (virtualKeyDefinitions.size() == 0) {
+ return;
+ }
+
+ mLocked.virtualKeys.setCapacity(virtualKeyDefinitions.size());
+
+ int32_t touchScreenLeft = mRawAxes.x.minValue;
+ int32_t touchScreenTop = mRawAxes.y.minValue;
+ int32_t touchScreenWidth = mRawAxes.x.getRange();
+ int32_t touchScreenHeight = mRawAxes.y.getRange();
+
+ for (size_t i = 0; i < virtualKeyDefinitions.size(); i++) {
+ const VirtualKeyDefinition& virtualKeyDefinition =
+ virtualKeyDefinitions[i];
+
+ mLocked.virtualKeys.add();
+ VirtualKey& virtualKey = mLocked.virtualKeys.editTop();
+
+ virtualKey.scanCode = virtualKeyDefinition.scanCode;
+ int32_t keyCode;
+ uint32_t flags;
+ if (getEventHub()->scancodeToKeycode(getDeviceId(), virtualKey.scanCode,
+ & keyCode, & flags)) {
+ LOGW(INDENT "VirtualKey %d: could not obtain key code, ignoring",
+ virtualKey.scanCode);
+ mLocked.virtualKeys.pop(); // drop the key
+ continue;
+ }
+
+ virtualKey.keyCode = keyCode;
+ virtualKey.flags = flags;
+
+ // convert the key definition's display coordinates into touch coordinates for a hit box
+ int32_t halfWidth = virtualKeyDefinition.width / 2;
+ int32_t halfHeight = virtualKeyDefinition.height / 2;
+
+ virtualKey.hitLeft = (virtualKeyDefinition.centerX - halfWidth)
+ * touchScreenWidth / mLocked.surfaceWidth + touchScreenLeft;
+ virtualKey.hitRight= (virtualKeyDefinition.centerX + halfWidth)
+ * touchScreenWidth / mLocked.surfaceWidth + touchScreenLeft;
+ virtualKey.hitTop = (virtualKeyDefinition.centerY - halfHeight)
+ * touchScreenHeight / mLocked.surfaceHeight + touchScreenTop;
+ virtualKey.hitBottom = (virtualKeyDefinition.centerY + halfHeight)
+ * touchScreenHeight / mLocked.surfaceHeight + touchScreenTop;
+
+ LOGI(INDENT "VirtualKey %d: keyCode=%d hitLeft=%d hitRight=%d hitTop=%d hitBottom=%d",
+ virtualKey.scanCode, virtualKey.keyCode,
+ virtualKey.hitLeft, virtualKey.hitRight, virtualKey.hitTop, virtualKey.hitBottom);
+ }
+}
+
+void TouchInputMapper::parseCalibration() {
+ const InputDeviceCalibration& in = getDevice()->getCalibration();
+ Calibration& out = mCalibration;
+
+ // Touch Area
+ out.touchAreaCalibration = Calibration::TOUCH_AREA_CALIBRATION_DEFAULT;
+ String8 touchAreaCalibrationString;
+ if (in.tryGetProperty(String8("touch.touchArea.calibration"), touchAreaCalibrationString)) {
+ if (touchAreaCalibrationString == "none") {
+ out.touchAreaCalibration = Calibration::TOUCH_AREA_CALIBRATION_NONE;
+ } else if (touchAreaCalibrationString == "geometric") {
+ out.touchAreaCalibration = Calibration::TOUCH_AREA_CALIBRATION_GEOMETRIC;
+ } else if (touchAreaCalibrationString == "pressure") {
+ out.touchAreaCalibration = Calibration::TOUCH_AREA_CALIBRATION_PRESSURE;
+ } else if (touchAreaCalibrationString != "default") {
+ LOGW("Invalid value for touch.touchArea.calibration: '%s'",
+ touchAreaCalibrationString.string());
+ }
+ }
+
+ // Tool Area
+ out.toolAreaCalibration = Calibration::TOOL_AREA_CALIBRATION_DEFAULT;
+ String8 toolAreaCalibrationString;
+ if (in.tryGetProperty(String8("tool.toolArea.calibration"), toolAreaCalibrationString)) {
+ if (toolAreaCalibrationString == "none") {
+ out.toolAreaCalibration = Calibration::TOOL_AREA_CALIBRATION_NONE;
+ } else if (toolAreaCalibrationString == "geometric") {
+ out.toolAreaCalibration = Calibration::TOOL_AREA_CALIBRATION_GEOMETRIC;
+ } else if (toolAreaCalibrationString == "linear") {
+ out.toolAreaCalibration = Calibration::TOOL_AREA_CALIBRATION_LINEAR;
+ } else if (toolAreaCalibrationString != "default") {
+ LOGW("Invalid value for tool.toolArea.calibration: '%s'",
+ toolAreaCalibrationString.string());
+ }
+ }
+
+ out.haveToolAreaLinearScale = in.tryGetProperty(String8("touch.toolArea.linearScale"),
+ out.toolAreaLinearScale);
+ out.haveToolAreaLinearBias = in.tryGetProperty(String8("touch.toolArea.linearBias"),
+ out.toolAreaLinearBias);
+ out.haveToolAreaIsSummed = in.tryGetProperty(String8("touch.toolArea.isSummed"),
+ out.toolAreaIsSummed);
+
+ // Pressure
+ out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_DEFAULT;
+ String8 pressureCalibrationString;
+ if (in.tryGetProperty(String8("tool.pressure.calibration"), pressureCalibrationString)) {
+ if (pressureCalibrationString == "none") {
+ out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_NONE;
+ } else if (pressureCalibrationString == "physical") {
+ out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_PHYSICAL;
+ } else if (pressureCalibrationString == "amplitude") {
+ out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_AMPLITUDE;
+ } else if (pressureCalibrationString != "default") {
+ LOGW("Invalid value for tool.pressure.calibration: '%s'",
+ pressureCalibrationString.string());
+ }
+ }
+
+ out.pressureSource = Calibration::PRESSURE_SOURCE_DEFAULT;
+ String8 pressureSourceString;
+ if (in.tryGetProperty(String8("touch.pressure.source"), pressureSourceString)) {
+ if (pressureSourceString == "pressure") {
+ out.pressureSource = Calibration::PRESSURE_SOURCE_PRESSURE;
+ } else if (pressureSourceString == "touch") {
+ out.pressureSource = Calibration::PRESSURE_SOURCE_TOUCH;
+ } else if (pressureSourceString != "default") {
+ LOGW("Invalid value for touch.pressure.source: '%s'",
+ pressureSourceString.string());
+ }
+ }
+
+ out.havePressureScale = in.tryGetProperty(String8("touch.pressure.scale"),
+ out.pressureScale);
+
+ // Size
+ out.sizeCalibration = Calibration::SIZE_CALIBRATION_DEFAULT;
+ String8 sizeCalibrationString;
+ if (in.tryGetProperty(String8("tool.size.calibration"), sizeCalibrationString)) {
+ if (sizeCalibrationString == "none") {
+ out.sizeCalibration = Calibration::SIZE_CALIBRATION_NONE;
+ } else if (sizeCalibrationString == "normalized") {
+ out.sizeCalibration = Calibration::SIZE_CALIBRATION_NORMALIZED;
+ } else if (sizeCalibrationString != "default") {
+ LOGW("Invalid value for tool.size.calibration: '%s'",
+ sizeCalibrationString.string());
+ }
+ }
+
+ // Orientation
+ out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_DEFAULT;
+ String8 orientationCalibrationString;
+ if (in.tryGetProperty(String8("tool.orientation.calibration"), orientationCalibrationString)) {
+ if (orientationCalibrationString == "none") {
+ out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_NONE;
+ } else if (orientationCalibrationString == "interpolated") {
+ out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_INTERPOLATED;
+ } else if (orientationCalibrationString != "default") {
+ LOGW("Invalid value for tool.orientation.calibration: '%s'",
+ orientationCalibrationString.string());
+ }
+ }
+}
+
+void TouchInputMapper::resolveCalibration() {
+ // Pressure
+ switch (mCalibration.pressureSource) {
+ case Calibration::PRESSURE_SOURCE_DEFAULT:
+ if (mRawAxes.pressure.valid) {
+ mCalibration.pressureSource = Calibration::PRESSURE_SOURCE_PRESSURE;
+ } else if (mRawAxes.touchMajor.valid) {
+ mCalibration.pressureSource = Calibration::PRESSURE_SOURCE_TOUCH;
+ }
+ break;
+
+ case Calibration::PRESSURE_SOURCE_PRESSURE:
+ if (! mRawAxes.pressure.valid) {
+ LOGW("Calibration property touch.pressure.source is 'pressure' but "
+ "the pressure axis is not available.");
+ }
+ break;
+
+ case Calibration::PRESSURE_SOURCE_TOUCH:
+ if (! mRawAxes.touchMajor.valid) {
+ LOGW("Calibration property touch.pressure.source is 'touch' but "
+ "the touchMajor axis is not available.");
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ switch (mCalibration.pressureCalibration) {
+ case Calibration::PRESSURE_CALIBRATION_DEFAULT:
+ if (mCalibration.pressureSource != Calibration::PRESSURE_SOURCE_DEFAULT) {
+ mCalibration.pressureCalibration = Calibration::PRESSURE_CALIBRATION_AMPLITUDE;
+ } else {
+ mCalibration.pressureCalibration = Calibration::PRESSURE_CALIBRATION_NONE;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ // Tool Area
+ switch (mCalibration.toolAreaCalibration) {
+ case Calibration::TOOL_AREA_CALIBRATION_DEFAULT:
+ if (mRawAxes.toolMajor.valid) {
+ mCalibration.toolAreaCalibration = Calibration::TOOL_AREA_CALIBRATION_LINEAR;
+ } else {
+ mCalibration.toolAreaCalibration = Calibration::TOOL_AREA_CALIBRATION_NONE;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ // Touch Area
+ switch (mCalibration.touchAreaCalibration) {
+ case Calibration::TOUCH_AREA_CALIBRATION_DEFAULT:
+ if (mCalibration.pressureCalibration != Calibration::PRESSURE_CALIBRATION_NONE
+ && mCalibration.toolAreaCalibration != Calibration::TOOL_AREA_CALIBRATION_NONE) {
+ mCalibration.touchAreaCalibration = Calibration::TOUCH_AREA_CALIBRATION_PRESSURE;
+ } else {
+ mCalibration.touchAreaCalibration = Calibration::TOUCH_AREA_CALIBRATION_NONE;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ // Size
+ switch (mCalibration.sizeCalibration) {
+ case Calibration::SIZE_CALIBRATION_DEFAULT:
+ if (mRawAxes.toolMajor.valid) {
+ mCalibration.sizeCalibration = Calibration::SIZE_CALIBRATION_NORMALIZED;
+ } else {
+ mCalibration.sizeCalibration = Calibration::SIZE_CALIBRATION_NONE;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ // Orientation
+ switch (mCalibration.orientationCalibration) {
+ case Calibration::ORIENTATION_CALIBRATION_DEFAULT:
+ if (mRawAxes.orientation.valid) {
+ mCalibration.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_INTERPOLATED;
+ } else {
+ mCalibration.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_NONE;
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+void TouchInputMapper::logCalibration() {
+ LOGI(INDENT "Calibration:");
+
+ // Touch Area
+ switch (mCalibration.touchAreaCalibration) {
+ case Calibration::TOUCH_AREA_CALIBRATION_NONE:
+ LOGI(INDENT INDENT "touch.touchArea.calibration: none");
+ break;
+ case Calibration::TOUCH_AREA_CALIBRATION_GEOMETRIC:
+ LOGI(INDENT INDENT "touch.touchArea.calibration: geometric");
+ break;
+ case Calibration::TOUCH_AREA_CALIBRATION_PRESSURE:
+ LOGI(INDENT INDENT "touch.touchArea.calibration: pressure");
+ break;
+ default:
+ assert(false);
+ }
+
+ // Tool Area
+ switch (mCalibration.toolAreaCalibration) {
+ case Calibration::TOOL_AREA_CALIBRATION_NONE:
+ LOGI(INDENT INDENT "touch.toolArea.calibration: none");
+ break;
+ case Calibration::TOOL_AREA_CALIBRATION_GEOMETRIC:
+ LOGI(INDENT INDENT "touch.toolArea.calibration: geometric");
+ break;
+ case Calibration::TOOL_AREA_CALIBRATION_LINEAR:
+ LOGI(INDENT INDENT "touch.toolArea.calibration: linear");
+ break;
+ default:
+ assert(false);
+ }
+
+ if (mCalibration.haveToolAreaLinearScale) {
+ LOGI(INDENT INDENT "touch.toolArea.linearScale: %f", mCalibration.toolAreaLinearScale);
+ }
+
+ if (mCalibration.haveToolAreaLinearBias) {
+ LOGI(INDENT INDENT "touch.toolArea.linearBias: %f", mCalibration.toolAreaLinearBias);
+ }
+
+ if (mCalibration.haveToolAreaIsSummed) {
+ LOGI(INDENT INDENT "touch.toolArea.isSummed: %d", mCalibration.toolAreaIsSummed);
+ }
+
+ // Pressure
+ switch (mCalibration.pressureCalibration) {
+ case Calibration::PRESSURE_CALIBRATION_NONE:
+ LOGI(INDENT INDENT "touch.pressure.calibration: none");
+ break;
+ case Calibration::PRESSURE_CALIBRATION_PHYSICAL:
+ LOGI(INDENT INDENT "touch.pressure.calibration: physical");
+ break;
+ case Calibration::PRESSURE_CALIBRATION_AMPLITUDE:
+ LOGI(INDENT INDENT "touch.pressure.calibration: amplitude");
+ break;
+ default:
+ assert(false);
+ }
+
+ switch (mCalibration.pressureSource) {
+ case Calibration::PRESSURE_SOURCE_PRESSURE:
+ LOGI(INDENT INDENT "touch.pressure.source: pressure");
+ break;
+ case Calibration::PRESSURE_SOURCE_TOUCH:
+ LOGI(INDENT INDENT "touch.pressure.source: touch");
+ break;
+ case Calibration::PRESSURE_SOURCE_DEFAULT:
+ break;
+ default:
+ assert(false);
+ }
+
+ if (mCalibration.havePressureScale) {
+ LOGI(INDENT INDENT "touch.pressure.scale: %f", mCalibration.pressureScale);
+ }
+
+ // Size
+ switch (mCalibration.sizeCalibration) {
+ case Calibration::SIZE_CALIBRATION_NONE:
+ LOGI(INDENT INDENT "touch.size.calibration: none");
+ break;
+ case Calibration::SIZE_CALIBRATION_NORMALIZED:
+ LOGI(INDENT INDENT "touch.size.calibration: normalized");
+ break;
+ default:
+ assert(false);
+ }
+
+ // Orientation
+ switch (mCalibration.orientationCalibration) {
+ case Calibration::ORIENTATION_CALIBRATION_NONE:
+ LOGI(INDENT INDENT "touch.orientation.calibration: none");
+ break;
+ case Calibration::ORIENTATION_CALIBRATION_INTERPOLATED:
+ LOGI(INDENT INDENT "touch.orientation.calibration: interpolated");
+ break;
+ default:
+ assert(false);
+ }
+}
+
+void TouchInputMapper::reset() {
+ // Synthesize touch up event if touch is currently down.
+ // This will also take care of finishing virtual key processing if needed.
+ if (mLastTouch.pointerCount != 0) {
+ nsecs_t when = systemTime(SYSTEM_TIME_MONOTONIC);
+ mCurrentTouch.clear();
+ syncTouch(when, true);
+ }
+
+ { // acquire lock
+ AutoMutex _l(mLock);
+ initializeLocked();
+ } // release lock
+
+ InputMapper::reset();
+}
+
+void TouchInputMapper::syncTouch(nsecs_t when, bool havePointerIds) {
+ // Apply generic policy actions.
+
+ uint32_t policyFlags = 0;
+ int32_t policyActions = getPolicy()->interceptGeneric(when, policyFlags);
+
+ if (! applyStandardPolicyActions(when, policyActions)) {
+ mLastTouch.clear();
+ return; // event dropped
+ }
+
+ // Preprocess pointer data.
+
+ if (mParameters.useBadTouchFilter) {
+ if (applyBadTouchFilter()) {
+ havePointerIds = false;
+ }
+ }
+
+ if (mParameters.useJumpyTouchFilter) {
+ if (applyJumpyTouchFilter()) {
+ havePointerIds = false;
+ }
+ }
+
+ if (! havePointerIds) {
+ calculatePointerIds();
+ }
+
+ TouchData temp;
+ TouchData* savedTouch;
+ if (mParameters.useAveragingTouchFilter) {
+ temp.copyFrom(mCurrentTouch);
+ savedTouch = & temp;
+
+ applyAveragingTouchFilter();
+ } else {
+ savedTouch = & mCurrentTouch;
+ }
+
+ // Process touches and virtual keys.
+
+ TouchResult touchResult = consumeOffScreenTouches(when, policyFlags);
+ if (touchResult == DISPATCH_TOUCH) {
+ dispatchTouches(when, policyFlags);
+ }
+
+ // Copy current touch to last touch in preparation for the next cycle.
+
+ if (touchResult == DROP_STROKE) {
+ mLastTouch.clear();
+ } else {
+ mLastTouch.copyFrom(*savedTouch);
+ }
+}
+
+TouchInputMapper::TouchResult TouchInputMapper::consumeOffScreenTouches(
+ nsecs_t when, uint32_t policyFlags) {
+ int32_t keyEventAction, keyEventFlags;
+ int32_t keyCode, scanCode, downTime;
+ TouchResult touchResult;
+
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ // Update surface size and orientation, including virtual key positions.
+ if (! configureSurfaceLocked()) {
+ return DROP_STROKE;
+ }
+
+ // Check for virtual key press.
+ if (mLocked.currentVirtualKey.down) {
+ if (mCurrentTouch.pointerCount == 0) {
+ // Pointer went up while virtual key was down.
+ mLocked.currentVirtualKey.down = false;
+#if DEBUG_VIRTUAL_KEYS
+ LOGD("VirtualKeys: Generating key up: keyCode=%d, scanCode=%d",
+ mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode);
+#endif
+ keyEventAction = AKEY_EVENT_ACTION_UP;
+ keyEventFlags = AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY;
+ touchResult = SKIP_TOUCH;
+ goto DispatchVirtualKey;
+ }
+
+ if (mCurrentTouch.pointerCount == 1) {
+ int32_t x = mCurrentTouch.pointers[0].x;
+ int32_t y = mCurrentTouch.pointers[0].y;
+ const VirtualKey* virtualKey = findVirtualKeyHitLocked(x, y);
+ if (virtualKey && virtualKey->keyCode == mLocked.currentVirtualKey.keyCode) {
+ // Pointer is still within the space of the virtual key.
+ return SKIP_TOUCH;
+ }
+ }
+
+ // Pointer left virtual key area or another pointer also went down.
+ // Send key cancellation and drop the stroke so subsequent motions will be
+ // considered fresh downs. This is useful when the user swipes away from the
+ // virtual key area into the main display surface.
+ mLocked.currentVirtualKey.down = false;
+#if DEBUG_VIRTUAL_KEYS
+ LOGD("VirtualKeys: Canceling key: keyCode=%d, scanCode=%d",
+ mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode);
+#endif
+ keyEventAction = AKEY_EVENT_ACTION_UP;
+ keyEventFlags = AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY
+ | AKEY_EVENT_FLAG_CANCELED;
+ touchResult = DROP_STROKE;
+ goto DispatchVirtualKey;
+ } else {
+ if (mCurrentTouch.pointerCount >= 1 && mLastTouch.pointerCount == 0) {
+ // Pointer just went down. Handle off-screen touches, if needed.
+ int32_t x = mCurrentTouch.pointers[0].x;
+ int32_t y = mCurrentTouch.pointers[0].y;
+ if (! isPointInsideSurfaceLocked(x, y)) {
+ // If exactly one pointer went down, check for virtual key hit.
+ // Otherwise we will drop the entire stroke.
+ if (mCurrentTouch.pointerCount == 1) {
+ const VirtualKey* virtualKey = findVirtualKeyHitLocked(x, y);
+ if (virtualKey) {
+ mLocked.currentVirtualKey.down = true;
+ mLocked.currentVirtualKey.downTime = when;
+ mLocked.currentVirtualKey.keyCode = virtualKey->keyCode;
+ mLocked.currentVirtualKey.scanCode = virtualKey->scanCode;
+#if DEBUG_VIRTUAL_KEYS
+ LOGD("VirtualKeys: Generating key down: keyCode=%d, scanCode=%d",
+ mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode);
+#endif
+ keyEventAction = AKEY_EVENT_ACTION_DOWN;
+ keyEventFlags = AKEY_EVENT_FLAG_FROM_SYSTEM
+ | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY;
+ touchResult = SKIP_TOUCH;
+ goto DispatchVirtualKey;
+ }
+ }
+ return DROP_STROKE;
+ }
+ }
+ return DISPATCH_TOUCH;
+ }
+
+ DispatchVirtualKey:
+ // Collect remaining state needed to dispatch virtual key.
+ keyCode = mLocked.currentVirtualKey.keyCode;
+ scanCode = mLocked.currentVirtualKey.scanCode;
+ downTime = mLocked.currentVirtualKey.downTime;
+ } // release lock
+
+ // Dispatch virtual key.
+ applyPolicyAndDispatchVirtualKey(when, policyFlags, keyEventAction, keyEventFlags,
+ keyCode, scanCode, downTime);
+ return touchResult;
+}
+
+void TouchInputMapper::applyPolicyAndDispatchVirtualKey(nsecs_t when, uint32_t policyFlags,
+ int32_t keyEventAction, int32_t keyEventFlags,
+ int32_t keyCode, int32_t scanCode, nsecs_t downTime) {
+ int32_t metaState = mContext->getGlobalMetaState();
+
+ if (keyEventAction == AKEY_EVENT_ACTION_DOWN) {
+ getPolicy()->virtualKeyDownFeedback();
+ }
+
+ int32_t policyActions = getPolicy()->interceptKey(when, getDeviceId(),
+ keyEventAction == AKEY_EVENT_ACTION_DOWN, keyCode, scanCode, policyFlags);
+
+ if (applyStandardPolicyActions(when, policyActions)) {
+ getDispatcher()->notifyKey(when, getDeviceId(), AINPUT_SOURCE_KEYBOARD, policyFlags,
+ keyEventAction, keyEventFlags, keyCode, scanCode, metaState, downTime);
+ }
+}
+
+void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) {
+ uint32_t currentPointerCount = mCurrentTouch.pointerCount;
+ uint32_t lastPointerCount = mLastTouch.pointerCount;
+ if (currentPointerCount == 0 && lastPointerCount == 0) {
+ return; // nothing to do!
+ }
+
+ BitSet32 currentIdBits = mCurrentTouch.idBits;
+ BitSet32 lastIdBits = mLastTouch.idBits;
+
+ if (currentIdBits == lastIdBits) {
+ // No pointer id changes so this is a move event.
+ // The dispatcher takes care of batching moves so we don't have to deal with that here.
+ int32_t motionEventAction = AMOTION_EVENT_ACTION_MOVE;
+ dispatchTouch(when, policyFlags, & mCurrentTouch,
+ currentIdBits, -1, currentPointerCount, motionEventAction);
+ } else {
+ // There may be pointers going up and pointers going down at the same time when pointer
+ // ids are reported by the device driver.
+ BitSet32 upIdBits(lastIdBits.value & ~ currentIdBits.value);
+ BitSet32 downIdBits(currentIdBits.value & ~ lastIdBits.value);
+ BitSet32 activeIdBits(lastIdBits.value);
+ uint32_t pointerCount = lastPointerCount;
+
+ while (! upIdBits.isEmpty()) {
+ uint32_t upId = upIdBits.firstMarkedBit();
+ upIdBits.clearBit(upId);
+ BitSet32 oldActiveIdBits = activeIdBits;
+ activeIdBits.clearBit(upId);
+
+ int32_t motionEventAction;
+ if (activeIdBits.isEmpty()) {
+ motionEventAction = AMOTION_EVENT_ACTION_UP;
+ } else {
+ motionEventAction = AMOTION_EVENT_ACTION_POINTER_UP;
+ }
+
+ dispatchTouch(when, policyFlags, & mLastTouch,
+ oldActiveIdBits, upId, pointerCount, motionEventAction);
+ pointerCount -= 1;
+ }
+
+ while (! downIdBits.isEmpty()) {
+ uint32_t downId = downIdBits.firstMarkedBit();
+ downIdBits.clearBit(downId);
+ BitSet32 oldActiveIdBits = activeIdBits;
+ activeIdBits.markBit(downId);
+
+ int32_t motionEventAction;
+ if (oldActiveIdBits.isEmpty()) {
+ motionEventAction = AMOTION_EVENT_ACTION_DOWN;
+ mDownTime = when;
+ } else {
+ motionEventAction = AMOTION_EVENT_ACTION_POINTER_DOWN;
+ }
+
+ pointerCount += 1;
+ dispatchTouch(when, policyFlags, & mCurrentTouch,
+ activeIdBits, downId, pointerCount, motionEventAction);
+ }
+ }
+}
+
+void TouchInputMapper::dispatchTouch(nsecs_t when, uint32_t policyFlags,
+ TouchData* touch, BitSet32 idBits, uint32_t changedId, uint32_t pointerCount,
+ int32_t motionEventAction) {
+ int32_t pointerIds[MAX_POINTERS];
+ PointerCoords pointerCoords[MAX_POINTERS];
+ int32_t motionEventEdgeFlags = 0;
+ float xPrecision, yPrecision;
+
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ // Walk through the the active pointers and map touch screen coordinates (TouchData) into
+ // display coordinates (PointerCoords) and adjust for display orientation.
+ for (uint32_t outIndex = 0; ! idBits.isEmpty(); outIndex++) {
+ uint32_t id = idBits.firstMarkedBit();
+ idBits.clearBit(id);
+ uint32_t inIndex = touch->idToIndex[id];
+
+ const PointerData& in = touch->pointers[inIndex];
+
+ // X and Y
+ float x = float(in.x - mLocked.xOrigin) * mLocked.xScale;
+ float y = float(in.y - mLocked.yOrigin) * mLocked.yScale;
+
+ // ToolMajor and ToolMinor
+ float toolMajor, toolMinor;
+ switch (mCalibration.toolAreaCalibration) {
+ case Calibration::TOOL_AREA_CALIBRATION_GEOMETRIC:
+ toolMajor = in.toolMajor * mLocked.geometricScale;
+ if (mRawAxes.toolMinor.valid) {
+ toolMinor = in.toolMinor * mLocked.geometricScale;
+ } else {
+ toolMinor = toolMajor;
+ }
+ break;
+ case Calibration::TOOL_AREA_CALIBRATION_LINEAR:
+ toolMajor = in.toolMajor != 0
+ ? in.toolMajor * mLocked.toolAreaLinearScale + mLocked.toolAreaLinearBias
+ : 0;
+ if (mRawAxes.toolMinor.valid) {
+ toolMinor = in.toolMinor != 0
+ ? in.toolMinor * mLocked.toolAreaLinearScale
+ + mLocked.toolAreaLinearBias
+ : 0;
+ } else {
+ toolMinor = toolMajor;
+ }
+ break;
+ default:
+ toolMajor = 0;
+ toolMinor = 0;
+ break;
+ }
+
+ if (mCalibration.haveToolAreaIsSummed && mCalibration.toolAreaIsSummed) {
+ toolMajor /= pointerCount;
+ toolMinor /= pointerCount;
+ }
+
+ // Pressure
+ float rawPressure;
+ switch (mCalibration.pressureSource) {
+ case Calibration::PRESSURE_SOURCE_PRESSURE:
+ rawPressure = in.pressure;
+ break;
+ case Calibration::PRESSURE_SOURCE_TOUCH:
+ rawPressure = in.touchMajor;
+ break;
+ default:
+ rawPressure = 0;
+ }
+
+ float pressure;
+ switch (mCalibration.pressureCalibration) {
+ case Calibration::PRESSURE_CALIBRATION_PHYSICAL:
+ case Calibration::PRESSURE_CALIBRATION_AMPLITUDE:
+ pressure = rawPressure * mLocked.pressureScale;
+ break;
+ default:
+ pressure = 1;
+ break;
+ }
+
+ // TouchMajor and TouchMinor
+ float touchMajor, touchMinor;
+ switch (mCalibration.touchAreaCalibration) {
+ case Calibration::TOUCH_AREA_CALIBRATION_GEOMETRIC:
+ touchMajor = in.touchMajor * mLocked.geometricScale;
+ if (mRawAxes.touchMinor.valid) {
+ touchMinor = in.touchMinor * mLocked.geometricScale;
+ } else {
+ touchMinor = touchMajor;
+ }
+ break;
+ case Calibration::TOUCH_AREA_CALIBRATION_PRESSURE:
+ touchMajor = toolMajor * pressure;
+ touchMinor = toolMinor * pressure;
+ break;
+ default:
+ touchMajor = 0;
+ touchMinor = 0;
+ break;
+ }
+
+ if (touchMajor > toolMajor) {
+ touchMajor = toolMajor;
+ }
+ if (touchMinor > toolMinor) {
+ touchMinor = toolMinor;
+ }
+
+ // Size
+ float size;
+ switch (mCalibration.sizeCalibration) {
+ case Calibration::SIZE_CALIBRATION_NORMALIZED: {
+ float rawSize = mRawAxes.toolMinor.valid
+ ? avg(in.toolMajor, in.toolMinor)
+ : in.toolMajor;
+ size = rawSize * mLocked.sizeScale;
+ break;
+ }
+ default:
+ size = 0;
+ break;
+ }
+
+ // Orientation
+ float orientation;
+ switch (mCalibration.orientationCalibration) {
+ case Calibration::ORIENTATION_CALIBRATION_INTERPOLATED:
+ orientation = in.orientation * mLocked.orientationScale;
+ break;
+ default:
+ orientation = 0;
+ }
+
+ // Adjust coords for orientation.
+ switch (mLocked.surfaceOrientation) {
+ case InputReaderPolicyInterface::ROTATION_90: {
+ float xTemp = x;
+ x = y;
+ y = mLocked.surfaceWidth - xTemp;
+ orientation -= M_PI_2;
+ if (orientation < - M_PI_2) {
+ orientation += M_PI;
+ }
+ break;
+ }
+ case InputReaderPolicyInterface::ROTATION_180: {
+ x = mLocked.surfaceWidth - x;
+ y = mLocked.surfaceHeight - y;
+ orientation = - orientation;
+ break;
+ }
+ case InputReaderPolicyInterface::ROTATION_270: {
+ float xTemp = x;
+ x = mLocked.surfaceHeight - y;
+ y = xTemp;
+ orientation += M_PI_2;
+ if (orientation > M_PI_2) {
+ orientation -= M_PI;
+ }
+ break;
+ }
+ }
+
+ // Write output coords.
+ PointerCoords& out = pointerCoords[outIndex];
+ out.x = x;
+ out.y = y;
+ out.pressure = pressure;
+ out.size = size;
+ out.touchMajor = touchMajor;
+ out.touchMinor = touchMinor;
+ out.toolMajor = toolMajor;
+ out.toolMinor = toolMinor;
+ out.orientation = orientation;
+
+ pointerIds[outIndex] = int32_t(id);
+
+ if (id == changedId) {
+ motionEventAction |= outIndex << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
+ }
+ }
+
+ // Check edge flags by looking only at the first pointer since the flags are
+ // global to the event.
+ if (motionEventAction == AMOTION_EVENT_ACTION_DOWN) {
+ if (pointerCoords[0].x <= 0) {
+ motionEventEdgeFlags |= AMOTION_EVENT_EDGE_FLAG_LEFT;
+ } else if (pointerCoords[0].x >= mLocked.orientedSurfaceWidth) {
+ motionEventEdgeFlags |= AMOTION_EVENT_EDGE_FLAG_RIGHT;
+ }
+ if (pointerCoords[0].y <= 0) {
+ motionEventEdgeFlags |= AMOTION_EVENT_EDGE_FLAG_TOP;
+ } else if (pointerCoords[0].y >= mLocked.orientedSurfaceHeight) {
+ motionEventEdgeFlags |= AMOTION_EVENT_EDGE_FLAG_BOTTOM;
+ }
+ }
+
+ xPrecision = mLocked.orientedXPrecision;
+ yPrecision = mLocked.orientedYPrecision;
+ } // release lock
+
+ getDispatcher()->notifyMotion(when, getDeviceId(), AINPUT_SOURCE_TOUCHSCREEN, policyFlags,
+ motionEventAction, 0, getContext()->getGlobalMetaState(), motionEventEdgeFlags,
+ pointerCount, pointerIds, pointerCoords,
+ xPrecision, yPrecision, mDownTime);
+}
+
+bool TouchInputMapper::isPointInsideSurfaceLocked(int32_t x, int32_t y) {
+ if (mRawAxes.x.valid && mRawAxes.y.valid) {
+ return x >= mRawAxes.x.minValue && x <= mRawAxes.x.maxValue
+ && y >= mRawAxes.y.minValue && y <= mRawAxes.y.maxValue;
+ }
+ return true;
+}
+
+const TouchInputMapper::VirtualKey* TouchInputMapper::findVirtualKeyHitLocked(
+ int32_t x, int32_t y) {
+ size_t numVirtualKeys = mLocked.virtualKeys.size();
+ for (size_t i = 0; i < numVirtualKeys; i++) {
+ const VirtualKey& virtualKey = mLocked.virtualKeys[i];
+
+#if DEBUG_VIRTUAL_KEYS
+ LOGD("VirtualKeys: Hit test (%d, %d): keyCode=%d, scanCode=%d, "
+ "left=%d, top=%d, right=%d, bottom=%d",
+ x, y,
+ virtualKey.keyCode, virtualKey.scanCode,
+ virtualKey.hitLeft, virtualKey.hitTop,
+ virtualKey.hitRight, virtualKey.hitBottom);
+#endif
+
+ if (virtualKey.isHit(x, y)) {
+ return & virtualKey;
+ }
+ }
+
+ return NULL;
+}
+
+void TouchInputMapper::calculatePointerIds() {
+ uint32_t currentPointerCount = mCurrentTouch.pointerCount;
+ uint32_t lastPointerCount = mLastTouch.pointerCount;
+
+ if (currentPointerCount == 0) {
+ // No pointers to assign.
+ mCurrentTouch.idBits.clear();
+ } else if (lastPointerCount == 0) {
+ // All pointers are new.
+ mCurrentTouch.idBits.clear();
+ for (uint32_t i = 0; i < currentPointerCount; i++) {
+ mCurrentTouch.pointers[i].id = i;
+ mCurrentTouch.idToIndex[i] = i;
+ mCurrentTouch.idBits.markBit(i);
+ }
+ } else if (currentPointerCount == 1 && lastPointerCount == 1) {
+ // Only one pointer and no change in count so it must have the same id as before.
+ uint32_t id = mLastTouch.pointers[0].id;
+ mCurrentTouch.pointers[0].id = id;
+ mCurrentTouch.idToIndex[id] = 0;
+ mCurrentTouch.idBits.value = BitSet32::valueForBit(id);
+ } else {
+ // General case.
+ // We build a heap of squared euclidean distances between current and last pointers
+ // associated with the current and last pointer indices. Then, we find the best
+ // match (by distance) for each current pointer.
+ PointerDistanceHeapElement heap[MAX_POINTERS * MAX_POINTERS];
+
+ uint32_t heapSize = 0;
+ for (uint32_t currentPointerIndex = 0; currentPointerIndex < currentPointerCount;
+ currentPointerIndex++) {
+ for (uint32_t lastPointerIndex = 0; lastPointerIndex < lastPointerCount;
+ lastPointerIndex++) {
+ int64_t deltaX = mCurrentTouch.pointers[currentPointerIndex].x
+ - mLastTouch.pointers[lastPointerIndex].x;
+ int64_t deltaY = mCurrentTouch.pointers[currentPointerIndex].y
+ - mLastTouch.pointers[lastPointerIndex].y;
+
+ uint64_t distance = uint64_t(deltaX * deltaX + deltaY * deltaY);
+
+ // Insert new element into the heap (sift up).
+ heap[heapSize].currentPointerIndex = currentPointerIndex;
+ heap[heapSize].lastPointerIndex = lastPointerIndex;
+ heap[heapSize].distance = distance;
+ heapSize += 1;
+ }
+ }
+
+ // Heapify
+ for (uint32_t startIndex = heapSize / 2; startIndex != 0; ) {
+ startIndex -= 1;
+ for (uint32_t parentIndex = startIndex; ;) {
+ uint32_t childIndex = parentIndex * 2 + 1;
+ if (childIndex >= heapSize) {
+ break;
+ }
+
+ if (childIndex + 1 < heapSize
+ && heap[childIndex + 1].distance < heap[childIndex].distance) {
+ childIndex += 1;
+ }
+
+ if (heap[parentIndex].distance <= heap[childIndex].distance) {
+ break;
+ }
+
+ swap(heap[parentIndex], heap[childIndex]);
+ parentIndex = childIndex;
+ }
+ }
+
+#if DEBUG_POINTER_ASSIGNMENT
+ LOGD("calculatePointerIds - initial distance min-heap: size=%d", heapSize);
+ for (size_t i = 0; i < heapSize; i++) {
+ LOGD(" heap[%d]: cur=%d, last=%d, distance=%lld",
+ i, heap[i].currentPointerIndex, heap[i].lastPointerIndex,
+ heap[i].distance);
+ }
+#endif
+
+ // Pull matches out by increasing order of distance.
+ // To avoid reassigning pointers that have already been matched, the loop keeps track
+ // of which last and current pointers have been matched using the matchedXXXBits variables.
+ // It also tracks the used pointer id bits.
+ BitSet32 matchedLastBits(0);
+ BitSet32 matchedCurrentBits(0);
+ BitSet32 usedIdBits(0);
+ bool first = true;
+ for (uint32_t i = min(currentPointerCount, lastPointerCount); i > 0; i--) {
+ for (;;) {
+ if (first) {
+ // The first time through the loop, we just consume the root element of
+ // the heap (the one with smallest distance).
+ first = false;
+ } else {
+ // Previous iterations consumed the root element of the heap.
+ // Pop root element off of the heap (sift down).
+ heapSize -= 1;
+ assert(heapSize > 0);
+
+ // Sift down.
+ heap[0] = heap[heapSize];
+ for (uint32_t parentIndex = 0; ;) {
+ uint32_t childIndex = parentIndex * 2 + 1;
+ if (childIndex >= heapSize) {
+ break;
+ }
+
+ if (childIndex + 1 < heapSize
+ && heap[childIndex + 1].distance < heap[childIndex].distance) {
+ childIndex += 1;
+ }
+
+ if (heap[parentIndex].distance <= heap[childIndex].distance) {
+ break;
+ }
+
+ swap(heap[parentIndex], heap[childIndex]);
+ parentIndex = childIndex;
+ }
+
+#if DEBUG_POINTER_ASSIGNMENT
+ LOGD("calculatePointerIds - reduced distance min-heap: size=%d", heapSize);
+ for (size_t i = 0; i < heapSize; i++) {
+ LOGD(" heap[%d]: cur=%d, last=%d, distance=%lld",
+ i, heap[i].currentPointerIndex, heap[i].lastPointerIndex,
+ heap[i].distance);
+ }
+#endif
+ }
+
+ uint32_t currentPointerIndex = heap[0].currentPointerIndex;
+ if (matchedCurrentBits.hasBit(currentPointerIndex)) continue; // already matched
+
+ uint32_t lastPointerIndex = heap[0].lastPointerIndex;
+ if (matchedLastBits.hasBit(lastPointerIndex)) continue; // already matched
+
+ matchedCurrentBits.markBit(currentPointerIndex);
+ matchedLastBits.markBit(lastPointerIndex);
+
+ uint32_t id = mLastTouch.pointers[lastPointerIndex].id;
+ mCurrentTouch.pointers[currentPointerIndex].id = id;
+ mCurrentTouch.idToIndex[id] = currentPointerIndex;
+ usedIdBits.markBit(id);
+
+#if DEBUG_POINTER_ASSIGNMENT
+ LOGD("calculatePointerIds - matched: cur=%d, last=%d, id=%d, distance=%lld",
+ lastPointerIndex, currentPointerIndex, id, heap[0].distance);
+#endif
+ break;
+ }
+ }
+
+ // Assign fresh ids to new pointers.
+ if (currentPointerCount > lastPointerCount) {
+ for (uint32_t i = currentPointerCount - lastPointerCount; ;) {
+ uint32_t currentPointerIndex = matchedCurrentBits.firstUnmarkedBit();
+ uint32_t id = usedIdBits.firstUnmarkedBit();
+
+ mCurrentTouch.pointers[currentPointerIndex].id = id;
+ mCurrentTouch.idToIndex[id] = currentPointerIndex;
+ usedIdBits.markBit(id);
+
+#if DEBUG_POINTER_ASSIGNMENT
+ LOGD("calculatePointerIds - assigned: cur=%d, id=%d",
+ currentPointerIndex, id);
+#endif
+
+ if (--i == 0) break; // done
+ matchedCurrentBits.markBit(currentPointerIndex);
+ }
+ }
+
+ // Fix id bits.
+ mCurrentTouch.idBits = usedIdBits;
+ }
+}
+
+/* Special hack for devices that have bad screen data: if one of the
+ * points has moved more than a screen height from the last position,
+ * then drop it. */
+bool TouchInputMapper::applyBadTouchFilter() {
+ // This hack requires valid axis parameters.
+ if (! mRawAxes.y.valid) {
+ return false;
+ }
+
+ uint32_t pointerCount = mCurrentTouch.pointerCount;
+
+ // Nothing to do if there are no points.
+ if (pointerCount == 0) {
+ return false;
+ }
+
+ // Don't do anything if a finger is going down or up. We run
+ // here before assigning pointer IDs, so there isn't a good
+ // way to do per-finger matching.
+ if (pointerCount != mLastTouch.pointerCount) {
+ return false;
+ }
+
+ // We consider a single movement across more than a 7/16 of
+ // the long size of the screen to be bad. This was a magic value
+ // determined by looking at the maximum distance it is feasible
+ // to actually move in one sample.
+ int32_t maxDeltaY = mRawAxes.y.getRange() * 7 / 16;
+
+ // XXX The original code in InputDevice.java included commented out
+ // code for testing the X axis. Note that when we drop a point
+ // we don't actually restore the old X either. Strange.
+ // The old code also tries to track when bad points were previously
+ // detected but it turns out that due to the placement of a "break"
+ // at the end of the loop, we never set mDroppedBadPoint to true
+ // so it is effectively dead code.
+ // Need to figure out if the old code is busted or just overcomplicated
+ // but working as intended.
+
+ // Look through all new points and see if any are farther than
+ // acceptable from all previous points.
+ for (uint32_t i = pointerCount; i-- > 0; ) {
+ int32_t y = mCurrentTouch.pointers[i].y;
+ int32_t closestY = INT_MAX;
+ int32_t closestDeltaY = 0;
+
+#if DEBUG_HACKS
+ LOGD("BadTouchFilter: Looking at next point #%d: y=%d", i, y);
+#endif
+
+ for (uint32_t j = pointerCount; j-- > 0; ) {
+ int32_t lastY = mLastTouch.pointers[j].y;
+ int32_t deltaY = abs(y - lastY);
+
+#if DEBUG_HACKS
+ LOGD("BadTouchFilter: Comparing with last point #%d: y=%d deltaY=%d",
+ j, lastY, deltaY);
+#endif
+
+ if (deltaY < maxDeltaY) {
+ goto SkipSufficientlyClosePoint;
+ }
+ if (deltaY < closestDeltaY) {
+ closestDeltaY = deltaY;
+ closestY = lastY;
+ }
+ }
+
+ // Must not have found a close enough match.
+#if DEBUG_HACKS
+ LOGD("BadTouchFilter: Dropping bad point #%d: newY=%d oldY=%d deltaY=%d maxDeltaY=%d",
+ i, y, closestY, closestDeltaY, maxDeltaY);
+#endif
+
+ mCurrentTouch.pointers[i].y = closestY;
+ return true; // XXX original code only corrects one point
+
+ SkipSufficientlyClosePoint: ;
+ }
+
+ // No change.
+ return false;
+}
+
+/* Special hack for devices that have bad screen data: drop points where
+ * the coordinate value for one axis has jumped to the other pointer's location.
+ */
+bool TouchInputMapper::applyJumpyTouchFilter() {
+ // This hack requires valid axis parameters.
+ if (! mRawAxes.y.valid) {
+ return false;
+ }
+
+ uint32_t pointerCount = mCurrentTouch.pointerCount;
+ if (mLastTouch.pointerCount != pointerCount) {
+#if DEBUG_HACKS
+ LOGD("JumpyTouchFilter: Different pointer count %d -> %d",
+ mLastTouch.pointerCount, pointerCount);
+ for (uint32_t i = 0; i < pointerCount; i++) {
+ LOGD(" Pointer %d (%d, %d)", i,
+ mCurrentTouch.pointers[i].x, mCurrentTouch.pointers[i].y);
+ }
+#endif
+
+ if (mJumpyTouchFilter.jumpyPointsDropped < JUMPY_TRANSITION_DROPS) {
+ if (mLastTouch.pointerCount == 1 && pointerCount == 2) {
+ // Just drop the first few events going from 1 to 2 pointers.
+ // They're bad often enough that they're not worth considering.
+ mCurrentTouch.pointerCount = 1;
+ mJumpyTouchFilter.jumpyPointsDropped += 1;
+
+#if DEBUG_HACKS
+ LOGD("JumpyTouchFilter: Pointer 2 dropped");
+#endif
+ return true;
+ } else if (mLastTouch.pointerCount == 2 && pointerCount == 1) {
+ // The event when we go from 2 -> 1 tends to be messed up too
+ mCurrentTouch.pointerCount = 2;
+ mCurrentTouch.pointers[0] = mLastTouch.pointers[0];
+ mCurrentTouch.pointers[1] = mLastTouch.pointers[1];
+ mJumpyTouchFilter.jumpyPointsDropped += 1;
+
+#if DEBUG_HACKS
+ for (int32_t i = 0; i < 2; i++) {
+ LOGD("JumpyTouchFilter: Pointer %d replaced (%d, %d)", i,
+ mCurrentTouch.pointers[i].x, mCurrentTouch.pointers[i].y);
+ }
+#endif
+ return true;
+ }
+ }
+ // Reset jumpy points dropped on other transitions or if limit exceeded.
+ mJumpyTouchFilter.jumpyPointsDropped = 0;
+
+#if DEBUG_HACKS
+ LOGD("JumpyTouchFilter: Transition - drop limit reset");
+#endif
+ return false;
+ }
+
+ // We have the same number of pointers as last time.
+ // A 'jumpy' point is one where the coordinate value for one axis
+ // has jumped to the other pointer's location. No need to do anything
+ // else if we only have one pointer.
+ if (pointerCount < 2) {
+ return false;
+ }
+
+ if (mJumpyTouchFilter.jumpyPointsDropped < JUMPY_DROP_LIMIT) {
+ int jumpyEpsilon = mRawAxes.y.getRange() / JUMPY_EPSILON_DIVISOR;
+
+ // We only replace the single worst jumpy point as characterized by pointer distance
+ // in a single axis.
+ int32_t badPointerIndex = -1;
+ int32_t badPointerReplacementIndex = -1;
+ int32_t badPointerDistance = INT_MIN; // distance to be corrected
+
+ for (uint32_t i = pointerCount; i-- > 0; ) {
+ int32_t x = mCurrentTouch.pointers[i].x;
+ int32_t y = mCurrentTouch.pointers[i].y;
+
+#if DEBUG_HACKS
+ LOGD("JumpyTouchFilter: Point %d (%d, %d)", i, x, y);
+#endif
+
+ // Check if a touch point is too close to another's coordinates
+ bool dropX = false, dropY = false;
+ for (uint32_t j = 0; j < pointerCount; j++) {
+ if (i == j) {
+ continue;
+ }
+
+ if (abs(x - mCurrentTouch.pointers[j].x) <= jumpyEpsilon) {
+ dropX = true;
+ break;
+ }
+
+ if (abs(y - mCurrentTouch.pointers[j].y) <= jumpyEpsilon) {
+ dropY = true;
+ break;
+ }
+ }
+ if (! dropX && ! dropY) {
+ continue; // not jumpy
+ }
+
+ // Find a replacement candidate by comparing with older points on the
+ // complementary (non-jumpy) axis.
+ int32_t distance = INT_MIN; // distance to be corrected
+ int32_t replacementIndex = -1;
+
+ if (dropX) {
+ // X looks too close. Find an older replacement point with a close Y.
+ int32_t smallestDeltaY = INT_MAX;
+ for (uint32_t j = 0; j < pointerCount; j++) {
+ int32_t deltaY = abs(y - mLastTouch.pointers[j].y);
+ if (deltaY < smallestDeltaY) {
+ smallestDeltaY = deltaY;
+ replacementIndex = j;
+ }
+ }
+ distance = abs(x - mLastTouch.pointers[replacementIndex].x);
+ } else {
+ // Y looks too close. Find an older replacement point with a close X.
+ int32_t smallestDeltaX = INT_MAX;
+ for (uint32_t j = 0; j < pointerCount; j++) {
+ int32_t deltaX = abs(x - mLastTouch.pointers[j].x);
+ if (deltaX < smallestDeltaX) {
+ smallestDeltaX = deltaX;
+ replacementIndex = j;
+ }
+ }
+ distance = abs(y - mLastTouch.pointers[replacementIndex].y);
+ }
+
+ // If replacing this pointer would correct a worse error than the previous ones
+ // considered, then use this replacement instead.
+ if (distance > badPointerDistance) {
+ badPointerIndex = i;
+ badPointerReplacementIndex = replacementIndex;
+ badPointerDistance = distance;
+ }
+ }
+
+ // Correct the jumpy pointer if one was found.
+ if (badPointerIndex >= 0) {
+#if DEBUG_HACKS
+ LOGD("JumpyTouchFilter: Replacing bad pointer %d with (%d, %d)",
+ badPointerIndex,
+ mLastTouch.pointers[badPointerReplacementIndex].x,
+ mLastTouch.pointers[badPointerReplacementIndex].y);
+#endif
+
+ mCurrentTouch.pointers[badPointerIndex].x =
+ mLastTouch.pointers[badPointerReplacementIndex].x;
+ mCurrentTouch.pointers[badPointerIndex].y =
+ mLastTouch.pointers[badPointerReplacementIndex].y;
+ mJumpyTouchFilter.jumpyPointsDropped += 1;
+ return true;
+ }
+ }
+
+ mJumpyTouchFilter.jumpyPointsDropped = 0;
+ return false;
+}
+
+/* Special hack for devices that have bad screen data: aggregate and
+ * compute averages of the coordinate data, to reduce the amount of
+ * jitter seen by applications. */
+void TouchInputMapper::applyAveragingTouchFilter() {
+ for (uint32_t currentIndex = 0; currentIndex < mCurrentTouch.pointerCount; currentIndex++) {
+ uint32_t id = mCurrentTouch.pointers[currentIndex].id;
+ int32_t x = mCurrentTouch.pointers[currentIndex].x;
+ int32_t y = mCurrentTouch.pointers[currentIndex].y;
+ int32_t pressure;
+ switch (mCalibration.pressureSource) {
+ case Calibration::PRESSURE_SOURCE_PRESSURE:
+ pressure = mCurrentTouch.pointers[currentIndex].pressure;
+ break;
+ case Calibration::PRESSURE_SOURCE_TOUCH:
+ pressure = mCurrentTouch.pointers[currentIndex].touchMajor;
+ break;
+ default:
+ pressure = 1;
+ break;
+ }
+
+ if (mLastTouch.idBits.hasBit(id)) {
+ // Pointer was down before and is still down now.
+ // Compute average over history trace.
+ uint32_t start = mAveragingTouchFilter.historyStart[id];
+ uint32_t end = mAveragingTouchFilter.historyEnd[id];
+
+ int64_t deltaX = x - mAveragingTouchFilter.historyData[end].pointers[id].x;
+ int64_t deltaY = y - mAveragingTouchFilter.historyData[end].pointers[id].y;
+ uint64_t distance = uint64_t(deltaX * deltaX + deltaY * deltaY);
+
+#if DEBUG_HACKS
+ LOGD("AveragingTouchFilter: Pointer id %d - Distance from last sample: %lld",
+ id, distance);
+#endif
+
+ if (distance < AVERAGING_DISTANCE_LIMIT) {
+ // Increment end index in preparation for recording new historical data.
+ end += 1;
+ if (end > AVERAGING_HISTORY_SIZE) {
+ end = 0;
+ }
+
+ // If the end index has looped back to the start index then we have filled
+ // the historical trace up to the desired size so we drop the historical
+ // data at the start of the trace.
+ if (end == start) {
+ start += 1;
+ if (start > AVERAGING_HISTORY_SIZE) {
+ start = 0;
+ }
+ }
+
+ // Add the raw data to the historical trace.
+ mAveragingTouchFilter.historyStart[id] = start;
+ mAveragingTouchFilter.historyEnd[id] = end;
+ mAveragingTouchFilter.historyData[end].pointers[id].x = x;
+ mAveragingTouchFilter.historyData[end].pointers[id].y = y;
+ mAveragingTouchFilter.historyData[end].pointers[id].pressure = pressure;
+
+ // Average over all historical positions in the trace by total pressure.
+ int32_t averagedX = 0;
+ int32_t averagedY = 0;
+ int32_t totalPressure = 0;
+ for (;;) {
+ int32_t historicalX = mAveragingTouchFilter.historyData[start].pointers[id].x;
+ int32_t historicalY = mAveragingTouchFilter.historyData[start].pointers[id].y;
+ int32_t historicalPressure = mAveragingTouchFilter.historyData[start]
+ .pointers[id].pressure;
+
+ averagedX += historicalX * historicalPressure;
+ averagedY += historicalY * historicalPressure;
+ totalPressure += historicalPressure;
+
+ if (start == end) {
+ break;
+ }
+
+ start += 1;
+ if (start > AVERAGING_HISTORY_SIZE) {
+ start = 0;
+ }
+ }
+
+ if (totalPressure != 0) {
+ averagedX /= totalPressure;
+ averagedY /= totalPressure;
+
+#if DEBUG_HACKS
+ LOGD("AveragingTouchFilter: Pointer id %d - "
+ "totalPressure=%d, averagedX=%d, averagedY=%d", id, totalPressure,
+ averagedX, averagedY);
+#endif
+
+ mCurrentTouch.pointers[currentIndex].x = averagedX;
+ mCurrentTouch.pointers[currentIndex].y = averagedY;
+ }
+ } else {
+#if DEBUG_HACKS
+ LOGD("AveragingTouchFilter: Pointer id %d - Exceeded max distance", id);
+#endif
+ }
+ } else {
+#if DEBUG_HACKS
+ LOGD("AveragingTouchFilter: Pointer id %d - Pointer went up", id);
+#endif
+ }
+
+ // Reset pointer history.
+ mAveragingTouchFilter.historyStart[id] = 0;
+ mAveragingTouchFilter.historyEnd[id] = 0;
+ mAveragingTouchFilter.historyData[0].pointers[id].x = x;
+ mAveragingTouchFilter.historyData[0].pointers[id].y = y;
+ mAveragingTouchFilter.historyData[0].pointers[id].pressure = pressure;
+ }
+}
+
+int32_t TouchInputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) {
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ if (mLocked.currentVirtualKey.down && mLocked.currentVirtualKey.keyCode == keyCode) {
+ return AKEY_STATE_VIRTUAL;
+ }
+
+ size_t numVirtualKeys = mLocked.virtualKeys.size();
+ for (size_t i = 0; i < numVirtualKeys; i++) {
+ const VirtualKey& virtualKey = mLocked.virtualKeys[i];
+ if (virtualKey.keyCode == keyCode) {
+ return AKEY_STATE_UP;
+ }
+ }
+ } // release lock
+
+ return AKEY_STATE_UNKNOWN;
+}
+
+int32_t TouchInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) {
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ if (mLocked.currentVirtualKey.down && mLocked.currentVirtualKey.scanCode == scanCode) {
+ return AKEY_STATE_VIRTUAL;
+ }
+
+ size_t numVirtualKeys = mLocked.virtualKeys.size();
+ for (size_t i = 0; i < numVirtualKeys; i++) {
+ const VirtualKey& virtualKey = mLocked.virtualKeys[i];
+ if (virtualKey.scanCode == scanCode) {
+ return AKEY_STATE_UP;
+ }
+ }
+ } // release lock
+
+ return AKEY_STATE_UNKNOWN;
+}
+
+bool TouchInputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
+ const int32_t* keyCodes, uint8_t* outFlags) {
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ size_t numVirtualKeys = mLocked.virtualKeys.size();
+ for (size_t i = 0; i < numVirtualKeys; i++) {
+ const VirtualKey& virtualKey = mLocked.virtualKeys[i];
+
+ for (size_t i = 0; i < numCodes; i++) {
+ if (virtualKey.keyCode == keyCodes[i]) {
+ outFlags[i] = 1;
+ }
+ }
+ }
+ } // release lock
+
+ return true;
+}
+
+
+// --- SingleTouchInputMapper ---
+
+SingleTouchInputMapper::SingleTouchInputMapper(InputDevice* device, int32_t associatedDisplayId) :
+ TouchInputMapper(device, associatedDisplayId) {
+ initialize();
+}
+
+SingleTouchInputMapper::~SingleTouchInputMapper() {
+}
+
+void SingleTouchInputMapper::initialize() {
+ mAccumulator.clear();
+
+ mDown = false;
+ mX = 0;
+ mY = 0;
+ mPressure = 0; // default to 0 for devices that don't report pressure
+ mToolWidth = 0; // default to 0 for devices that don't report tool width
+}
+
+void SingleTouchInputMapper::reset() {
+ TouchInputMapper::reset();
+
+ initialize();
+ }
+
+void SingleTouchInputMapper::process(const RawEvent* rawEvent) {
+ switch (rawEvent->type) {
+ case EV_KEY:
+ switch (rawEvent->scanCode) {
+ case BTN_TOUCH:
+ mAccumulator.fields |= Accumulator::FIELD_BTN_TOUCH;
+ mAccumulator.btnTouch = rawEvent->value != 0;
+ // Don't sync immediately. Wait until the next SYN_REPORT since we might
+ // not have received valid position information yet. This logic assumes that
+ // BTN_TOUCH is always followed by SYN_REPORT as part of a complete packet.
+ break;
+ }
+ break;
+
+ case EV_ABS:
+ switch (rawEvent->scanCode) {
+ case ABS_X:
+ mAccumulator.fields |= Accumulator::FIELD_ABS_X;
+ mAccumulator.absX = rawEvent->value;
+ break;
+ case ABS_Y:
+ mAccumulator.fields |= Accumulator::FIELD_ABS_Y;
+ mAccumulator.absY = rawEvent->value;
+ break;
+ case ABS_PRESSURE:
+ mAccumulator.fields |= Accumulator::FIELD_ABS_PRESSURE;
+ mAccumulator.absPressure = rawEvent->value;
+ break;
+ case ABS_TOOL_WIDTH:
+ mAccumulator.fields |= Accumulator::FIELD_ABS_TOOL_WIDTH;
+ mAccumulator.absToolWidth = rawEvent->value;
+ break;
+ }
+ break;
+
+ case EV_SYN:
+ switch (rawEvent->scanCode) {
+ case SYN_REPORT:
+ sync(rawEvent->when);
+ break;
+ }
+ break;
+ }
+}
+
+void SingleTouchInputMapper::sync(nsecs_t when) {
+ uint32_t fields = mAccumulator.fields;
+ if (fields == 0) {
+ return; // no new state changes, so nothing to do
+ }
+
+ if (fields & Accumulator::FIELD_BTN_TOUCH) {
+ mDown = mAccumulator.btnTouch;
+ }
+
+ if (fields & Accumulator::FIELD_ABS_X) {
+ mX = mAccumulator.absX;
+ }
+
+ if (fields & Accumulator::FIELD_ABS_Y) {
+ mY = mAccumulator.absY;
+ }
+
+ if (fields & Accumulator::FIELD_ABS_PRESSURE) {
+ mPressure = mAccumulator.absPressure;
+ }
+
+ if (fields & Accumulator::FIELD_ABS_TOOL_WIDTH) {
+ mToolWidth = mAccumulator.absToolWidth;
+ }
+
+ mCurrentTouch.clear();
+
+ if (mDown) {
+ mCurrentTouch.pointerCount = 1;
+ mCurrentTouch.pointers[0].id = 0;
+ mCurrentTouch.pointers[0].x = mX;
+ mCurrentTouch.pointers[0].y = mY;
+ mCurrentTouch.pointers[0].pressure = mPressure;
+ mCurrentTouch.pointers[0].touchMajor = 0;
+ mCurrentTouch.pointers[0].touchMinor = 0;
+ mCurrentTouch.pointers[0].toolMajor = mToolWidth;
+ mCurrentTouch.pointers[0].toolMinor = mToolWidth;
+ mCurrentTouch.pointers[0].orientation = 0;
+ mCurrentTouch.idToIndex[0] = 0;
+ mCurrentTouch.idBits.markBit(0);
+ }
+
+ syncTouch(when, true);
+
+ mAccumulator.clear();
+}
+
+void SingleTouchInputMapper::configureRawAxes() {
+ TouchInputMapper::configureRawAxes();
+
+ getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_X, & mRawAxes.x);
+ getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_Y, & mRawAxes.y);
+ getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_PRESSURE, & mRawAxes.pressure);
+ getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_TOOL_WIDTH, & mRawAxes.toolMajor);
+}
+
+
+// --- MultiTouchInputMapper ---
+
+MultiTouchInputMapper::MultiTouchInputMapper(InputDevice* device, int32_t associatedDisplayId) :
+ TouchInputMapper(device, associatedDisplayId) {
+ initialize();
+}
+
+MultiTouchInputMapper::~MultiTouchInputMapper() {
+}
+
+void MultiTouchInputMapper::initialize() {
+ mAccumulator.clear();
+}
+
+void MultiTouchInputMapper::reset() {
+ TouchInputMapper::reset();
+
+ initialize();
+}
+
+void MultiTouchInputMapper::process(const RawEvent* rawEvent) {
+ switch (rawEvent->type) {
+ case EV_ABS: {
+ uint32_t pointerIndex = mAccumulator.pointerCount;
+ Accumulator::Pointer* pointer = & mAccumulator.pointers[pointerIndex];
+
+ switch (rawEvent->scanCode) {
+ case ABS_MT_POSITION_X:
+ pointer->fields |= Accumulator::FIELD_ABS_MT_POSITION_X;
+ pointer->absMTPositionX = rawEvent->value;
+ break;
+ case ABS_MT_POSITION_Y:
+ pointer->fields |= Accumulator::FIELD_ABS_MT_POSITION_Y;
+ pointer->absMTPositionY = rawEvent->value;
+ break;
+ case ABS_MT_TOUCH_MAJOR:
+ pointer->fields |= Accumulator::FIELD_ABS_MT_TOUCH_MAJOR;
+ pointer->absMTTouchMajor = rawEvent->value;
+ break;
+ case ABS_MT_TOUCH_MINOR:
+ pointer->fields |= Accumulator::FIELD_ABS_MT_TOUCH_MINOR;
+ pointer->absMTTouchMinor = rawEvent->value;
+ break;
+ case ABS_MT_WIDTH_MAJOR:
+ pointer->fields |= Accumulator::FIELD_ABS_MT_WIDTH_MAJOR;
+ pointer->absMTWidthMajor = rawEvent->value;
+ break;
+ case ABS_MT_WIDTH_MINOR:
+ pointer->fields |= Accumulator::FIELD_ABS_MT_WIDTH_MINOR;
+ pointer->absMTWidthMinor = rawEvent->value;
+ break;
+ case ABS_MT_ORIENTATION:
+ pointer->fields |= Accumulator::FIELD_ABS_MT_ORIENTATION;
+ pointer->absMTOrientation = rawEvent->value;
+ break;
+ case ABS_MT_TRACKING_ID:
+ pointer->fields |= Accumulator::FIELD_ABS_MT_TRACKING_ID;
+ pointer->absMTTrackingId = rawEvent->value;
+ break;
+ case ABS_MT_PRESSURE:
+ pointer->fields |= Accumulator::FIELD_ABS_MT_PRESSURE;
+ pointer->absMTPressure = rawEvent->value;
+ break;
+ }
+ break;
+ }
+
+ case EV_SYN:
+ switch (rawEvent->scanCode) {
+ case SYN_MT_REPORT: {
+ // MultiTouch Sync: The driver has returned all data for *one* of the pointers.
+ uint32_t pointerIndex = mAccumulator.pointerCount;
+
+ if (mAccumulator.pointers[pointerIndex].fields) {
+ if (pointerIndex == MAX_POINTERS) {
+ LOGW("MultiTouch device driver returned more than maximum of %d pointers.",
+ MAX_POINTERS);
+ } else {
+ pointerIndex += 1;
+ mAccumulator.pointerCount = pointerIndex;
+ }
+ }
+
+ mAccumulator.pointers[pointerIndex].clear();
+ break;
+ }
+
+ case SYN_REPORT:
+ sync(rawEvent->when);
+ break;
+ }
+ break;
+ }
+}
+
+void MultiTouchInputMapper::sync(nsecs_t when) {
+ static const uint32_t REQUIRED_FIELDS =
+ Accumulator::FIELD_ABS_MT_POSITION_X | Accumulator::FIELD_ABS_MT_POSITION_Y;
+
+ uint32_t inCount = mAccumulator.pointerCount;
+ uint32_t outCount = 0;
+ bool havePointerIds = true;
+
+ mCurrentTouch.clear();
+
+ for (uint32_t inIndex = 0; inIndex < inCount; inIndex++) {
+ const Accumulator::Pointer& inPointer = mAccumulator.pointers[inIndex];
+ uint32_t fields = inPointer.fields;
+
+ if ((fields & REQUIRED_FIELDS) != REQUIRED_FIELDS) {
+ // Some drivers send empty MT sync packets without X / Y to indicate a pointer up.
+ // Drop this finger.
+ continue;
+ }
+
+ PointerData& outPointer = mCurrentTouch.pointers[outCount];
+ outPointer.x = inPointer.absMTPositionX;
+ outPointer.y = inPointer.absMTPositionY;
+
+ if (fields & Accumulator::FIELD_ABS_MT_PRESSURE) {
+ if (inPointer.absMTPressure <= 0) {
+ // Some devices send sync packets with X / Y but with a 0 presure to indicate
+ // a pointer up. Drop this finger.
+ continue;
+ }
+ outPointer.pressure = inPointer.absMTPressure;
+ } else {
+ // Default pressure to 0 if absent.
+ outPointer.pressure = 0;
+ }
+
+ if (fields & Accumulator::FIELD_ABS_MT_TOUCH_MAJOR) {
+ if (inPointer.absMTTouchMajor <= 0) {
+ // Some devices send sync packets with X / Y but with a 0 touch major to indicate
+ // a pointer going up. Drop this finger.
+ continue;
+ }
+ outPointer.touchMajor = inPointer.absMTTouchMajor;
+ } else {
+ // Default touch area to 0 if absent.
+ outPointer.touchMajor = 0;
+ }
+
+ if (fields & Accumulator::FIELD_ABS_MT_TOUCH_MINOR) {
+ outPointer.touchMinor = inPointer.absMTTouchMinor;
+ } else {
+ // Assume touch area is circular.
+ outPointer.touchMinor = outPointer.touchMajor;
+ }
+
+ if (fields & Accumulator::FIELD_ABS_MT_WIDTH_MAJOR) {
+ outPointer.toolMajor = inPointer.absMTWidthMajor;
+ } else {
+ // Default tool area to 0 if absent.
+ outPointer.toolMajor = 0;
+ }
+
+ if (fields & Accumulator::FIELD_ABS_MT_WIDTH_MINOR) {
+ outPointer.toolMinor = inPointer.absMTWidthMinor;
+ } else {
+ // Assume tool area is circular.
+ outPointer.toolMinor = outPointer.toolMajor;
+ }
+
+ if (fields & Accumulator::FIELD_ABS_MT_ORIENTATION) {
+ outPointer.orientation = inPointer.absMTOrientation;
+ } else {
+ // Default orientation to vertical if absent.
+ outPointer.orientation = 0;
+ }
+
+ // Assign pointer id using tracking id if available.
+ if (havePointerIds) {
+ if (fields & Accumulator::FIELD_ABS_MT_TRACKING_ID) {
+ uint32_t id = uint32_t(inPointer.absMTTrackingId);
+
+ if (id > MAX_POINTER_ID) {
+#if DEBUG_POINTERS
+ LOGD("Pointers: Ignoring driver provided pointer id %d because "
+ "it is larger than max supported id %d for optimizations",
+ id, MAX_POINTER_ID);
+#endif
+ havePointerIds = false;
+ }
+ else {
+ outPointer.id = id;
+ mCurrentTouch.idToIndex[id] = outCount;
+ mCurrentTouch.idBits.markBit(id);
+ }
+ } else {
+ havePointerIds = false;
+ }
+ }
+
+ outCount += 1;
+ }
+
+ mCurrentTouch.pointerCount = outCount;
+
+ syncTouch(when, havePointerIds);
+
+ mAccumulator.clear();
+}
+
+void MultiTouchInputMapper::configureRawAxes() {
+ TouchInputMapper::configureRawAxes();
+
+ getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_POSITION_X, & mRawAxes.x);
+ getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_POSITION_Y, & mRawAxes.y);
+ getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_TOUCH_MAJOR, & mRawAxes.touchMajor);
+ getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_TOUCH_MINOR, & mRawAxes.touchMinor);
+ getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_WIDTH_MAJOR, & mRawAxes.toolMajor);
+ getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_WIDTH_MINOR, & mRawAxes.toolMinor);
+ getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_ORIENTATION, & mRawAxes.orientation);
+ getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_PRESSURE, & mRawAxes.pressure);
+}
+
+
+} // namespace android
diff --git a/libs/ui/InputTransport.cpp b/libs/ui/InputTransport.cpp
new file mode 100644
index 000000000000..4c402dc0202e
--- /dev/null
+++ b/libs/ui/InputTransport.cpp
@@ -0,0 +1,695 @@
+//
+// Copyright 2010 The Android Open Source Project
+//
+// Provides a shared memory transport for input events.
+//
+#define LOG_TAG "InputTransport"
+
+//#define LOG_NDEBUG 0
+
+// Log debug messages about channel signalling (send signal, receive signal)
+#define DEBUG_CHANNEL_SIGNALS 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
+
+
+#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>
+
+namespace android {
+
+// Must be at least sizeof(InputMessage) + sufficient space for pointer data
+static const int DEFAULT_MESSAGE_BUFFER_SIZE = 16384;
+
+// 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.
+static const char INPUT_SIGNAL_FINISHED = 'f';
+
+
+// --- InputChannel ---
+
+InputChannel::InputChannel(const String8& name, int32_t ashmemFd, int32_t receivePipeFd,
+ int32_t sendPipeFd) :
+ mName(name), mAshmemFd(ashmemFd), mReceivePipeFd(receivePipeFd), mSendPipeFd(sendPipeFd) {
+#if DEBUG_CHANNEL_LIFECYCLE
+ LOGD("Input channel constructed: name='%s', ashmemFd=%d, receivePipeFd=%d, sendPipeFd=%d",
+ mName.string(), ashmemFd, receivePipeFd, sendPipeFd);
+#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 "
+ "non-blocking. errno=%d", mName.string(), errno);
+}
+
+InputChannel::~InputChannel() {
+#if DEBUG_CHANNEL_LIFECYCLE
+ LOGD("Input channel destroyed: name='%s', ashmemFd=%d, receivePipeFd=%d, sendPipeFd=%d",
+ mName.string(), mAshmemFd, mReceivePipeFd, mSendPipeFd);
+#endif
+
+ ::close(mAshmemFd);
+ ::close(mReceivePipeFd);
+ ::close(mSendPipeFd);
+}
+
+status_t InputChannel::openInputChannelPair(const String8& name,
+ sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) {
+ status_t result;
+
+ int serverAshmemFd = ashmem_create_region(name.string(), DEFAULT_MESSAGE_BUFFER_SIZE);
+ if (serverAshmemFd < 0) {
+ result = -errno;
+ LOGE("channel '%s' ~ Could not create shared memory region. errno=%d",
+ name.string(), errno);
+ } else {
+ result = ashmem_set_prot_region(serverAshmemFd, PROT_READ | PROT_WRITE);
+ if (result < 0) {
+ LOGE("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;
+ LOGE("channel '%s' ~ Could not dup() shared memory region fd. errno=%d",
+ name.string(), errno);
+ } else {
+ int forward[2];
+ if (pipe(forward)) {
+ result = -errno;
+ LOGE("channel '%s' ~ Could not create forward pipe. errno=%d",
+ name.string(), errno);
+ } else {
+ int reverse[2];
+ if (pipe(reverse)) {
+ result = -errno;
+ LOGE("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;
+}
+
+status_t InputChannel::sendSignal(char signal) {
+ ssize_t nWrite = ::write(mSendPipeFd, & signal, 1);
+
+ if (nWrite == 1) {
+#if DEBUG_CHANNEL_SIGNALS
+ LOGD("channel '%s' ~ sent signal '%c'", mName.string(), signal);
+#endif
+ return OK;
+ }
+
+#if DEBUG_CHANNEL_SIGNALS
+ LOGD("channel '%s' ~ error sending signal '%c', errno=%d", mName.string(), signal, errno);
+#endif
+ return -errno;
+}
+
+status_t InputChannel::receiveSignal(char* outSignal) {
+ ssize_t nRead = ::read(mReceivePipeFd, outSignal, 1);
+ if (nRead == 1) {
+#if DEBUG_CHANNEL_SIGNALS
+ LOGD("channel '%s' ~ received signal '%c'", mName.string(), *outSignal);
+#endif
+ return OK;
+ }
+
+ if (nRead == 0) { // check for EOF
+#if DEBUG_CHANNEL_SIGNALS
+ LOGD("channel '%s' ~ receive signal failed because peer was closed", mName.string());
+#endif
+ return DEAD_OBJECT;
+ }
+
+ if (errno == EAGAIN) {
+#if DEBUG_CHANNEL_SIGNALS
+ LOGD("channel '%s' ~ receive signal failed because no signal available", mName.string());
+#endif
+ return WOULD_BLOCK;
+ }
+
+#if DEBUG_CHANNEL_SIGNALS
+ LOGD("channel '%s' ~ receive signal failed, errno=%d", mName.string(), errno);
+#endif
+ return -errno;
+}
+
+
+// --- InputPublisher ---
+
+InputPublisher::InputPublisher(const sp<InputChannel>& channel) :
+ mChannel(channel), mSharedMessage(NULL),
+ mPinned(false), mSemaphoreInitialized(false), mWasDispatched(false),
+ mMotionEventSampleDataTail(NULL) {
+}
+
+InputPublisher::~InputPublisher() {
+ reset();
+
+ if (mSharedMessage) {
+ munmap(mSharedMessage, mAshmemSize);
+ }
+}
+
+status_t InputPublisher::initialize() {
+#if DEBUG_TRANSPORT_ACTIONS
+ LOGD("channel '%s' publisher ~ initialize",
+ mChannel->getName().string());
+#endif
+
+ int ashmemFd = mChannel->getAshmemFd();
+ int result = ashmem_get_size_region(ashmemFd);
+ if (result < 0) {
+ LOGE("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) {
+ LOGE("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
+ LOGD("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) {
+ LOGE("channel '%s' publisher ~ Error %d in sem_post.",
+ mChannel->getName().string(), errno);
+ return UNKNOWN_ERROR;
+ }
+ }
+
+ result = sem_destroy(& mSharedMessage->semaphore);
+ if (result < 0) {
+ LOGE("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) {
+ LOGE("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) {
+ LOGE("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) {
+ LOGE("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) {
+ LOGE("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(
+ int32_t deviceId,
+ int32_t source,
+ int32_t action,
+ int32_t flags,
+ int32_t keyCode,
+ int32_t scanCode,
+ int32_t metaState,
+ int32_t repeatCount,
+ nsecs_t downTime,
+ nsecs_t eventTime) {
+#if DEBUG_TRANSPORT_ACTIONS
+ LOGD("channel '%s' publisher ~ publishKeyEvent: deviceId=%d, source=0x%x, "
+ "action=0x%x, flags=0x%x, keyCode=%d, scanCode=%d, metaState=0x%x, repeatCount=%d,"
+ "downTime=%lld, eventTime=%lld",
+ mChannel->getName().string(),
+ deviceId, source, action, flags, keyCode, scanCode, metaState, repeatCount,
+ 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;
+}
+
+status_t InputPublisher::publishMotionEvent(
+ int32_t deviceId,
+ int32_t source,
+ int32_t action,
+ int32_t flags,
+ int32_t edgeFlags,
+ int32_t metaState,
+ float xOffset,
+ float yOffset,
+ float xPrecision,
+ float yPrecision,
+ nsecs_t downTime,
+ nsecs_t eventTime,
+ size_t pointerCount,
+ const int32_t* pointerIds,
+ const PointerCoords* pointerCoords) {
+#if DEBUG_TRANSPORT_ACTIONS
+ LOGD("channel '%s' publisher ~ publishMotionEvent: deviceId=%d, source=0x%x, "
+ "action=0x%x, flags=0x%x, edgeFlags=0x%x, metaState=0x%x, xOffset=%f, yOffset=%f, "
+ "xPrecision=%f, yPrecision=%f, downTime=%lld, eventTime=%lld, "
+ "pointerCount=%d",
+ mChannel->getName().string(),
+ deviceId, source, action, flags, edgeFlags, metaState, xOffset, yOffset,
+ xPrecision, yPrecision, downTime, eventTime, pointerCount);
+#endif
+
+ if (pointerCount > MAX_POINTERS || pointerCount < 1) {
+ LOGE("channel '%s' publisher ~ Invalid number of pointers provided: %d.",
+ mChannel->getName().string(), pointerCount);
+ 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.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;
+
+ for (size_t i = 0; i < pointerCount; i++) {
+ mSharedMessage->motion.pointerIds[i] = pointerIds[i];
+ mSharedMessage->motion.sampleData[0].coords[i] = 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) {
+ 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
+ LOGD("channel '%s' publisher ~ appendMotionSample: eventTime=%lld",
+ mChannel->getName().string(), eventTime);
+#endif
+
+ if (! mPinned || ! mMotionEventSampleDataTail) {
+ LOGE("channel '%s' publisher ~ Cannot append motion sample because there is no current "
+ "AMOTION_EVENT_ACTION_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
+ LOGD("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
+ LOGD("channel '%s' publisher ~ Cannot append motion sample because the message has "
+ "already been consumed.", mChannel->getName().string());
+#endif
+ return FAILED_TRANSACTION;
+ } else {
+ LOGE("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] = pointerCoords[i];
+ }
+ mMotionEventSampleDataTail = newTail;
+
+ mSharedMessage->motion.sampleCount += 1;
+
+ if (mWasDispatched) {
+ result = sem_post(& mSharedMessage->semaphore);
+ if (result < 0) {
+ LOGE("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
+ LOGD("channel '%s' publisher ~ sendDispatchSignal",
+ mChannel->getName().string());
+#endif
+
+ mWasDispatched = true;
+ return mChannel->sendSignal(INPUT_SIGNAL_DISPATCH);
+}
+
+status_t InputPublisher::receiveFinishedSignal() {
+#if DEBUG_TRANSPORT_ACTIONS
+ LOGD("channel '%s' publisher ~ receiveFinishedSignal",
+ mChannel->getName().string());
+#endif
+
+ char signal;
+ status_t result = mChannel->receiveSignal(& signal);
+ if (result) {
+ return result;
+ }
+ if (signal != INPUT_SIGNAL_FINISHED) {
+ LOGE("channel '%s' publisher ~ Received unexpected signal '%c' from consumer",
+ mChannel->getName().string(), signal);
+ return UNKNOWN_ERROR;
+ }
+ return OK;
+}
+
+// --- InputConsumer ---
+
+InputConsumer::InputConsumer(const sp<InputChannel>& channel) :
+ mChannel(channel), mSharedMessage(NULL) {
+}
+
+InputConsumer::~InputConsumer() {
+ if (mSharedMessage) {
+ munmap(mSharedMessage, mAshmemSize);
+ }
+}
+
+status_t InputConsumer::initialize() {
+#if DEBUG_TRANSPORT_ACTIONS
+ LOGD("channel '%s' consumer ~ initialize",
+ mChannel->getName().string());
+#endif
+
+ int ashmemFd = mChannel->getAshmemFd();
+ int result = ashmem_get_size_region(ashmemFd);
+ if (result < 0) {
+ LOGE("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) {
+ LOGE("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) {
+#if DEBUG_TRANSPORT_ACTIONS
+ LOGD("channel '%s' consumer ~ consume",
+ mChannel->getName().string());
+#endif
+
+ *outEvent = NULL;
+
+ int ashmemFd = mChannel->getAshmemFd();
+ int result = ashmem_pin_region(ashmemFd, 0, 0);
+ if (result != ASHMEM_NOT_PURGED) {
+ if (result == ASHMEM_WAS_PURGED) {
+ LOGE("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;
+ }
+
+ LOGE("channel '%s' consumer ~ Error %d pinning ashmem fd %d.",
+ mChannel->getName().string(), result, ashmemFd);
+ return UNKNOWN_ERROR;
+ }
+
+ if (mSharedMessage->consumed) {
+ LOGE("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) {
+ LOGE("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: {
+ KeyEvent* keyEvent = factory->createKeyEvent();
+ if (! keyEvent) return NO_MEMORY;
+
+ populateKeyEvent(keyEvent);
+
+ *outEvent = keyEvent;
+ break;
+ }
+
+ case AINPUT_EVENT_TYPE_MOTION: {
+ MotionEvent* motionEvent = factory->createMotionEvent();
+ if (! motionEvent) return NO_MEMORY;
+
+ populateMotionEvent(motionEvent);
+
+ *outEvent = motionEvent;
+ break;
+ }
+
+ default:
+ LOGE("channel '%s' consumer ~ Received message of unknown type %d",
+ mChannel->getName().string(), mSharedMessage->type);
+ return UNKNOWN_ERROR;
+ }
+
+ return OK;
+}
+
+status_t InputConsumer::sendFinishedSignal() {
+#if DEBUG_TRANSPORT_ACTIONS
+ LOGD("channel '%s' consumer ~ sendFinishedSignal",
+ mChannel->getName().string());
+#endif
+
+ return mChannel->sendSignal(INPUT_SIGNAL_FINISHED);
+}
+
+status_t InputConsumer::receiveDispatchSignal() {
+#if DEBUG_TRANSPORT_ACTIONS
+ LOGD("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) {
+ LOGE("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.xOffset,
+ mSharedMessage->motion.yOffset,
+ mSharedMessage->motion.xPrecision,
+ mSharedMessage->motion.yPrecision,
+ mSharedMessage->motion.downTime,
+ mSharedMessage->motion.sampleData[0].eventTime,
+ mSharedMessage->motion.pointerCount,
+ mSharedMessage->motion.pointerIds,
+ 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);
+ }
+ }
+}
+
+} // namespace android
diff --git a/libs/ui/PixelFormat.cpp b/libs/ui/PixelFormat.cpp
index 9b41804eafc5..ee186c84de9c 100644
--- a/libs/ui/PixelFormat.cpp
+++ b/libs/ui/PixelFormat.cpp
@@ -59,19 +59,11 @@ status_t getPixelFormatInfo(PixelFormat format, PixelFormatInfo* info)
// YUV format from the HAL are handled here
switch (format) {
case HAL_PIXEL_FORMAT_YCbCr_422_SP:
- case HAL_PIXEL_FORMAT_YCrCb_422_SP:
- case HAL_PIXEL_FORMAT_YCbCr_422_P:
case HAL_PIXEL_FORMAT_YCbCr_422_I:
- case HAL_PIXEL_FORMAT_CbYCrY_422_I:
info->bitsPerPixel = 16;
goto done;
- case HAL_PIXEL_FORMAT_YCbCr_420_SP:
case HAL_PIXEL_FORMAT_YCrCb_420_SP:
- case HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED:
- case HAL_PIXEL_FORMAT_YCrCb_420_SP_TILED:
- case HAL_PIXEL_FORMAT_YCbCr_420_P:
- case HAL_PIXEL_FORMAT_YCbCr_420_I:
- case HAL_PIXEL_FORMAT_CbYCrY_420_I:
+ case HAL_PIXEL_FORMAT_YV12:
info->bitsPerPixel = 12;
done:
info->format = format;
diff --git a/libs/ui/Rect.cpp b/libs/ui/Rect.cpp
index 66b95762d050..5694e000025d 100644
--- a/libs/ui/Rect.cpp
+++ b/libs/ui/Rect.cpp
@@ -18,11 +18,11 @@
namespace android {
-static inline int min(int a, int b) {
+static inline int32_t min(int32_t a, int32_t b) {
return (a<b) ? a : b;
}
-static inline int max(int a, int b) {
+static inline int32_t max(int32_t a, int32_t b) {
return (a>b) ? a : b;
}
@@ -53,7 +53,7 @@ bool Rect::operator < (const Rect& rhs) const
return false;
}
-Rect& Rect::offsetTo(int x, int y)
+Rect& Rect::offsetTo(int32_t x, int32_t y)
{
right -= left - x;
bottom -= top - y;
@@ -62,7 +62,7 @@ Rect& Rect::offsetTo(int x, int y)
return *this;
}
-Rect& Rect::offsetBy(int x, int y)
+Rect& Rect::offsetBy(int32_t x, int32_t y)
{
left += x;
top += y;
diff --git a/libs/ui/tests/Android.mk b/libs/ui/tests/Android.mk
index 6cc4a5ab6390..62f824fc7216 100644
--- a/libs/ui/tests/Android.mk
+++ b/libs/ui/tests/Android.mk
@@ -1,16 +1,50 @@
+# Build the unit tests.
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_SRC_FILES:= \
- region.cpp
+ifneq ($(TARGET_SIMULATOR),true)
-LOCAL_SHARED_LIBRARIES := \
+# Build the unit tests.
+test_src_files := \
+ InputChannel_test.cpp \
+ InputDispatcher_test.cpp \
+ InputPublisherAndConsumer_test.cpp
+
+shared_libraries := \
libcutils \
libutils \
- libui
+ libEGL \
+ libbinder \
+ libpixelflinger \
+ libhardware \
+ libhardware_legacy \
+ libui \
+ libstlport
+
+static_libraries := \
+ libgtest \
+ libgtest_main
+
+c_includes := \
+ bionic \
+ bionic/libstdc++/include \
+ external/gtest/include \
+ external/stlport/stlport
+
+module_tags := eng tests
-LOCAL_MODULE:= test-region
+$(foreach file,$(test_src_files), \
+ $(eval include $(CLEAR_VARS)) \
+ $(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \
+ $(eval LOCAL_STATIC_LIBRARIES := $(static_libraries)) \
+ $(eval LOCAL_C_INCLUDES := $(c_includes)) \
+ $(eval LOCAL_SRC_FILES := $(file)) \
+ $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \
+ $(eval LOCAL_MODULE_TAGS := $(module_tags)) \
+ $(eval include $(BUILD_EXECUTABLE)) \
+)
-LOCAL_MODULE_TAGS := tests
+# Build the manual test programs.
+include $(call all-subdir-makefiles)
-include $(BUILD_EXECUTABLE)
+endif \ No newline at end of file
diff --git a/libs/ui/tests/InputChannel_test.cpp b/libs/ui/tests/InputChannel_test.cpp
new file mode 100644
index 000000000000..6cec1c02ea95
--- /dev/null
+++ b/libs/ui/tests/InputChannel_test.cpp
@@ -0,0 +1,158 @@
+//
+// Copyright 2010 The Android Open Source Project
+//
+
+#include <ui/InputTransport.h>
+#include <utils/Timers.h>
+#include <utils/StopWatch.h>
+#include <gtest/gtest.h>
+#include <unistd.h>
+#include <time.h>
+#include <sys/mman.h>
+#include <cutils/ashmem.h>
+
+#include "../../utils/tests/TestHelpers.h"
+
+namespace android {
+
+class InputChannelTest : public testing::Test {
+protected:
+ virtual void SetUp() { }
+ virtual void TearDown() { }
+};
+
+
+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
+ // of a pipe and to check for EPIPE on the other end after the channel is destroyed.
+ Pipe fakeAshmem, sendPipe, receivePipe;
+
+ sp<InputChannel> inputChannel = new InputChannel(String8("channel name"),
+ fakeAshmem.sendFd, receivePipe.receiveFd, sendPipe.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";
+
+ 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";
+
+ // 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;
+}
+
+TEST_F(InputChannelTest, OpenInputChannelPair_ReturnsAPairOfConnectedChannels) {
+ sp<InputChannel> serverChannel, clientChannel;
+
+ status_t result = InputChannel::openInputChannelPair(String8("channel name"),
+ serverChannel, clientChannel);
+
+ ASSERT_EQ(OK, result)
+ << "should have successfully opened a channel pair";
+
+ // Name
+ EXPECT_STREQ("channel name (server)", serverChannel->getName().string())
+ << "server channel should have suffixed name";
+ 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";
+
+ // 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";
+}
+
+TEST_F(InputChannelTest, ReceiveSignal_WhenNoSignalPresent_ReturnsAnError) {
+ sp<InputChannel> serverChannel, clientChannel;
+
+ status_t result = InputChannel::openInputChannelPair(String8("channel name"),
+ serverChannel, clientChannel);
+
+ 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";
+}
+
+TEST_F(InputChannelTest, ReceiveSignal_WhenPeerClosed_ReturnsAnError) {
+ sp<InputChannel> serverChannel, clientChannel;
+
+ status_t result = InputChannel::openInputChannelPair(String8("channel name"),
+ serverChannel, clientChannel);
+
+ ASSERT_EQ(OK, result)
+ << "should have successfully opened a channel pair";
+
+ serverChannel.clear(); // close server channel
+
+ char signal;
+ EXPECT_EQ(DEAD_OBJECT, clientChannel->receiveSignal(& signal))
+ << "receiveSignal should have returned DEAD_OBJECT";
+}
+
+TEST_F(InputChannelTest, SendSignal_WhenPeerClosed_ReturnsAnError) {
+ sp<InputChannel> serverChannel, clientChannel;
+
+ status_t result = InputChannel::openInputChannelPair(String8("channel name"),
+ serverChannel, clientChannel);
+
+ ASSERT_EQ(OK, result)
+ << "should have successfully opened a channel pair";
+
+ serverChannel.clear(); // close server channel
+
+ EXPECT_EQ(DEAD_OBJECT, clientChannel->sendSignal('S'))
+ << "sendSignal should have returned DEAD_OBJECT";
+}
+
+
+} // namespace android
diff --git a/libs/ui/tests/InputDispatcher_test.cpp b/libs/ui/tests/InputDispatcher_test.cpp
new file mode 100644
index 000000000000..1dc6e46a453d
--- /dev/null
+++ b/libs/ui/tests/InputDispatcher_test.cpp
@@ -0,0 +1,18 @@
+//
+// Copyright 2010 The Android Open Source Project
+//
+
+#include <ui/InputDispatcher.h>
+#include <gtest/gtest.h>
+
+namespace android {
+
+class InputDispatcherTest : public testing::Test {
+public:
+};
+
+TEST_F(InputDispatcherTest, Dummy) {
+ // TODO
+}
+
+} // namespace android
diff --git a/libs/ui/tests/InputPublisherAndConsumer_test.cpp b/libs/ui/tests/InputPublisherAndConsumer_test.cpp
new file mode 100644
index 000000000000..952b9747691e
--- /dev/null
+++ b/libs/ui/tests/InputPublisherAndConsumer_test.cpp
@@ -0,0 +1,471 @@
+//
+// Copyright 2010 The Android Open Source Project
+//
+
+#include <ui/InputTransport.h>
+#include <utils/Timers.h>
+#include <utils/StopWatch.h>
+#include <gtest/gtest.h>
+#include <unistd.h>
+#include <time.h>
+#include <sys/mman.h>
+#include <cutils/ashmem.h>
+
+#include "../../utils/tests/TestHelpers.h"
+
+namespace android {
+
+class InputPublisherAndConsumerTest : public testing::Test {
+protected:
+ sp<InputChannel> serverChannel, clientChannel;
+ InputPublisher* mPublisher;
+ InputConsumer* mConsumer;
+ PreallocatedInputEventFactory mEventFactory;
+
+ virtual void SetUp() {
+ status_t result = InputChannel::openInputChannelPair(String8("channel name"),
+ serverChannel, clientChannel);
+
+ mPublisher = new InputPublisher(serverChannel);
+ mConsumer = new InputConsumer(clientChannel);
+ }
+
+ virtual void TearDown() {
+ if (mPublisher) {
+ delete mPublisher;
+ mPublisher = NULL;
+ }
+
+ if (mConsumer) {
+ delete mConsumer;
+ mConsumer = NULL;
+ }
+
+ serverChannel.clear();
+ clientChannel.clear();
+ }
+
+ void Initialize();
+ void PublishAndConsumeKeyEvent();
+ void PublishAndConsumeMotionEvent(
+ size_t samplesToAppendBeforeDispatch = 0,
+ size_t samplesToAppendAfterDispatch = 0);
+};
+
+TEST_F(InputPublisherAndConsumerTest, GetChannel_ReturnsTheChannel) {
+ EXPECT_EQ(serverChannel.get(), mPublisher->getChannel().get());
+ 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;
+
+ const int32_t deviceId = 1;
+ const int32_t source = AINPUT_SOURCE_KEYBOARD;
+ const int32_t action = AKEY_EVENT_ACTION_DOWN;
+ const int32_t flags = AKEY_EVENT_FLAG_FROM_SYSTEM;
+ const int32_t keyCode = AKEYCODE_ENTER;
+ const int32_t scanCode = 13;
+ const int32_t metaState = AMETA_ALT_LEFT_ON | AMETA_ALT_ON;
+ const int32_t repeatCount = 1;
+ const nsecs_t downTime = 3;
+ const nsecs_t eventTime = 4;
+
+ status = mPublisher->publishKeyEvent(deviceId, source, action, flags,
+ keyCode, scanCode, metaState, repeatCount, downTime, eventTime);
+ 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)
+ << "consumer consume should return OK";
+
+ ASSERT_TRUE(event != NULL)
+ << "consumer should have returned non-NULL event";
+ ASSERT_EQ(AINPUT_EVENT_TYPE_KEY, event->getType())
+ << "consumer should have returned a key event";
+
+ KeyEvent* keyEvent = static_cast<KeyEvent*>(event);
+ EXPECT_EQ(deviceId, keyEvent->getDeviceId());
+ EXPECT_EQ(source, keyEvent->getSource());
+ EXPECT_EQ(action, keyEvent->getAction());
+ EXPECT_EQ(flags, keyEvent->getFlags());
+ EXPECT_EQ(keyCode, keyEvent->getKeyCode());
+ EXPECT_EQ(scanCode, keyEvent->getScanCode());
+ EXPECT_EQ(metaState, keyEvent->getMetaState());
+ EXPECT_EQ(repeatCount, keyEvent->getRepeatCount());
+ EXPECT_EQ(downTime, keyEvent->getDownTime());
+ EXPECT_EQ(eventTime, keyEvent->getEventTime());
+
+ status = mConsumer->sendFinishedSignal();
+ ASSERT_EQ(OK, status)
+ << "consumer sendFinishedSignal should return OK";
+
+ status = mPublisher->receiveFinishedSignal();
+ ASSERT_EQ(OK, status)
+ << "publisher receiveFinishedSignal should return OK";
+
+ status = mPublisher->reset();
+ ASSERT_EQ(OK, status)
+ << "publisher reset should return OK";
+}
+
+void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent(
+ size_t samplesToAppendBeforeDispatch, size_t samplesToAppendAfterDispatch) {
+ status_t status;
+
+ const int32_t deviceId = 1;
+ const int32_t source = AINPUT_SOURCE_TOUCHSCREEN;
+ const int32_t action = AMOTION_EVENT_ACTION_MOVE;
+ const int32_t flags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED;
+ const int32_t edgeFlags = AMOTION_EVENT_EDGE_FLAG_TOP;
+ const int32_t metaState = AMETA_ALT_LEFT_ON | AMETA_ALT_ON;
+ const float xOffset = -10;
+ const float yOffset = -20;
+ const float xPrecision = 0.25;
+ const float yPrecision = 0.5;
+ const nsecs_t downTime = 3;
+ const size_t pointerCount = 3;
+ const int32_t pointerIds[pointerCount] = { 2, 0, 1 };
+
+ 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();
+ samplePointerCoords.editTop().x = 100 * i + j;
+ samplePointerCoords.editTop().y = 200 * i + j;
+ samplePointerCoords.editTop().pressure = 0.5 * i + j;
+ samplePointerCoords.editTop().size = 0.7 * i + j;
+ samplePointerCoords.editTop().touchMajor = 1.5 * i + j;
+ samplePointerCoords.editTop().touchMinor = 1.7 * i + j;
+ samplePointerCoords.editTop().toolMajor = 2.5 * i + j;
+ samplePointerCoords.editTop().toolMinor = 2.7 * i + j;
+ samplePointerCoords.editTop().orientation = 3.5 * i + j;
+ }
+ }
+
+ status = mPublisher->publishMotionEvent(deviceId, source, action, flags, edgeFlags,
+ metaState, xOffset, yOffset, xPrecision, yPrecision,
+ downTime, sampleEventTimes[0], pointerCount, pointerIds, samplePointerCoords.array());
+ 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)
+ << "consumer consume should return OK";
+
+ ASSERT_TRUE(event != NULL)
+ << "consumer should have returned non-NULL event";
+ 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());
+ EXPECT_EQ(action, motionEvent->getAction());
+ EXPECT_EQ(flags, motionEvent->getFlags());
+ EXPECT_EQ(edgeFlags, motionEvent->getEdgeFlags());
+ EXPECT_EQ(metaState, motionEvent->getMetaState());
+ EXPECT_EQ(xPrecision, motionEvent->getXPrecision());
+ EXPECT_EQ(yPrecision, motionEvent->getYPrecision());
+ EXPECT_EQ(downTime, motionEvent->getDownTime());
+ EXPECT_EQ(sampleEventTimes[lastSampleIndex], motionEvent->getEventTime());
+ EXPECT_EQ(pointerCount, motionEvent->getPointerCount());
+ EXPECT_EQ(lastSampleIndex, motionEvent->getHistorySize());
+
+ for (size_t i = 0; i < pointerCount; i++) {
+ SCOPED_TRACE(i);
+ EXPECT_EQ(pointerIds[i], motionEvent->getPointerId(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].x,
+ motionEvent->getHistoricalRawX(i, sampleIndex));
+ EXPECT_EQ(samplePointerCoords[offset].y,
+ motionEvent->getHistoricalRawY(i, sampleIndex));
+ EXPECT_EQ(samplePointerCoords[offset].x + xOffset,
+ motionEvent->getHistoricalX(i, sampleIndex));
+ EXPECT_EQ(samplePointerCoords[offset].y + yOffset,
+ motionEvent->getHistoricalY(i, sampleIndex));
+ EXPECT_EQ(samplePointerCoords[offset].pressure,
+ motionEvent->getHistoricalPressure(i, sampleIndex));
+ EXPECT_EQ(samplePointerCoords[offset].size,
+ motionEvent->getHistoricalSize(i, sampleIndex));
+ EXPECT_EQ(samplePointerCoords[offset].touchMajor,
+ motionEvent->getHistoricalTouchMajor(i, sampleIndex));
+ EXPECT_EQ(samplePointerCoords[offset].touchMinor,
+ motionEvent->getHistoricalTouchMinor(i, sampleIndex));
+ EXPECT_EQ(samplePointerCoords[offset].toolMajor,
+ motionEvent->getHistoricalToolMajor(i, sampleIndex));
+ EXPECT_EQ(samplePointerCoords[offset].toolMinor,
+ motionEvent->getHistoricalToolMinor(i, sampleIndex));
+ EXPECT_EQ(samplePointerCoords[offset].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].x, motionEvent->getRawX(i));
+ EXPECT_EQ(samplePointerCoords[offset].y, motionEvent->getRawY(i));
+ EXPECT_EQ(samplePointerCoords[offset].x + xOffset, motionEvent->getX(i));
+ EXPECT_EQ(samplePointerCoords[offset].y + yOffset, motionEvent->getY(i));
+ EXPECT_EQ(samplePointerCoords[offset].pressure, motionEvent->getPressure(i));
+ EXPECT_EQ(samplePointerCoords[offset].size, motionEvent->getSize(i));
+ EXPECT_EQ(samplePointerCoords[offset].touchMajor, motionEvent->getTouchMajor(i));
+ EXPECT_EQ(samplePointerCoords[offset].touchMinor, motionEvent->getTouchMinor(i));
+ EXPECT_EQ(samplePointerCoords[offset].toolMajor, motionEvent->getToolMajor(i));
+ EXPECT_EQ(samplePointerCoords[offset].toolMinor, motionEvent->getToolMinor(i));
+ EXPECT_EQ(samplePointerCoords[offset].orientation, motionEvent->getOrientation(i));
+ }
+
+ status = mConsumer->sendFinishedSignal();
+ ASSERT_EQ(OK, status)
+ << "consumer sendFinishedSignal should return OK";
+
+ status = mPublisher->receiveFinishedSignal();
+ ASSERT_EQ(OK, status)
+ << "publisher receiveFinishedSignal should return OK";
+
+ 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;
+ int32_t pointerIds[pointerCount] = { 0 };
+ PointerCoords pointerCoords[pointerCount] = { { 0, 0, 0, 0, 0, 0, 0, 0, 0 } };
+
+ status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ pointerCount, pointerIds, 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,
+ pointerCount, pointerIds, 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;
+ int32_t pointerIds[pointerCount];
+ PointerCoords pointerCoords[pointerCount];
+
+ status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ pointerCount, pointerIds, pointerCoords);
+ ASSERT_EQ(BAD_VALUE, status)
+ << "publisher publishMotionEvent should return BAD_VALUE";
+}
+
+TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenPointerCountGreaterThanMax_ReturnsError) {
+ status_t status;
+ ASSERT_NO_FATAL_FAILURE(Initialize());
+
+ const size_t pointerCount = MAX_POINTERS + 1;
+ int32_t pointerIds[pointerCount];
+ PointerCoords pointerCoords[pointerCount];
+
+ status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ pointerCount, pointerIds, pointerCoords);
+ ASSERT_EQ(BAD_VALUE, status)
+ << "publisher publishMotionEvent should return BAD_VALUE";
+}
+
+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());
+ ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent());
+ 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;
+ int32_t pointerIds[pointerCount];
+ PointerCoords pointerCoords[pointerCount];
+
+ status = mPublisher->publishMotionEvent(0, 0, AMOTION_EVENT_ACTION_DOWN,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, pointerCount, pointerIds, 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;
+ int32_t pointerIds[pointerCount];
+ PointerCoords pointerCoords[pointerCount];
+
+ status = mPublisher->publishMotionEvent(0, 0, AMOTION_EVENT_ACTION_MOVE,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, pointerCount, pointerIds, 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;
+ int32_t pointerIds[pointerCount];
+ PointerCoords pointerCoords[pointerCount];
+
+ status = mPublisher->publishMotionEvent(0, 0, AMOTION_EVENT_ACTION_MOVE,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, pointerCount, pointerIds, 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
diff --git a/libs/ui/tests/region/Android.mk b/libs/ui/tests/region/Android.mk
new file mode 100644
index 000000000000..6cc4a5ab6390
--- /dev/null
+++ b/libs/ui/tests/region/Android.mk
@@ -0,0 +1,16 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ region.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+ libcutils \
+ libutils \
+ libui
+
+LOCAL_MODULE:= test-region
+
+LOCAL_MODULE_TAGS := tests
+
+include $(BUILD_EXECUTABLE)
diff --git a/libs/ui/tests/region.cpp b/libs/ui/tests/region/region.cpp
index ef15de9747e0..ef15de9747e0 100644
--- a/libs/ui/tests/region.cpp
+++ b/libs/ui/tests/region/region.cpp