diff options
Diffstat (limited to 'libs')
53 files changed, 4686 insertions, 13580 deletions
diff --git a/libs/binder/Android.mk b/libs/binder/Android.mk index 13dc500d94..f9d9f25922 100644 --- a/libs/binder/Android.mk +++ b/libs/binder/Android.mk @@ -16,6 +16,7 @@ sources := \ Binder.cpp \ BpBinder.cpp \ + CursorWindow.cpp \ IInterface.cpp \ IMemory.cpp \ IPCThreadState.cpp \ diff --git a/libs/binder/CursorWindow.cpp b/libs/binder/CursorWindow.cpp new file mode 100644 index 0000000000..47bbd04e1f --- /dev/null +++ b/libs/binder/CursorWindow.cpp @@ -0,0 +1,412 @@ +/* + * Copyright (C) 2006-2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#undef LOG_TAG +#define LOG_TAG "CursorWindow" + +#include <utils/Log.h> +#include <binder/CursorWindow.h> +#include <binder/MemoryHeapBase.h> +#include <binder/MemoryBase.h> + +#include <assert.h> +#include <string.h> +#include <stdlib.h> + +namespace android { + +CursorWindow::CursorWindow(size_t maxSize) : + mMaxSize(maxSize) +{ +} + +bool CursorWindow::setMemory(const sp<IMemory>& memory) +{ + mMemory = memory; + mData = (uint8_t *) memory->pointer(); + if (mData == NULL) { + return false; + } + mHeader = (window_header_t *) mData; + + // Make the window read-only + ssize_t size = memory->size(); + mSize = size; + mMaxSize = size; + mFreeOffset = size; +LOG_WINDOW("Created CursorWindow from existing IMemory: mFreeOffset = %d, numRows = %d, numColumns = %d, mSize = %d, mMaxSize = %d, mData = %p", mFreeOffset, mHeader->numRows, mHeader->numColumns, mSize, mMaxSize, mData); + return true; +} + +bool CursorWindow::initBuffer(bool localOnly) +{ + //TODO Use a non-memory dealer mmap region for localOnly + + sp<MemoryHeapBase> heap; + heap = new MemoryHeapBase(mMaxSize, 0, "CursorWindow"); + if (heap != NULL) { + mMemory = new MemoryBase(heap, 0, mMaxSize); + if (mMemory != NULL) { + mData = (uint8_t *) mMemory->pointer(); + if (mData) { + mHeader = (window_header_t *) mData; + mSize = mMaxSize; + + // Put the window into a clean state + clear(); + LOG_WINDOW("Created CursorWindow with new MemoryDealer: mFreeOffset = %d, mSize = %d, mMaxSize = %d, mData = %p", mFreeOffset, mSize, mMaxSize, mData); + return true; + } + } + LOGE("CursorWindow heap allocation failed"); + return false; + } else { + LOGE("failed to create the CursorWindow heap"); + return false; + } +} + +CursorWindow::~CursorWindow() +{ + // Everything that matters is a smart pointer +} + +void CursorWindow::clear() +{ + mHeader->numRows = 0; + mHeader->numColumns = 0; + mFreeOffset = sizeof(window_header_t) + ROW_SLOT_CHUNK_SIZE; + // Mark the first chunk's next 'pointer' as null + *((uint32_t *)(mData + mFreeOffset - sizeof(uint32_t))) = 0; +} + +int32_t CursorWindow::freeSpace() +{ + int32_t freeSpace = mSize - mFreeOffset; + if (freeSpace < 0) { + freeSpace = 0; + } + return freeSpace; +} + +field_slot_t * CursorWindow::allocRow() +{ + // Fill in the row slot + row_slot_t * rowSlot = allocRowSlot(); + if (rowSlot == NULL) { + return NULL; + } + + // Allocate the slots for the field directory + size_t fieldDirSize = mHeader->numColumns * sizeof(field_slot_t); + uint32_t fieldDirOffset = alloc(fieldDirSize); + if (!fieldDirOffset) { + mHeader->numRows--; + LOG_WINDOW("The row failed, so back out the new row accounting from allocRowSlot %d", mHeader->numRows); + return NULL; + } + field_slot_t * fieldDir = (field_slot_t *)offsetToPtr(fieldDirOffset); + memset(fieldDir, 0x0, fieldDirSize); + +LOG_WINDOW("Allocated row %u, rowSlot is at offset %u, fieldDir is %d bytes at offset %u\n", (mHeader->numRows - 1), ((uint8_t *)rowSlot) - mData, fieldDirSize, fieldDirOffset); + rowSlot->offset = fieldDirOffset; + + return fieldDir; +} + +uint32_t CursorWindow::alloc(size_t requestedSize, bool aligned) +{ + int32_t size; + uint32_t padding; + if (aligned) { + // 4 byte alignment + padding = 4 - (mFreeOffset & 0x3); + } else { + padding = 0; + } + + size = requestedSize + padding; + + if (size > freeSpace()) { + LOGV("need to grow: mSize = %d, size = %d, freeSpace() = %d, numRows = %d", mSize, size, + freeSpace(), mHeader->numRows); + // Only grow the window if the first row doesn't fit + if (mHeader->numRows > 1) { + LOGV("not growing since there are already %d row(s), max size %d", mHeader->numRows, + mMaxSize); + return 0; + } + + // Find a new size that will fit the allocation + int allocated = mSize - freeSpace(); + int newSize = mSize + WINDOW_ALLOCATION_SIZE; + while (size > (newSize - allocated)) { + newSize += WINDOW_ALLOCATION_SIZE; + if (newSize > mMaxSize) { + LOGE("Attempting to grow window beyond max size (%d)", mMaxSize); + return 0; + } + } +LOG_WINDOW("found size %d", newSize); + mSize = newSize; + } + + uint32_t offset = mFreeOffset + padding; + mFreeOffset += size; + return offset; +} + +row_slot_t * CursorWindow::getRowSlot(int row) +{ + LOG_WINDOW("enter getRowSlot current row num %d, this row %d", mHeader->numRows, row); + int chunkNum = row / ROW_SLOT_CHUNK_NUM_ROWS; + int chunkPos = row % ROW_SLOT_CHUNK_NUM_ROWS; + int chunkPtrOffset = sizeof(window_header_t) + ROW_SLOT_CHUNK_SIZE - sizeof(uint32_t); + uint8_t * rowChunk = mData + sizeof(window_header_t); + for (int i = 0; i < chunkNum; i++) { + rowChunk = offsetToPtr(*((uint32_t *)(mData + chunkPtrOffset))); + chunkPtrOffset = rowChunk - mData + (ROW_SLOT_CHUNK_NUM_ROWS * sizeof(row_slot_t)); + } + return (row_slot_t *)(rowChunk + (chunkPos * sizeof(row_slot_t))); + LOG_WINDOW("exit getRowSlot current row num %d, this row %d", mHeader->numRows, row); +} + +row_slot_t * CursorWindow::allocRowSlot() +{ + int chunkNum = mHeader->numRows / ROW_SLOT_CHUNK_NUM_ROWS; + int chunkPos = mHeader->numRows % ROW_SLOT_CHUNK_NUM_ROWS; + int chunkPtrOffset = sizeof(window_header_t) + ROW_SLOT_CHUNK_SIZE - sizeof(uint32_t); + uint8_t * rowChunk = mData + sizeof(window_header_t); +LOG_WINDOW("Allocating row slot, mHeader->numRows is %d, chunkNum is %d, chunkPos is %d", mHeader->numRows, chunkNum, chunkPos); + for (int i = 0; i < chunkNum; i++) { + uint32_t nextChunkOffset = *((uint32_t *)(mData + chunkPtrOffset)); +LOG_WINDOW("nextChunkOffset is %d", nextChunkOffset); + if (nextChunkOffset == 0) { + // Allocate a new row chunk + nextChunkOffset = alloc(ROW_SLOT_CHUNK_SIZE, true); + if (nextChunkOffset == 0) { + return NULL; + } + rowChunk = offsetToPtr(nextChunkOffset); +LOG_WINDOW("allocated new chunk at %d, rowChunk = %p", nextChunkOffset, rowChunk); + *((uint32_t *)(mData + chunkPtrOffset)) = rowChunk - mData; + // Mark the new chunk's next 'pointer' as null + *((uint32_t *)(rowChunk + ROW_SLOT_CHUNK_SIZE - sizeof(uint32_t))) = 0; + } else { +LOG_WINDOW("follwing 'pointer' to next chunk, offset of next pointer is %d", chunkPtrOffset); + rowChunk = offsetToPtr(nextChunkOffset); + chunkPtrOffset = rowChunk - mData + (ROW_SLOT_CHUNK_NUM_ROWS * sizeof(row_slot_t)); + } + } + mHeader->numRows++; + + return (row_slot_t *)(rowChunk + (chunkPos * sizeof(row_slot_t))); +} + +field_slot_t * CursorWindow::getFieldSlotWithCheck(int row, int column) +{ + if (row < 0 || row >= mHeader->numRows || column < 0 || column >= mHeader->numColumns) { + LOGE("Failed to read row# %d, column# from a CursorWindow which has %d rows, %d columns.", + row, column, mHeader->numRows, mHeader->numColumns); + return NULL; + } + row_slot_t * rowSlot = getRowSlot(row); + if (!rowSlot) { + LOGE("Failed to find rowSlot for row %d", row); + return NULL; + } + if (rowSlot->offset == 0 || rowSlot->offset >= mSize) { + LOGE("Invalid rowSlot, offset = %d", rowSlot->offset); + return NULL; + } + int fieldDirOffset = rowSlot->offset; + return ((field_slot_t *)offsetToPtr(fieldDirOffset)) + column; +} + +uint32_t CursorWindow::read_field_slot(int row, int column, field_slot_t * slotOut) +{ + if (row < 0 || row >= mHeader->numRows || column < 0 || column >= mHeader->numColumns) { + LOGE("Can't read row# %d, col# %d from CursorWindow. Make sure your Cursor is initialized correctly.", + row, column); + return -1; + } + row_slot_t * rowSlot = getRowSlot(row); + if (!rowSlot) { + LOGE("Failed to find rowSlot for row %d", row); + return -1; + } + if (rowSlot->offset == 0 || rowSlot->offset >= mSize) { + LOGE("Invalid rowSlot, offset = %d", rowSlot->offset); + return -1; + } +LOG_WINDOW("Found field directory for %d,%d at rowSlot %d, offset %d", row, column, (uint8_t *)rowSlot - mData, rowSlot->offset); + field_slot_t * fieldDir = (field_slot_t *)offsetToPtr(rowSlot->offset); +LOG_WINDOW("Read field_slot_t %d,%d: offset = %d, size = %d, type = %d", row, column, fieldDir[column].data.buffer.offset, fieldDir[column].data.buffer.size, fieldDir[column].type); + + // Copy the data to the out param + slotOut->data.buffer.offset = fieldDir[column].data.buffer.offset; + slotOut->data.buffer.size = fieldDir[column].data.buffer.size; + slotOut->type = fieldDir[column].type; + return 0; +} + +void CursorWindow::copyIn(uint32_t offset, uint8_t const * data, size_t size) +{ + assert(offset + size <= mSize); + memcpy(mData + offset, data, size); +} + +void CursorWindow::copyIn(uint32_t offset, int64_t data) +{ + assert(offset + sizeof(int64_t) <= mSize); + memcpy(mData + offset, (uint8_t *)&data, sizeof(int64_t)); +} + +void CursorWindow::copyIn(uint32_t offset, double data) +{ + assert(offset + sizeof(double) <= mSize); + memcpy(mData + offset, (uint8_t *)&data, sizeof(double)); +} + +void CursorWindow::copyOut(uint32_t offset, uint8_t * data, size_t size) +{ + assert(offset + size <= mSize); + memcpy(data, mData + offset, size); +} + +int64_t CursorWindow::copyOutLong(uint32_t offset) +{ + int64_t value; + assert(offset + sizeof(int64_t) <= mSize); + memcpy(&value, mData + offset, sizeof(int64_t)); + return value; +} + +double CursorWindow::copyOutDouble(uint32_t offset) +{ + double value; + assert(offset + sizeof(double) <= mSize); + memcpy(&value, mData + offset, sizeof(double)); + return value; +} + +bool CursorWindow::putLong(unsigned int row, unsigned int col, int64_t value) +{ + field_slot_t * fieldSlot = getFieldSlotWithCheck(row, col); + if (!fieldSlot) { + return false; + } + +#if WINDOW_STORAGE_INLINE_NUMERICS + fieldSlot->data.l = value; +#else + int offset = alloc(sizeof(int64_t)); + if (!offset) { + return false; + } + + copyIn(offset, value); + + fieldSlot->data.buffer.offset = offset; + fieldSlot->data.buffer.size = sizeof(int64_t); +#endif + fieldSlot->type = FIELD_TYPE_INTEGER; + return true; +} + +bool CursorWindow::putDouble(unsigned int row, unsigned int col, double value) +{ + field_slot_t * fieldSlot = getFieldSlotWithCheck(row, col); + if (!fieldSlot) { + return false; + } + +#if WINDOW_STORAGE_INLINE_NUMERICS + fieldSlot->data.d = value; +#else + int offset = alloc(sizeof(int64_t)); + if (!offset) { + return false; + } + + copyIn(offset, value); + + fieldSlot->data.buffer.offset = offset; + fieldSlot->data.buffer.size = sizeof(double); +#endif + fieldSlot->type = FIELD_TYPE_FLOAT; + return true; +} + +bool CursorWindow::putNull(unsigned int row, unsigned int col) +{ + field_slot_t * fieldSlot = getFieldSlotWithCheck(row, col); + if (!fieldSlot) { + return false; + } + + fieldSlot->type = FIELD_TYPE_NULL; + fieldSlot->data.buffer.offset = 0; + fieldSlot->data.buffer.size = 0; + return true; +} + +bool CursorWindow::getLong(unsigned int row, unsigned int col, int64_t * valueOut) +{ + field_slot_t * fieldSlot = getFieldSlotWithCheck(row, col); + if (!fieldSlot || fieldSlot->type != FIELD_TYPE_INTEGER) { + return false; + } + +#if WINDOW_STORAGE_INLINE_NUMERICS + *valueOut = fieldSlot->data.l; +#else + *valueOut = copyOutLong(fieldSlot->data.buffer.offset); +#endif + return true; +} + +bool CursorWindow::getDouble(unsigned int row, unsigned int col, double * valueOut) +{ + field_slot_t * fieldSlot = getFieldSlotWithCheck(row, col); + if (!fieldSlot || fieldSlot->type != FIELD_TYPE_FLOAT) { + return false; + } + +#if WINDOW_STORAGE_INLINE_NUMERICS + *valueOut = fieldSlot->data.d; +#else + *valueOut = copyOutDouble(fieldSlot->data.buffer.offset); +#endif + return true; +} + +bool CursorWindow::getNull(unsigned int row, unsigned int col, bool * valueOut) +{ + field_slot_t * fieldSlot = getFieldSlotWithCheck(row, col); + if (!fieldSlot) { + return false; + } + + if (fieldSlot->type != FIELD_TYPE_NULL) { + *valueOut = false; + } else { + *valueOut = true; + } + return true; +} + +}; // namespace android diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp index 13c58f04fc..95cfddf758 100644 --- a/libs/binder/IPCThreadState.cpp +++ b/libs/binder/IPCThreadState.cpp @@ -318,6 +318,16 @@ restart: goto restart; } +IPCThreadState* IPCThreadState::selfOrNull() +{ + if (gHaveTLS) { + const pthread_key_t k = gTLS; + IPCThreadState* st = (IPCThreadState*)pthread_getspecific(k); + return st; + } + return NULL; +} + void IPCThreadState::shutdown() { gShutdown = true; diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp index f329ac4642..d57f2c9fdc 100644 --- a/libs/binder/Parcel.cpp +++ b/libs/binder/Parcel.cpp @@ -619,7 +619,10 @@ status_t Parcel::writeCString(const char* str) status_t Parcel::writeString8(const String8& str) { status_t err = writeInt32(str.bytes()); - if (err == NO_ERROR) { + // only write string if its length is more than zero characters, + // as readString8 will only read if the length field is non-zero. + // this is slightly different from how writeString16 works. + if (str.bytes() > 0 && err == NO_ERROR) { err = write(str.string(), str.bytes()+1); } return err; diff --git a/libs/gui/Android.mk b/libs/gui/Android.mk index 249558ac86..d1a6af1dc1 100644 --- a/libs/gui/Android.mk +++ b/libs/gui/Android.mk @@ -4,17 +4,25 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ ISensorEventConnection.cpp \ ISensorServer.cpp \ + ISurfaceTexture.cpp \ Sensor.cpp \ SensorChannel.cpp \ SensorEventQueue.cpp \ - SensorManager.cpp + SensorManager.cpp \ + SurfaceTexture.cpp \ + SurfaceTextureClient.cpp LOCAL_SHARED_LIBRARIES := \ libcutils \ libutils \ libbinder \ libhardware \ - libhardware_legacy + libhardware_legacy \ + libui \ + libEGL \ + libGLESv2 \ + libsurfaceflinger_client + LOCAL_MODULE:= libgui diff --git a/libs/gui/ISurfaceTexture.cpp b/libs/gui/ISurfaceTexture.cpp new file mode 100644 index 0000000000..90bca3ca4b --- /dev/null +++ b/libs/gui/ISurfaceTexture.cpp @@ -0,0 +1,204 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdint.h> +#include <sys/types.h> + +#include <utils/Errors.h> +#include <utils/RefBase.h> +#include <utils/Vector.h> +#include <utils/Timers.h> + +#include <binder/Parcel.h> +#include <binder/IInterface.h> + +#include <gui/ISurfaceTexture.h> + +namespace android { +// ---------------------------------------------------------------------------- + +enum { + REQUEST_BUFFER = IBinder::FIRST_CALL_TRANSACTION, + SET_BUFFER_COUNT, + DEQUEUE_BUFFER, + QUEUE_BUFFER, + CANCEL_BUFFER, + SET_CROP, + SET_TRANSFORM, +}; + + +class BpSurfaceTexture : public BpInterface<ISurfaceTexture> +{ +public: + BpSurfaceTexture(const sp<IBinder>& impl) + : BpInterface<ISurfaceTexture>(impl) + { + } + + virtual sp<GraphicBuffer> requestBuffer(int bufferIdx, + uint32_t w, uint32_t h, uint32_t format, uint32_t usage) { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor()); + data.writeInt32(bufferIdx); + data.writeInt32(w); + data.writeInt32(h); + data.writeInt32(format); + data.writeInt32(usage); + remote()->transact(REQUEST_BUFFER, data, &reply); + sp<GraphicBuffer> buffer; + bool nonNull = reply.readInt32(); + if (nonNull) { + buffer = new GraphicBuffer(); + reply.read(*buffer); + } + return buffer; + } + + virtual status_t setBufferCount(int bufferCount) + { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor()); + data.writeInt32(bufferCount); + remote()->transact(SET_BUFFER_COUNT, data, &reply); + status_t err = reply.readInt32(); + return err; + } + + virtual status_t dequeueBuffer(int *buf) { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor()); + remote()->transact(DEQUEUE_BUFFER, data, &reply); + *buf = reply.readInt32(); + int result = reply.readInt32(); + return result; + } + + virtual status_t queueBuffer(int buf) { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor()); + data.writeInt32(buf); + remote()->transact(QUEUE_BUFFER, data, &reply); + status_t result = reply.readInt32(); + return result; + } + + virtual void cancelBuffer(int buf) { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor()); + data.writeInt32(buf); + remote()->transact(CANCEL_BUFFER, data, &reply); + } + + virtual status_t setCrop(const Rect& reg) { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor()); + data.writeFloat(reg.left); + data.writeFloat(reg.top); + data.writeFloat(reg.right); + data.writeFloat(reg.bottom); + remote()->transact(SET_CROP, data, &reply); + status_t result = reply.readInt32(); + return result; + } + + virtual status_t setTransform(uint32_t transform) { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor()); + data.writeInt32(transform); + remote()->transact(SET_TRANSFORM, data, &reply); + status_t result = reply.readInt32(); + return result; + } +}; + +IMPLEMENT_META_INTERFACE(SurfaceTexture, "android.gui.SurfaceTexture"); + +// ---------------------------------------------------------------------- + +status_t BnSurfaceTexture::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) { + case REQUEST_BUFFER: { + CHECK_INTERFACE(ISurfaceTexture, data, reply); + int bufferIdx = data.readInt32(); + uint32_t w = data.readInt32(); + uint32_t h = data.readInt32(); + uint32_t format = data.readInt32(); + uint32_t usage = data.readInt32(); + sp<GraphicBuffer> buffer(requestBuffer(bufferIdx, w, h, format, + usage)); + reply->writeInt32(buffer != 0); + if (buffer != 0) { + reply->write(*buffer); + } + return NO_ERROR; + } break; + case SET_BUFFER_COUNT: { + CHECK_INTERFACE(ISurfaceTexture, data, reply); + int bufferCount = data.readInt32(); + int result = setBufferCount(bufferCount); + reply->writeInt32(result); + return NO_ERROR; + } break; + case DEQUEUE_BUFFER: { + CHECK_INTERFACE(ISurfaceTexture, data, reply); + int buf; + int result = dequeueBuffer(&buf); + reply->writeInt32(buf); + reply->writeInt32(result); + return NO_ERROR; + } break; + case QUEUE_BUFFER: { + CHECK_INTERFACE(ISurfaceTexture, data, reply); + int buf = data.readInt32(); + status_t result = queueBuffer(buf); + reply->writeInt32(result); + return NO_ERROR; + } break; + case CANCEL_BUFFER: { + CHECK_INTERFACE(ISurfaceTexture, data, reply); + int buf = data.readInt32(); + cancelBuffer(buf); + return NO_ERROR; + } break; + case SET_CROP: { + Rect reg; + CHECK_INTERFACE(ISurfaceTexture, data, reply); + reg.left = data.readFloat(); + reg.top = data.readFloat(); + reg.right = data.readFloat(); + reg.bottom = data.readFloat(); + status_t result = setCrop(reg); + reply->writeInt32(result); + return NO_ERROR; + } break; + case SET_TRANSFORM: { + Rect reg; + CHECK_INTERFACE(ISurfaceTexture, data, reply); + uint32_t transform = data.readInt32(); + status_t result = setTransform(transform); + reply->writeInt32(result); + return NO_ERROR; + } break; + } + return BBinder::onTransact(code, data, reply, flags); +} + +// ---------------------------------------------------------------------------- + +}; // namespace android diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp new file mode 100644 index 0000000000..1dadd53891 --- /dev/null +++ b/libs/gui/SurfaceTexture.cpp @@ -0,0 +1,373 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "SurfaceTexture" +//#define LOG_NDEBUG 0 + +#define GL_GLEXT_PROTOTYPES +#define EGL_EGLEXT_PROTOTYPES + +#include <EGL/egl.h> +#include <EGL/eglext.h> +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> + +#include <gui/SurfaceTexture.h> + +#include <surfaceflinger/ISurfaceComposer.h> +#include <surfaceflinger/SurfaceComposerClient.h> +#include <surfaceflinger/IGraphicBufferAlloc.h> + +#include <utils/Log.h> + +namespace android { + +// Transform matrices +static float mtxIdentity[16] = { + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1, +}; +static float mtxFlipH[16] = { + -1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 1, 0, 0, 1, +}; +static float mtxFlipV[16] = { + 1, 0, 0, 0, + 0, -1, 0, 0, + 0, 0, 1, 0, + 0, 1, 0, 1, +}; +static float mtxRot90[16] = { + 0, 1, 0, 0, + -1, 0, 0, 0, + 0, 0, 1, 0, + 1, 0, 0, 1, +}; +static float mtxRot180[16] = { + -1, 0, 0, 0, + 0, -1, 0, 0, + 0, 0, 1, 0, + 1, 1, 0, 1, +}; +static float mtxRot270[16] = { + 0, -1, 0, 0, + 1, 0, 0, 0, + 0, 0, 1, 0, + 0, 1, 0, 1, +}; + +static void mtxMul(float out[16], const float a[16], const float b[16]); + +SurfaceTexture::SurfaceTexture(GLuint tex) : + mBufferCount(MIN_BUFFER_SLOTS), mCurrentTexture(INVALID_BUFFER_SLOT), + mLastQueued(INVALID_BUFFER_SLOT), mTexName(tex) { + LOGV("SurfaceTexture::SurfaceTexture"); + for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { + mSlots[i].mEglImage = EGL_NO_IMAGE_KHR; + mSlots[i].mEglDisplay = EGL_NO_DISPLAY; + mSlots[i].mOwnedByClient = false; + } + sp<ISurfaceComposer> composer(ComposerService::getComposerService()); + mGraphicBufferAlloc = composer->createGraphicBufferAlloc(); +} + +SurfaceTexture::~SurfaceTexture() { + LOGV("SurfaceTexture::~SurfaceTexture"); + freeAllBuffers(); +} + +status_t SurfaceTexture::setBufferCount(int bufferCount) { + LOGV("SurfaceTexture::setBufferCount"); + Mutex::Autolock lock(mMutex); + freeAllBuffers(); + mBufferCount = bufferCount; + mCurrentTexture = INVALID_BUFFER_SLOT; + mLastQueued = INVALID_BUFFER_SLOT; + return OK; +} + +sp<GraphicBuffer> SurfaceTexture::requestBuffer(int buf, + uint32_t w, uint32_t h, uint32_t format, uint32_t usage) { + LOGV("SurfaceTexture::requestBuffer"); + Mutex::Autolock lock(mMutex); + if (buf < 0 || mBufferCount <= buf) { + LOGE("requestBuffer: slot index out of range [0, %d]: %d", + mBufferCount, buf); + return 0; + } + usage |= GraphicBuffer::USAGE_HW_TEXTURE; + sp<GraphicBuffer> graphicBuffer( + mGraphicBufferAlloc->createGraphicBuffer(w, h, format, usage)); + if (graphicBuffer == 0) { + LOGE("requestBuffer: SurfaceComposer::createGraphicBuffer failed"); + } else { + mSlots[buf].mGraphicBuffer = graphicBuffer; + if (mSlots[buf].mEglImage != EGL_NO_IMAGE_KHR) { + eglDestroyImageKHR(mSlots[buf].mEglDisplay, mSlots[buf].mEglImage); + mSlots[buf].mEglImage = EGL_NO_IMAGE_KHR; + mSlots[buf].mEglDisplay = EGL_NO_DISPLAY; + } + mAllocdBuffers.add(graphicBuffer); + } + return graphicBuffer; +} + +status_t SurfaceTexture::dequeueBuffer(int *buf) { + LOGV("SurfaceTexture::dequeueBuffer"); + Mutex::Autolock lock(mMutex); + int found = INVALID_BUFFER_SLOT; + for (int i = 0; i < mBufferCount; i++) { + if (!mSlots[i].mOwnedByClient && i != mCurrentTexture && i != mLastQueued) { + mSlots[i].mOwnedByClient = true; + found = i; + break; + } + } + if (found == INVALID_BUFFER_SLOT) { + return -EBUSY; + } + *buf = found; + return OK; +} + +status_t SurfaceTexture::queueBuffer(int buf) { + LOGV("SurfaceTexture::queueBuffer"); + Mutex::Autolock lock(mMutex); + if (buf < 0 || mBufferCount <= buf) { + LOGE("queueBuffer: slot index out of range [0, %d]: %d", + mBufferCount, buf); + return -EINVAL; + } else if (!mSlots[buf].mOwnedByClient) { + LOGE("queueBuffer: slot %d is not owned by the client", buf); + return -EINVAL; + } else if (mSlots[buf].mGraphicBuffer == 0) { + LOGE("queueBuffer: slot %d was enqueued without requesting a buffer", + buf); + return -EINVAL; + } + mSlots[buf].mOwnedByClient = false; + mLastQueued = buf; + mLastQueuedCrop = mNextCrop; + mLastQueuedTransform = mNextTransform; + if (mFrameAvailableListener != 0) { + mFrameAvailableListener->onFrameAvailable(); + } + return OK; +} + +void SurfaceTexture::cancelBuffer(int buf) { + LOGV("SurfaceTexture::cancelBuffer"); + Mutex::Autolock lock(mMutex); + if (buf < 0 || mBufferCount <= buf) { + LOGE("cancelBuffer: slot index out of range [0, %d]: %d", mBufferCount, + buf); + return; + } else if (!mSlots[buf].mOwnedByClient) { + LOGE("cancelBuffer: slot %d is not owned by the client", buf); + return; + } + mSlots[buf].mOwnedByClient = false; +} + +status_t SurfaceTexture::setCrop(const Rect& crop) { + LOGV("SurfaceTexture::setCrop"); + Mutex::Autolock lock(mMutex); + mNextCrop = crop; + return OK; +} + +status_t SurfaceTexture::setTransform(uint32_t transform) { + LOGV("SurfaceTexture::setTransform"); + Mutex::Autolock lock(mMutex); + mNextTransform = transform; + return OK; +} + +status_t SurfaceTexture::updateTexImage() { + LOGV("SurfaceTexture::updateTexImage"); + Mutex::Autolock lock(mMutex); + + // We always bind the texture even if we don't update its contents. + glBindTexture(GL_TEXTURE_EXTERNAL_OES, mTexName); + + // Initially both mCurrentTexture and mLastQueued are INVALID_BUFFER_SLOT, + // so this check will fail until a buffer gets queued. + if (mCurrentTexture != mLastQueued) { + // Update the GL texture object. + EGLImageKHR image = mSlots[mLastQueued].mEglImage; + if (image == EGL_NO_IMAGE_KHR) { + EGLDisplay dpy = eglGetCurrentDisplay(); + sp<GraphicBuffer> graphicBuffer = mSlots[mLastQueued].mGraphicBuffer; + image = createImage(dpy, graphicBuffer); + mSlots[mLastQueued].mEglImage = image; + mSlots[mLastQueued].mEglDisplay = dpy; + } + glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, (GLeglImageOES)image); + GLint error = glGetError(); + if (error != GL_NO_ERROR) { + LOGE("error binding external texture image %p (slot %d): %#04x", + image, mLastQueued, error); + return -EINVAL; + } + + // Update the SurfaceTexture state. + mCurrentTexture = mLastQueued; + mCurrentTextureBuf = mSlots[mCurrentTexture].mGraphicBuffer; + mCurrentCrop = mLastQueuedCrop; + mCurrentTransform = mLastQueuedTransform; + } + return OK; +} + +void SurfaceTexture::getTransformMatrix(float mtx[16]) { + LOGV("SurfaceTexture::updateTexImage"); + Mutex::Autolock lock(mMutex); + + float xform[16]; + for (int i = 0; i < 16; i++) { + xform[i] = mtxIdentity[i]; + } + if (mCurrentTransform & NATIVE_WINDOW_TRANSFORM_FLIP_H) { + float result[16]; + mtxMul(result, xform, mtxFlipH); + for (int i = 0; i < 16; i++) { + xform[i] = result[i]; + } + } + if (mCurrentTransform & NATIVE_WINDOW_TRANSFORM_FLIP_V) { + float result[16]; + mtxMul(result, xform, mtxFlipV); + for (int i = 0; i < 16; i++) { + xform[i] = result[i]; + } + } + if (mCurrentTransform & NATIVE_WINDOW_TRANSFORM_ROT_90) { + float result[16]; + mtxMul(result, xform, mtxRot90); + for (int i = 0; i < 16; i++) { + xform[i] = result[i]; + } + } + + sp<GraphicBuffer>& buf(mSlots[mCurrentTexture].mGraphicBuffer); + float tx, ty, sx, sy; + if (!mCurrentCrop.isEmpty()) { + tx = float(mCurrentCrop.left) / float(buf->getWidth()); + ty = float(buf->getHeight() - mCurrentCrop.bottom) / + float(buf->getHeight()); + sx = float(mCurrentCrop.width()) / float(buf->getWidth()); + sy = float(mCurrentCrop.height()) / float(buf->getHeight()); + } else { + tx = 0.0f; + ty = 0.0f; + sx = 1.0f; + sy = 1.0f; + } + float crop[16] = { + sx, 0, 0, 0, + 0, sy, 0, 0, + 0, 0, 1, 0, + sx*tx, sy*ty, 0, 1, + }; + + float mtxBeforeFlipV[16]; + mtxMul(mtxBeforeFlipV, crop, xform); + + // SurfaceFlinger expects the top of its window textures to be at a Y + // coordinate of 0, so SurfaceTexture must behave the same way. We don't + // want to expose this to applications, however, so we must add an + // additional vertical flip to the transform after all the other transforms. + mtxMul(mtx, mtxFlipV, mtxBeforeFlipV); +} + +void SurfaceTexture::setFrameAvailableListener( + const sp<FrameAvailableListener>& l) { + LOGV("SurfaceTexture::setFrameAvailableListener"); + Mutex::Autolock lock(mMutex); + mFrameAvailableListener = l; +} + +void SurfaceTexture::freeAllBuffers() { + for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { + mSlots[i].mGraphicBuffer = 0; + mSlots[i].mOwnedByClient = false; + if (mSlots[i].mEglImage != EGL_NO_IMAGE_KHR) { + eglDestroyImageKHR(mSlots[i].mEglDisplay, mSlots[i].mEglImage); + mSlots[i].mEglImage = EGL_NO_IMAGE_KHR; + mSlots[i].mEglDisplay = EGL_NO_DISPLAY; + } + } + + int exceptBuf = -1; + for (size_t i = 0; i < mAllocdBuffers.size(); i++) { + if (mAllocdBuffers[i] == mCurrentTextureBuf) { + exceptBuf = i; + break; + } + } + mAllocdBuffers.clear(); + if (exceptBuf >= 0) { + mAllocdBuffers.add(mCurrentTextureBuf); + } + mGraphicBufferAlloc->freeAllGraphicBuffersExcept(exceptBuf); +} + +EGLImageKHR SurfaceTexture::createImage(EGLDisplay dpy, + const sp<GraphicBuffer>& graphicBuffer) { + EGLClientBuffer cbuf = (EGLClientBuffer)graphicBuffer->getNativeBuffer(); + EGLint attrs[] = { + EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, + EGL_NONE, + }; + EGLImageKHR image = eglCreateImageKHR(dpy, EGL_NO_CONTEXT, + EGL_NATIVE_BUFFER_ANDROID, cbuf, attrs); + EGLint error = eglGetError(); + if (error != EGL_SUCCESS) { + LOGE("error creating EGLImage: %#x", error); + } else if (image == EGL_NO_IMAGE_KHR) { + LOGE("no error reported, but no image was returned by " + "eglCreateImageKHR"); + } + return image; +} + +static void mtxMul(float out[16], const float a[16], const float b[16]) { + out[0] = a[0]*b[0] + a[4]*b[1] + a[8]*b[2] + a[12]*b[3]; + out[1] = a[1]*b[0] + a[5]*b[1] + a[9]*b[2] + a[13]*b[3]; + out[2] = a[2]*b[0] + a[6]*b[1] + a[10]*b[2] + a[14]*b[3]; + out[3] = a[3]*b[0] + a[7]*b[1] + a[11]*b[2] + a[15]*b[3]; + + out[4] = a[0]*b[4] + a[4]*b[5] + a[8]*b[6] + a[12]*b[7]; + out[5] = a[1]*b[4] + a[5]*b[5] + a[9]*b[6] + a[13]*b[7]; + out[6] = a[2]*b[4] + a[6]*b[5] + a[10]*b[6] + a[14]*b[7]; + out[7] = a[3]*b[4] + a[7]*b[5] + a[11]*b[6] + a[15]*b[7]; + + out[8] = a[0]*b[8] + a[4]*b[9] + a[8]*b[10] + a[12]*b[11]; + out[9] = a[1]*b[8] + a[5]*b[9] + a[9]*b[10] + a[13]*b[11]; + out[10] = a[2]*b[8] + a[6]*b[9] + a[10]*b[10] + a[14]*b[11]; + out[11] = a[3]*b[8] + a[7]*b[9] + a[11]*b[10] + a[15]*b[11]; + + out[12] = a[0]*b[12] + a[4]*b[13] + a[8]*b[14] + a[12]*b[15]; + out[13] = a[1]*b[12] + a[5]*b[13] + a[9]*b[14] + a[13]*b[15]; + out[14] = a[2]*b[12] + a[6]*b[13] + a[10]*b[14] + a[14]*b[15]; + out[15] = a[3]*b[12] + a[7]*b[13] + a[11]*b[14] + a[15]*b[15]; +} + +}; // namespace android diff --git a/libs/gui/SurfaceTextureClient.cpp b/libs/gui/SurfaceTextureClient.cpp new file mode 100644 index 0000000000..0ed8be528b --- /dev/null +++ b/libs/gui/SurfaceTextureClient.cpp @@ -0,0 +1,300 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "SurfaceTextureClient" +//#define LOG_NDEBUG 0 + +#include <gui/SurfaceTextureClient.h> + +#include <utils/Log.h> + +namespace android { + +SurfaceTextureClient::SurfaceTextureClient( + const sp<ISurfaceTexture>& surfaceTexture): + mSurfaceTexture(surfaceTexture), mReqWidth(1), mReqHeight(1), + mReqFormat(DEFAULT_FORMAT), mReqUsage(0), mMutex() { + // Initialize the ANativeWindow function pointers. + ANativeWindow::setSwapInterval = setSwapInterval; + ANativeWindow::dequeueBuffer = dequeueBuffer; + ANativeWindow::cancelBuffer = cancelBuffer; + ANativeWindow::lockBuffer = lockBuffer; + ANativeWindow::queueBuffer = queueBuffer; + ANativeWindow::query = query; + ANativeWindow::perform = perform; +} + +int SurfaceTextureClient::setSwapInterval(ANativeWindow* window, int interval) { + SurfaceTextureClient* c = getSelf(window); + return c->setSwapInterval(interval); +} + +int SurfaceTextureClient::dequeueBuffer(ANativeWindow* window, + android_native_buffer_t** buffer) { + SurfaceTextureClient* c = getSelf(window); + return c->dequeueBuffer(buffer); +} + +int SurfaceTextureClient::cancelBuffer(ANativeWindow* window, + android_native_buffer_t* buffer) { + SurfaceTextureClient* c = getSelf(window); + return c->cancelBuffer(buffer); +} + +int SurfaceTextureClient::lockBuffer(ANativeWindow* window, + android_native_buffer_t* buffer) { + SurfaceTextureClient* c = getSelf(window); + return c->lockBuffer(buffer); +} + +int SurfaceTextureClient::queueBuffer(ANativeWindow* window, + android_native_buffer_t* buffer) { + SurfaceTextureClient* c = getSelf(window); + return c->queueBuffer(buffer); +} + +int SurfaceTextureClient::query(ANativeWindow* window, int what, int* value) { + SurfaceTextureClient* c = getSelf(window); + return c->query(what, value); +} + +int SurfaceTextureClient::perform(ANativeWindow* window, int operation, ...) { + va_list args; + va_start(args, operation); + SurfaceTextureClient* c = getSelf(window); + return c->perform(operation, args); +} + +int SurfaceTextureClient::setSwapInterval(int interval) { + return INVALID_OPERATION; +} + +int SurfaceTextureClient::dequeueBuffer(android_native_buffer_t** buffer) { + LOGV("SurfaceTextureClient::dequeueBuffer"); + Mutex::Autolock lock(mMutex); + int buf = -1; + status_t err = mSurfaceTexture->dequeueBuffer(&buf); + if (err < 0) { + LOGE("dequeueBuffer: ISurfaceTexture::dequeueBuffer failed: %d", err); + return err; + } + sp<GraphicBuffer>& gbuf(mSlots[buf]); + if (gbuf == 0 || gbuf->getWidth() != mReqWidth || + gbuf->getHeight() != mReqHeight || + uint32_t(gbuf->getPixelFormat()) != mReqFormat || + (gbuf->getUsage() & mReqUsage) != mReqUsage) { + gbuf = mSurfaceTexture->requestBuffer(buf, mReqWidth, mReqHeight, + mReqFormat, mReqUsage); + if (gbuf == 0) { + LOGE("dequeueBuffer: ISurfaceTexture::requestBuffer failed"); + return NO_MEMORY; + } + } + *buffer = gbuf.get(); + return OK; +} + +int SurfaceTextureClient::cancelBuffer(android_native_buffer_t* buffer) { + LOGV("SurfaceTextureClient::cancelBuffer"); + Mutex::Autolock lock(mMutex); + for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { + if (mSlots[i]->handle == buffer->handle) { + mSurfaceTexture->cancelBuffer(i); + return OK; + } + } + return BAD_VALUE; +} + +int SurfaceTextureClient::lockBuffer(android_native_buffer_t* buffer) { + LOGV("SurfaceTextureClient::lockBuffer"); + Mutex::Autolock lock(mMutex); + return OK; +} + +int SurfaceTextureClient::queueBuffer(android_native_buffer_t* buffer) { + LOGV("SurfaceTextureClient::queueBuffer"); + Mutex::Autolock lock(mMutex); + for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { + if (mSlots[i]->handle == buffer->handle) { + return mSurfaceTexture->queueBuffer(i); + } + } + LOGE("queueBuffer: unknown buffer queued"); + return BAD_VALUE; +} + +int SurfaceTextureClient::query(int what, int* value) { + LOGV("SurfaceTextureClient::query"); + Mutex::Autolock lock(mMutex); + // XXX: Implement this! + return INVALID_OPERATION; +} + +int SurfaceTextureClient::perform(int operation, va_list args) +{ + int res = NO_ERROR; + switch (operation) { + case NATIVE_WINDOW_CONNECT: + res = dispatchConnect(args); + break; + case NATIVE_WINDOW_DISCONNECT: + res = dispatchDisconnect(args); + break; + case NATIVE_WINDOW_SET_USAGE: + res = dispatchSetUsage(args); + break; + case NATIVE_WINDOW_SET_CROP: + res = dispatchSetCrop(args); + break; + case NATIVE_WINDOW_SET_BUFFER_COUNT: + res = dispatchSetBufferCount(args); + break; + case NATIVE_WINDOW_SET_BUFFERS_GEOMETRY: + res = dispatchSetBuffersGeometry(args); + break; + case NATIVE_WINDOW_SET_BUFFERS_TRANSFORM: + res = dispatchSetBuffersTransform(args); + break; + default: + res = NAME_NOT_FOUND; + break; + } + return res; +} + +int SurfaceTextureClient::dispatchConnect(va_list args) { + int api = va_arg(args, int); + return connect(api); +} + +int SurfaceTextureClient::dispatchDisconnect(va_list args) { + int api = va_arg(args, int); + return disconnect(api); +} + +int SurfaceTextureClient::dispatchSetUsage(va_list args) { + int usage = va_arg(args, int); + return setUsage(usage); +} + +int SurfaceTextureClient::dispatchSetCrop(va_list args) { + android_native_rect_t const* rect = va_arg(args, android_native_rect_t*); + return setCrop(reinterpret_cast<Rect const*>(rect)); +} + +int SurfaceTextureClient::dispatchSetBufferCount(va_list args) { + size_t bufferCount = va_arg(args, size_t); + return setBufferCount(bufferCount); +} + +int SurfaceTextureClient::dispatchSetBuffersGeometry(va_list args) { + int w = va_arg(args, int); + int h = va_arg(args, int); + int f = va_arg(args, int); + return setBuffersGeometry(w, h, f); +} + +int SurfaceTextureClient::dispatchSetBuffersTransform(va_list args) { + int transform = va_arg(args, int); + return setBuffersTransform(transform); +} + +int SurfaceTextureClient::connect(int api) { + LOGV("SurfaceTextureClient::connect"); + // XXX: Implement this! + return INVALID_OPERATION; +} + +int SurfaceTextureClient::disconnect(int api) { + LOGV("SurfaceTextureClient::disconnect"); + // XXX: Implement this! + return INVALID_OPERATION; +} + +int SurfaceTextureClient::setUsage(uint32_t reqUsage) +{ + LOGV("SurfaceTextureClient::setUsage"); + Mutex::Autolock lock(mMutex); + mReqUsage = reqUsage; + return OK; +} + +int SurfaceTextureClient::setCrop(Rect const* rect) +{ + LOGV("SurfaceTextureClient::setCrop"); + Mutex::Autolock lock(mMutex); + + // empty/invalid rects are not allowed + if (rect->isEmpty()) + return BAD_VALUE; + + status_t err = mSurfaceTexture->setCrop(*rect); + LOGE_IF(err, "ISurfaceTexture::setCrop(...) returned %s", + strerror(-err)); + + return err; +} + +int SurfaceTextureClient::setBufferCount(int bufferCount) +{ + LOGV("SurfaceTextureClient::setBufferCount"); + Mutex::Autolock lock(mMutex); + + status_t err = mSurfaceTexture->setBufferCount(bufferCount); + LOGE_IF(err, "ISurfaceTexture::setBufferCount(%d) returned %s", + bufferCount, strerror(-err)); + + if (err == NO_ERROR) { + freeAllBuffers(); + } + + return err; +} + +int SurfaceTextureClient::setBuffersGeometry(int w, int h, int format) +{ + LOGV("SurfaceTextureClient::setBuffersGeometry"); + Mutex::Autolock lock(mMutex); + + if (w<0 || h<0 || format<0) + return BAD_VALUE; + + if ((w && !h) || (!w && h)) + return BAD_VALUE; + + mReqWidth = w; + mReqHeight = h; + mReqFormat = format; + + return NO_ERROR; +} + +int SurfaceTextureClient::setBuffersTransform(int transform) +{ + LOGV("SurfaceTextureClient::setBuffersTransform"); + Mutex::Autolock lock(mMutex); + status_t err = mSurfaceTexture->setTransform(transform); + return err; +} + +void SurfaceTextureClient::freeAllBuffers() { + for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { + mSlots[i] = 0; + } +} + +}; // namespace android diff --git a/libs/surfaceflinger_client/Android.mk b/libs/surfaceflinger_client/Android.mk index ce3c71a7d9..4a0faf06a1 100644 --- a/libs/surfaceflinger_client/Android.mk +++ b/libs/surfaceflinger_client/Android.mk @@ -5,6 +5,7 @@ LOCAL_SRC_FILES:= \ ISurfaceComposer.cpp \ ISurface.cpp \ ISurfaceComposerClient.cpp \ + IGraphicBufferAlloc.cpp \ LayerState.cpp \ SharedBufferStack.cpp \ Surface.cpp \ diff --git a/libs/surfaceflinger_client/IGraphicBufferAlloc.cpp b/libs/surfaceflinger_client/IGraphicBufferAlloc.cpp new file mode 100644 index 0000000000..e05da725c4 --- /dev/null +++ b/libs/surfaceflinger_client/IGraphicBufferAlloc.cpp @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// tag as surfaceflinger +#define LOG_TAG "SurfaceFlinger" + +#include <stdint.h> +#include <sys/types.h> + +#include <binder/Parcel.h> + +#include <ui/GraphicBuffer.h> + +#include <surfaceflinger/IGraphicBufferAlloc.h> + +// --------------------------------------------------------------------------- + +namespace android { + +enum { + CREATE_GRAPHIC_BUFFER = IBinder::FIRST_CALL_TRANSACTION, + FREE_ALL_GRAPHIC_BUFFERS_EXCEPT, +}; + +class BpGraphicBufferAlloc : public BpInterface<IGraphicBufferAlloc> +{ +public: + BpGraphicBufferAlloc(const sp<IBinder>& impl) + : BpInterface<IGraphicBufferAlloc>(impl) + { + } + + virtual sp<GraphicBuffer> createGraphicBuffer(uint32_t w, uint32_t h, + PixelFormat format, uint32_t usage) { + Parcel data, reply; + data.writeInterfaceToken( + IGraphicBufferAlloc::getInterfaceDescriptor()); + data.writeInt32(w); + data.writeInt32(h); + data.writeInt32(format); + data.writeInt32(usage); + remote()->transact(CREATE_GRAPHIC_BUFFER, data, &reply); + sp<GraphicBuffer> graphicBuffer; + bool nonNull = (bool)reply.readInt32(); + if (nonNull) { + graphicBuffer = new GraphicBuffer(); + reply.read(*graphicBuffer); + } + return graphicBuffer; + } + + virtual void freeAllGraphicBuffersExcept(int bufIdx) { + Parcel data, reply; + data.writeInterfaceToken( + IGraphicBufferAlloc::getInterfaceDescriptor()); + data.writeInt32(bufIdx); + remote()->transact(FREE_ALL_GRAPHIC_BUFFERS_EXCEPT, data, &reply); + } +}; + +IMPLEMENT_META_INTERFACE(GraphicBufferAlloc, "android.ui.IGraphicBufferAlloc"); + +// ---------------------------------------------------------------------- + +status_t BnGraphicBufferAlloc::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + // codes that don't require permission check + + switch(code) { + case CREATE_GRAPHIC_BUFFER: { + CHECK_INTERFACE(IGraphicBufferAlloc, data, reply); + uint32_t w = data.readInt32(); + uint32_t h = data.readInt32(); + PixelFormat format = data.readInt32(); + uint32_t usage = data.readInt32(); + sp<GraphicBuffer> result(createGraphicBuffer(w, h, format, usage)); + reply->writeInt32(result != 0); + if (result != 0) { + reply->write(*result); + } + return NO_ERROR; + } break; + case FREE_ALL_GRAPHIC_BUFFERS_EXCEPT: { + CHECK_INTERFACE(IGraphicBufferAlloc, data, reply); + int bufIdx = data.readInt32(); + freeAllGraphicBuffersExcept(bufIdx); + return NO_ERROR; + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +}; // namespace android diff --git a/libs/surfaceflinger_client/ISurface.cpp b/libs/surfaceflinger_client/ISurface.cpp index 7049d9e42d..23b90af311 100644 --- a/libs/surfaceflinger_client/ISurface.cpp +++ b/libs/surfaceflinger_client/ISurface.cpp @@ -21,9 +21,7 @@ #include <sys/types.h> #include <binder/Parcel.h> -#include <binder/IMemory.h> -#include <ui/Overlay.h> #include <ui/GraphicBuffer.h> #include <surfaceflinger/Surface.h> @@ -33,36 +31,6 @@ namespace android { // ---------------------------------------------------------------------- -ISurface::BufferHeap::BufferHeap() - : w(0), h(0), hor_stride(0), ver_stride(0), format(0), - transform(0), flags(0) -{ -} - -ISurface::BufferHeap::BufferHeap(uint32_t w, uint32_t h, - int32_t hor_stride, int32_t ver_stride, - PixelFormat format, const sp<IMemoryHeap>& heap) - : w(w), h(h), hor_stride(hor_stride), ver_stride(ver_stride), - format(format), transform(0), flags(0), heap(heap) -{ -} - -ISurface::BufferHeap::BufferHeap(uint32_t w, uint32_t h, - int32_t hor_stride, int32_t ver_stride, - PixelFormat format, uint32_t transform, uint32_t flags, - const sp<IMemoryHeap>& heap) - : w(w), h(h), hor_stride(hor_stride), ver_stride(ver_stride), - format(format), transform(transform), flags(flags), heap(heap) -{ -} - - -ISurface::BufferHeap::~BufferHeap() -{ -} - -// ---------------------------------------------------------------------- - class BpSurface : public BpInterface<ISurface> { public: @@ -96,51 +64,6 @@ public: status_t err = reply.readInt32(); return err; } - - virtual status_t registerBuffers(const BufferHeap& buffers) - { - Parcel data, reply; - data.writeInterfaceToken(ISurface::getInterfaceDescriptor()); - data.writeInt32(buffers.w); - data.writeInt32(buffers.h); - data.writeInt32(buffers.hor_stride); - data.writeInt32(buffers.ver_stride); - data.writeInt32(buffers.format); - data.writeInt32(buffers.transform); - data.writeInt32(buffers.flags); - data.writeStrongBinder(buffers.heap->asBinder()); - remote()->transact(REGISTER_BUFFERS, data, &reply); - status_t result = reply.readInt32(); - return result; - } - - virtual void postBuffer(ssize_t offset) - { - Parcel data, reply; - data.writeInterfaceToken(ISurface::getInterfaceDescriptor()); - data.writeInt32(offset); - remote()->transact(POST_BUFFER, data, &reply, IBinder::FLAG_ONEWAY); - } - - virtual void unregisterBuffers() - { - Parcel data, reply; - data.writeInterfaceToken(ISurface::getInterfaceDescriptor()); - remote()->transact(UNREGISTER_BUFFERS, data, &reply); - } - - virtual sp<OverlayRef> createOverlay( - uint32_t w, uint32_t h, int32_t format, int32_t orientation) - { - Parcel data, reply; - data.writeInterfaceToken(ISurface::getInterfaceDescriptor()); - data.writeInt32(w); - data.writeInt32(h); - data.writeInt32(format); - data.writeInt32(orientation); - remote()->transact(CREATE_OVERLAY, data, &reply); - return OverlayRef::readFromParcel(reply); - } }; IMPLEMENT_META_INTERFACE(Surface, "android.ui.ISurface"); @@ -170,41 +93,6 @@ status_t BnSurface::onTransact( reply->writeInt32(err); return NO_ERROR; } - case REGISTER_BUFFERS: { - CHECK_INTERFACE(ISurface, data, reply); - BufferHeap buffer; - buffer.w = data.readInt32(); - buffer.h = data.readInt32(); - buffer.hor_stride = data.readInt32(); - buffer.ver_stride= data.readInt32(); - buffer.format = data.readInt32(); - buffer.transform = data.readInt32(); - buffer.flags = data.readInt32(); - buffer.heap = interface_cast<IMemoryHeap>(data.readStrongBinder()); - status_t err = registerBuffers(buffer); - reply->writeInt32(err); - return NO_ERROR; - } break; - case UNREGISTER_BUFFERS: { - CHECK_INTERFACE(ISurface, data, reply); - unregisterBuffers(); - return NO_ERROR; - } break; - case POST_BUFFER: { - CHECK_INTERFACE(ISurface, data, reply); - ssize_t offset = data.readInt32(); - postBuffer(offset); - return NO_ERROR; - } break; - case CREATE_OVERLAY: { - CHECK_INTERFACE(ISurface, data, reply); - int w = data.readInt32(); - int h = data.readInt32(); - int f = data.readInt32(); - int orientation = data.readInt32(); - sp<OverlayRef> o = createOverlay(w, h, f, orientation); - return OverlayRef::writeToParcel(reply, o); - } break; default: return BBinder::onTransact(code, data, reply, flags); } diff --git a/libs/surfaceflinger_client/ISurfaceComposer.cpp b/libs/surfaceflinger_client/ISurfaceComposer.cpp index a2a5455c4d..01ae23fe26 100644 --- a/libs/surfaceflinger_client/ISurfaceComposer.cpp +++ b/libs/surfaceflinger_client/ISurfaceComposer.cpp @@ -64,6 +64,15 @@ public: return interface_cast<ISurfaceComposerClient>(reply.readStrongBinder()); } + virtual sp<IGraphicBufferAlloc> createGraphicBufferAlloc() + { + uint32_t n; + Parcel data, reply; + data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + remote()->transact(BnSurfaceComposer::CREATE_GRAPHIC_BUFFER_ALLOC, data, &reply); + return interface_cast<IGraphicBufferAlloc>(reply.readStrongBinder()); + } + virtual sp<IMemoryHeap> getCblk() const { Parcel data, reply; @@ -127,13 +136,16 @@ public: virtual status_t captureScreen(DisplayID dpy, sp<IMemoryHeap>* heap, uint32_t* width, uint32_t* height, PixelFormat* format, - uint32_t reqWidth, uint32_t reqHeight) + uint32_t reqWidth, uint32_t reqHeight, + uint32_t minLayerZ, uint32_t maxLayerZ) { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); data.writeInt32(dpy); data.writeInt32(reqWidth); data.writeInt32(reqHeight); + data.writeInt32(minLayerZ); + data.writeInt32(maxLayerZ); remote()->transact(BnSurfaceComposer::CAPTURE_SCREEN, data, &reply); *heap = interface_cast<IMemoryHeap>(reply.readStrongBinder()); *width = reply.readInt32(); @@ -186,6 +198,11 @@ status_t BnSurfaceComposer::onTransact( sp<IBinder> b = createClientConnection()->asBinder(); reply->writeStrongBinder(b); } break; + case CREATE_GRAPHIC_BUFFER_ALLOC: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + sp<IBinder> b = createGraphicBufferAlloc()->asBinder(); + reply->writeStrongBinder(b); + } break; case OPEN_GLOBAL_TRANSACTION: { CHECK_INTERFACE(ISurfaceComposer, data, reply); openGlobalTransaction(); @@ -231,11 +248,13 @@ status_t BnSurfaceComposer::onTransact( DisplayID dpy = data.readInt32(); uint32_t reqWidth = data.readInt32(); uint32_t reqHeight = data.readInt32(); + uint32_t minLayerZ = data.readInt32(); + uint32_t maxLayerZ = data.readInt32(); sp<IMemoryHeap> heap; uint32_t w, h; PixelFormat f; status_t res = captureScreen(dpy, &heap, &w, &h, &f, - reqWidth, reqHeight); + reqWidth, reqHeight, minLayerZ, maxLayerZ); reply->writeStrongBinder(heap->asBinder()); reply->writeInt32(w); reply->writeInt32(h); diff --git a/libs/surfaceflinger_client/SharedBufferStack.cpp b/libs/surfaceflinger_client/SharedBufferStack.cpp index b45e43fb3d..7505d530e0 100644 --- a/libs/surfaceflinger_client/SharedBufferStack.cpp +++ b/libs/surfaceflinger_client/SharedBufferStack.cpp @@ -263,6 +263,14 @@ bool SharedBufferClient::LockCondition::operator()() const { return (buf != stack.index[stack.head]); } +SharedBufferServer::BuffersAvailableCondition::BuffersAvailableCondition( + SharedBufferServer* sbs, int numBuffers) : ConditionBase(sbs), + mNumBuffers(numBuffers) { +} +bool SharedBufferServer::BuffersAvailableCondition::operator()() const { + return stack.available == mNumBuffers; +} + // ---------------------------------------------------------------------------- SharedBufferClient::QueueUpdate::QueueUpdate(SharedBufferBase* sbb) @@ -358,11 +366,6 @@ ssize_t SharedBufferClient::dequeue() { SharedBufferStack& stack( *mSharedStack ); - if (stack.head == tail && stack.available == mNumBuffers) { - LOGW("dequeue: tail=%d, head=%d, avail=%d, queued=%d", - tail, stack.head, stack.available, stack.queued); - } - RWLock::AutoRLock _rd(mLock); const nsecs_t dequeueTime = systemTime(SYSTEM_TIME_THREAD); @@ -431,6 +434,7 @@ status_t SharedBufferClient::queue(int buf) const nsecs_t now = systemTime(SYSTEM_TIME_THREAD); stack.stats.totalTime = ns2us(now - mDequeueTime[buf]); + return err; } @@ -475,6 +479,7 @@ status_t SharedBufferClient::setBufferCount( if (err == NO_ERROR) { mNumBuffers = bufferCount; queued_head = (stack.head + stack.queued) % mNumBuffers; + tail = computeTail(); } return err; } @@ -582,17 +587,24 @@ uint32_t SharedBufferServer::getTransform(int buf) const */ status_t SharedBufferServer::resize(int newNumBuffers) { - if (uint32_t(newNumBuffers) >= SharedBufferStack::NUM_BUFFER_MAX) + if ((unsigned int)(newNumBuffers) < SharedBufferStack::NUM_BUFFER_MIN || + (unsigned int)(newNumBuffers) > SharedBufferStack::NUM_BUFFER_MAX) { return BAD_VALUE; + } RWLock::AutoWLock _l(mLock); - // for now we're not supporting shrinking - const int numBuffers = mNumBuffers; - if (newNumBuffers < numBuffers) - return BAD_VALUE; + if (newNumBuffers < mNumBuffers) { + return shrink(newNumBuffers); + } else { + return grow(newNumBuffers); + } +} +status_t SharedBufferServer::grow(int newNumBuffers) +{ SharedBufferStack& stack( *mSharedStack ); + const int numBuffers = mNumBuffers; const int extra = newNumBuffers - numBuffers; // read the head, make sure it's valid @@ -626,6 +638,48 @@ status_t SharedBufferServer::resize(int newNumBuffers) return NO_ERROR; } +status_t SharedBufferServer::shrink(int newNumBuffers) +{ + SharedBufferStack& stack( *mSharedStack ); + + // Shrinking is only supported if there are no buffers currently dequeued. + int32_t avail = stack.available; + int32_t queued = stack.queued; + if (avail + queued != mNumBuffers) { + return INVALID_OPERATION; + } + + // Wait for any queued buffers to be displayed. + BuffersAvailableCondition condition(this, mNumBuffers); + status_t err = waitForCondition(condition); + if (err < 0) { + return err; + } + + // Reset head to index 0 and make it refer to buffer 0. The same renaming + // (head -> 0) is done in the BufferManager. + int32_t head = stack.head; + int8_t* index = const_cast<int8_t*>(stack.index); + for (int8_t i = 0; i < newNumBuffers; i++) { + index[i] = i; + } + stack.head = 0; + stack.headBuf = 0; + + // Free the buffers from the end of the list that are no longer needed. + for (int i = newNumBuffers; i < mNumBuffers; i++) { + mBufferList.remove(i); + } + + // Tell the client to reallocate all the buffers. + reallocateAll(); + + mNumBuffers = newNumBuffers; + stack.available = newNumBuffers; + + return NO_ERROR; +} + SharedBufferStack::Statistics SharedBufferServer::getStats() const { SharedBufferStack& stack( *mSharedStack ); diff --git a/libs/surfaceflinger_client/Surface.cpp b/libs/surfaceflinger_client/Surface.cpp index 017e94c94f..e21bab7353 100644 --- a/libs/surfaceflinger_client/Surface.cpp +++ b/libs/surfaceflinger_client/Surface.cpp @@ -364,6 +364,13 @@ status_t Surface::writeToParcel( height = surface->mHeight; format = surface->mFormat; flags = surface->mFlags; + } else if (surface != 0 && surface->mSurface != 0) { + LOGW("Parceling invalid surface with non-NULL ISurface as NULL: " + "mSurface = %p, mIdentity = %d, mWidth = %d, mHeight = %d, " + "mFormat = %d, mFlags = 0x%08x, mInitCheck = %d", + surface->mSurface.get(), surface->mIdentity, surface->mWidth, + surface->mHeight, surface->mFormat, surface->mFlags, + surface->mInitCheck); } parcel->writeStrongBinder(sur!=0 ? sur->asBinder() : NULL); parcel->writeInt32(identity); @@ -438,21 +445,15 @@ void Surface::init() mSharedBufferClient = new SharedBufferClient( mClient.getSharedClient(), token, 2, mIdentity); mInitCheck = mClient.getSharedClient()->validate(token); + } else { + LOGW("Not initializing the shared buffer client because token = %d", + token); } } } Surface::~Surface() { - // this is a client-side operation, the surface is destroyed, unmap - // its buffers in this process. - size_t size = mBuffers.size(); - for (size_t i=0 ; i<size ; i++) { - if (mBuffers[i] != 0 && mBuffers[i]->handle != 0) { - getBufferMapper().unregisterBuffer(mBuffers[i]->handle); - } - } - // clear all references and trigger an IPC now, to make sure things // happen without delay, since these resources are quite heavy. mBuffers.clear(); @@ -465,7 +466,7 @@ bool Surface::isValid() { return mInitCheck == NO_ERROR; } -status_t Surface::validate() const +status_t Surface::validate(bool inCancelBuffer) const { // check that we initialized ourself properly if (mInitCheck != NO_ERROR) { @@ -475,27 +476,26 @@ status_t Surface::validate() const // verify the identity of this surface uint32_t identity = mSharedBufferClient->getIdentity(); - - // this is a bit of a (temporary) special case, identity==0 means that - // no operation are allowed from the client (eg: dequeue/queue), this - // is used with PUSH_BUFFER surfaces for instance - if (identity == 0) { - LOGE("[Surface] invalid operation (identity=%u)", mIdentity); - return INVALID_OPERATION; - } - if (mIdentity != identity) { LOGE("[Surface] using an invalid surface, " "identity=%u should be %d", mIdentity, identity); - return NO_INIT; + CallStack stack; + stack.update(); + stack.dump("Surface"); + return BAD_INDEX; } // check the surface didn't become invalid status_t err = mSharedBufferClient->getStatus(); if (err != NO_ERROR) { - LOGE("surface (identity=%u) is invalid, err=%d (%s)", - mIdentity, err, strerror(-err)); + if (!inCancelBuffer) { + LOGE("surface (identity=%u) is invalid, err=%d (%s)", + mIdentity, err, strerror(-err)); + CallStack stack; + stack.update(); + stack.dump("Surface"); + } return err; } @@ -626,12 +626,12 @@ int Surface::dequeueBuffer(android_native_buffer_t** buffer) int Surface::cancelBuffer(android_native_buffer_t* buffer) { - status_t err = validate(); + status_t err = validate(true); switch (err) { case NO_ERROR: // no error, common case break; - case INVALID_OPERATION: + case BAD_INDEX: // legitimate errors here return err; default: @@ -854,6 +854,12 @@ int Surface::setBufferCount(int bufferCount) status_t err = mSharedBufferClient->setBufferCount(bufferCount, ipc); LOGE_IF(err, "ISurface::setBufferCount(%d) returned %s", bufferCount, strerror(-err)); + + if (err == NO_ERROR) { + // Clear out any references to the old buffers. + mBuffers.clear(); + } + return err; } @@ -1022,7 +1028,20 @@ void Surface::setSwapRectangle(const Rect& r) { int Surface::getBufferIndex(const sp<GraphicBuffer>& buffer) const { - return buffer->getIndex(); + int idx = buffer->getIndex(); + if (idx < 0) { + // The buffer doesn't have an index set. See if the handle the same as + // one of the buffers for which we do know the index. This can happen + // e.g. if GraphicBuffer is used to wrap an android_native_buffer_t that + // was dequeued from an ANativeWindow. + for (size_t i = 0; i < mBuffers.size(); i++) { + if (buffer->handle == mBuffers[i]->handle) { + idx = mBuffers[i]->getIndex(); + break; + } + } + } + return idx; } status_t Surface::getBufferLocked(int index, @@ -1036,7 +1055,6 @@ status_t Surface::getBufferLocked(int index, // free the current buffer sp<GraphicBuffer>& currentBuffer(mBuffers.editItemAt(index)); if (currentBuffer != 0) { - getBufferMapper().unregisterBuffer(currentBuffer->handle); currentBuffer.clear(); } @@ -1044,7 +1062,7 @@ status_t Surface::getBufferLocked(int index, LOGE_IF(buffer==0, "ISurface::getBuffer(%d, %08x) returned NULL", index, usage); - if (buffer != 0) { // this should never happen by construction + if (buffer != 0) { // this should always happen by construction LOGE_IF(buffer->handle == NULL, "Surface (identity=%d) requestBuffer(%d, %u, %u, %u, %08x) " "returned a buffer with a null handle", @@ -1052,13 +1070,8 @@ status_t Surface::getBufferLocked(int index, err = mSharedBufferClient->getStatus(); LOGE_IF(err, "Surface (identity=%d) state = %d", mIdentity, err); if (!err && buffer->handle != NULL) { - err = getBufferMapper().registerBuffer(buffer->handle); - LOGW_IF(err, "registerBuffer(...) failed %d (%s)", - err, strerror(-err)); - if (err == NO_ERROR) { - currentBuffer = buffer; - currentBuffer->setIndex(index); - } + currentBuffer = buffer; + currentBuffer->setIndex(index); } else { err = err<0 ? err : status_t(NO_MEMORY); } diff --git a/libs/surfaceflinger_client/SurfaceComposerClient.cpp b/libs/surfaceflinger_client/SurfaceComposerClient.cpp index f270461607..d3367246a1 100644 --- a/libs/surfaceflinger_client/SurfaceComposerClient.cpp +++ b/libs/surfaceflinger_client/SurfaceComposerClient.cpp @@ -555,7 +555,8 @@ status_t ScreenshotClient::update() { if (s == NULL) return NO_INIT; mHeap = 0; return s->captureScreen(0, &mHeap, - &mWidth, &mHeight, &mFormat, 0, 0); + &mWidth, &mHeight, &mFormat, 0, 0, + 0, -1UL); } status_t ScreenshotClient::update(uint32_t reqWidth, uint32_t reqHeight) { @@ -563,7 +564,18 @@ status_t ScreenshotClient::update(uint32_t reqWidth, uint32_t reqHeight) { if (s == NULL) return NO_INIT; mHeap = 0; return s->captureScreen(0, &mHeap, - &mWidth, &mHeight, &mFormat, reqWidth, reqHeight); + &mWidth, &mHeight, &mFormat, reqWidth, reqHeight, + 0, -1UL); +} + +status_t ScreenshotClient::update(uint32_t reqWidth, uint32_t reqHeight, + uint32_t minLayerZ, uint32_t maxLayerZ) { + sp<ISurfaceComposer> s(ComposerService::getComposerService()); + if (s == NULL) return NO_INIT; + mHeap = 0; + return s->captureScreen(0, &mHeap, + &mWidth, &mHeight, &mFormat, reqWidth, reqHeight, + minLayerZ, maxLayerZ); } void ScreenshotClient::release() { diff --git a/libs/surfaceflinger_client/tests/SharedBufferStack/SharedBufferStackTest.cpp b/libs/surfaceflinger_client/tests/SharedBufferStack/SharedBufferStackTest.cpp index f409f48289..7ef59269ba 100644 --- a/libs/surfaceflinger_client/tests/SharedBufferStack/SharedBufferStackTest.cpp +++ b/libs/surfaceflinger_client/tests/SharedBufferStack/SharedBufferStackTest.cpp @@ -32,7 +32,8 @@ void test0(SharedBufferServer& s, SharedBufferClient& c, size_t num, int* list); int main(int argc, char** argv) { SharedClient client; - SharedBufferServer s(&client, 0, 4, 0); + sp<SharedBufferServer> ps(new SharedBufferServer(&client, 0, 4, 0)); + SharedBufferServer& s(*ps); SharedBufferClient c(&client, 0, 4, 0); printf("basic test 0\n"); @@ -67,6 +68,10 @@ int main(int argc, char** argv) int list3[6] = {3, 2, 1, 4, 5, 0}; test0(s, c, 6, list3); + c.setBufferCount(4, resize); + int list4[4] = {1, 2, 3, 0}; + test0(s, c, 4, list4); + return 0; } diff --git a/libs/ui/Android.mk b/libs/ui/Android.mk index c4a09d67d4..0d55f0861d 100644 --- a/libs/ui/Android.mk +++ b/libs/ui/Android.mk @@ -1,24 +1,55 @@ +# Copyright (C) 2010 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + LOCAL_PATH:= $(call my-dir) + +# libui is partially built for the host (used by build time keymap validation tool) +# These files are common to host and target builds. +commonSources:= \ + Input.cpp \ + Keyboard.cpp \ + KeyLayoutMap.cpp \ + KeyCharacterMap.cpp \ + VirtualKeyMap.cpp + +# For the host +# ===================================================== + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= $(commonSources) + +LOCAL_MODULE:= libui + +include $(BUILD_HOST_STATIC_LIBRARY) + + +# For the device +# ===================================================== + include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ + $(commonSources) \ EGLUtils.cpp \ - EventHub.cpp \ EventRecurrence.cpp \ FramebufferNativeWindow.cpp \ GraphicBuffer.cpp \ GraphicBufferAllocator.cpp \ GraphicBufferMapper.cpp \ GraphicLog.cpp \ - KeyLayoutMap.cpp \ - KeyCharacterMap.cpp \ - Input.cpp \ - InputDispatcher.cpp \ - InputManager.cpp \ - InputReader.cpp \ InputTransport.cpp \ - IOverlay.cpp \ - Overlay.cpp \ PixelFormat.cpp \ Rect.cpp \ Region.cpp @@ -27,7 +58,6 @@ LOCAL_SHARED_LIBRARIES := \ libcutils \ libutils \ libEGL \ - libbinder \ libpixelflinger \ libhardware \ libhardware_legacy diff --git a/libs/ui/EventHub.cpp b/libs/ui/EventHub.cpp deleted file mode 100644 index 41daa9ca0d..0000000000 --- a/libs/ui/EventHub.cpp +++ /dev/null @@ -1,1026 +0,0 @@ -// -// Copyright 2005 The Android Open Source Project -// -// Handle events, like key input and vsync. -// -// The goal is to provide an optimized solution for Linux, not an -// implementation that works well across all platforms. We expect -// events to arrive on file descriptors, so that we can use a select() -// select() call to sleep. -// -// We can't select() on anything but network sockets in Windows, so we -// provide an alternative implementation of waitEvent for that platform. -// -#define LOG_TAG "EventHub" - -//#define LOG_NDEBUG 0 - -#include <ui/EventHub.h> -#include <ui/KeycodeLabels.h> -#include <hardware_legacy/power.h> - -#include <cutils/properties.h> -#include <utils/Log.h> -#include <utils/Timers.h> -#include <utils/threads.h> -#include <utils/Errors.h> - -#include <stdlib.h> -#include <stdio.h> -#include <unistd.h> -#include <fcntl.h> -#include <memory.h> -#include <errno.h> -#include <assert.h> - -#include "KeyLayoutMap.h" - -#include <string.h> -#include <stdint.h> -#include <dirent.h> -#ifdef HAVE_INOTIFY -# include <sys/inotify.h> -#endif -#ifdef HAVE_ANDROID_OS -# include <sys/limits.h> /* not part of Linux */ -#endif -#include <sys/poll.h> -#include <sys/ioctl.h> - -/* this macro is used to tell if "bit" is set in "array" - * it selects a byte from the array, and does a boolean AND - * operation with a byte that only has the relevant bit set. - * eg. to check for the 12th bit, we do (array[1] & 1<<4) - */ -#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 - -#ifndef ABS_MT_TOUCH_MAJOR -#define ABS_MT_TOUCH_MAJOR 0x30 /* Major axis of touching ellipse */ -#endif - -#ifndef ABS_MT_POSITION_X -#define ABS_MT_POSITION_X 0x35 /* Center X ellipse position */ -#endif - -#ifndef ABS_MT_POSITION_Y -#define ABS_MT_POSITION_Y 0x36 /* Center Y ellipse position */ -#endif - -#define INDENT " " -#define INDENT2 " " -#define INDENT3 " " - -namespace android { - -static const char *WAKE_LOCK_ID = "KeyEvents"; -static const char *device_path = "/dev/input"; - -/* return the larger integer */ -static inline int max(int v1, int v2) -{ - return (v1 > v2) ? v1 : v2; -} - -static inline const char* toString(bool value) { - return value ? "true" : "false"; -} - -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()), fd(-1), next(NULL) { -} - -EventHub::device_t::~device_t() { - delete [] keyBitmask; - delete layoutMap; -} - -EventHub::EventHub(void) - : mError(NO_INIT), mHaveFirstKeyboard(false), mFirstKeyboardId(0) - , mDevicesById(0), mNumDevicesById(0) - , mOpeningDevices(0), mClosingDevices(0) - , mDevices(0), mFDs(0), mFDCount(0), mOpened(false), mNeedToSendFinishedDeviceScan(false) - , mInputBufferIndex(0), mInputBufferCount(0), mInputDeviceIndex(0) -{ - acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID); -#ifdef EV_SW - memset(mSwitches, 0, sizeof(mSwitches)); -#endif -} - -/* - * Clean up. - */ -EventHub::~EventHub(void) -{ - release_wake_lock(WAKE_LOCK_ID); - // we should free stuff here... -} - -status_t EventHub::errorCheck() const -{ - return mError; -} - -String8 EventHub::getDeviceName(int32_t deviceId) const -{ - AutoMutex _l(mLock); - device_t* device = getDeviceLocked(deviceId); - if (device == NULL) return String8(); - return device->name; -} - -uint32_t EventHub::getDeviceClasses(int32_t deviceId) const -{ - AutoMutex _l(mLock); - device_t* device = getDeviceLocked(deviceId); - if (device == NULL) return 0; - return device->classes; -} - -status_t EventHub::getAbsoluteAxisInfo(int32_t deviceId, int axis, - RawAbsoluteAxisInfo* outAxisInfo) const { - outAxisInfo->clear(); - - AutoMutex _l(mLock); - device_t* device = getDeviceLocked(deviceId); - if (device == NULL) return -1; - - struct input_absinfo info; - - 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; - } - - if (info.minimum != info.maximum) { - outAxisInfo->valid = true; - outAxisInfo->minValue = info.minimum; - outAxisInfo->maxValue = info.maximum; - outAxisInfo->flat = info.flat; - outAxisInfo->fuzz = info.fuzz; - } - return OK; -} - -int32_t EventHub::getScanCodeState(int32_t deviceId, int32_t scanCode) const { - if (scanCode >= 0 && scanCode <= KEY_MAX) { - AutoMutex _l(mLock); - - device_t* device = getDeviceLocked(deviceId); - if (device != NULL) { - return getScanCodeStateLocked(device, scanCode); - } - } - return AKEY_STATE_UNKNOWN; -} - -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; -} - -int32_t EventHub::getKeyCodeState(int32_t deviceId, int32_t keyCode) const { - AutoMutex _l(mLock); - - device_t* device = getDeviceLocked(deviceId); - if (device != NULL) { - return getKeyCodeStateLocked(device, keyCode); - } - return AKEY_STATE_UNKNOWN; -} - -int32_t EventHub::getKeyCodeStateLocked(device_t* device, int32_t keyCode) const { - Vector<int32_t> scanCodes; - device->layoutMap->findScancodes(keyCode, &scanCodes); - - 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) { - #if 0 - for (size_t i=0; i<=KEY_MAX; i++) { - LOGI("(Scan code %d: down=%d)", i, test_bit(i, key_bitmask)); - } - #endif - const size_t N = scanCodes.size(); - for (size_t i=0; i<N && i<=KEY_MAX; i++) { - 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 AKEY_STATE_DOWN; - } - } - return AKEY_STATE_UP; - } - 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 = getDeviceLocked(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 = getDeviceLocked(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, - int32_t* outKeycode, uint32_t* outFlags) const -{ - AutoMutex _l(mLock); - device_t* device = getDeviceLocked(deviceId); - - if (device != NULL && device->layoutMap != NULL) { - status_t err = device->layoutMap->map(scancode, outKeycode, outFlags); - if (err == NO_ERROR) { - return NO_ERROR; - } - } - - if (mHaveFirstKeyboard) { - device = getDeviceLocked(mFirstKeyboardId); - - if (device != NULL && device->layoutMap != NULL) { - status_t err = device->layoutMap->map(scancode, outKeycode, outFlags); - if (err == NO_ERROR) { - return NO_ERROR; - } - } - } - - *outKeycode = 0; - *outFlags = 0; - return NAME_NOT_FOUND; -} - -void EventHub::addExcludedDevice(const char* deviceName) -{ - AutoMutex _l(mLock); - - String8 name(deviceName); - mExcludedDevices.push_back(name); -} - -EventHub::device_t* EventHub::getDeviceLocked(int32_t deviceId) const -{ - if (deviceId == 0) deviceId = mFirstKeyboardId; - int32_t id = deviceId & ID_MASK; - if (id >= mNumDevicesById || id < 0) return NULL; - device_t* dev = mDevicesById[id].device; - if (dev == NULL) return NULL; - if (dev->id == deviceId) { - return dev; - } - return NULL; -} - -bool EventHub::getEvent(RawEvent* outEvent) -{ - 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. - - if (!mOpened) { - mError = openPlatformInput() ? NO_ERROR : UNKNOWN_ERROR; - mOpened = true; - mNeedToSendFinishedDeviceScan = true; - } - - 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; - if (device->id == mFirstKeyboardId) { - outEvent->deviceId = 0; - } else { - outEvent->deviceId = device->id; - } - outEvent->type = DEVICE_REMOVED; - outEvent->when = systemTime(SYSTEM_TIME_MONOTONIC); - delete device; - mNeedToSendFinishedDeviceScan = true; - 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; - if (device->id == mFirstKeyboardId) { - outEvent->deviceId = 0; - } else { - outEvent->deviceId = device->id; - } - outEvent->type = DEVICE_ADDED; - outEvent->when = systemTime(SYSTEM_TIME_MONOTONIC); - mNeedToSendFinishedDeviceScan = true; - return true; - } - - if (mNeedToSendFinishedDeviceScan) { - mNeedToSendFinishedDeviceScan = false; - outEvent->type = FINISHED_DEVICE_SCAN; - outEvent->when = systemTime(SYSTEM_TIME_MONOTONIC); - return true; - } - - // 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; - - // 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; - } - - // 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) { - break; - } - - 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; - } - } - } - -#if HAVE_INOTIFY - // readNotify() will modify mFDs and mFDCount, so this must be done after - // processing all other events. - if(mFDs[0].revents & POLLIN) { - readNotify(mFDs[0].fd); - mFDs[0].revents = 0; - continue; // report added or removed devices immediately - } -#endif - - mInputDeviceIndex = 0; - - // 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("poll failed (errno=%d)\n", errno); - usleep(100000); - } - } - } -} - -/* - * Open the platform-specific input device. - */ -bool EventHub::openPlatformInput(void) -{ - /* - * Open platform-specific input device(s). - */ - int res; - - mFDCount = 1; - 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(); - res = inotify_add_watch(mFDs[0].fd, device_path, IN_DELETE | IN_CREATE); - if(res < 0) { - LOGE("could not add watch for %s, %s\n", device_path, strerror(errno)); - } -#else - /* - * The code in EventHub::getEvent assumes that mFDs[0] is an inotify fd. - * We allocate space for it and set it to something invalid. - */ - mFDs[0].fd = -1; -#endif - - res = scanDir(device_path); - if(res < 0) { - LOGE("scan dir failed for %s\n", device_path); - } - - return true; -} - -// ---------------------------------------------------------------------------- - -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 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::openDevice(const char *deviceName) { - int version; - int fd; - struct pollfd *new_mFDs; - device_t **new_devices; - char **new_device_names; - char name[80]; - char location[80]; - char idstr[80]; - struct input_id id; - - LOGV("Opening device: %s", deviceName); - - AutoMutex _l(mLock); - - fd = open(deviceName, O_RDWR); - if(fd < 0) { - LOGE("could not open %s, %s\n", deviceName, strerror(errno)); - return -1; - } - - if(ioctl(fd, EVIOCGVERSION, &version)) { - LOGE("could not get driver version for %s, %s\n", deviceName, strerror(errno)); - return -1; - } - if(ioctl(fd, EVIOCGID, &id)) { - LOGE("could not get driver id for %s, %s\n", deviceName, strerror(errno)); - return -1; - } - name[sizeof(name) - 1] = '\0'; - location[sizeof(location) - 1] = '\0'; - idstr[sizeof(idstr) - 1] = '\0'; - if(ioctl(fd, EVIOCGNAME(sizeof(name) - 1), &name) < 1) { - //fprintf(stderr, "could not get device name for %s, %s\n", deviceName, strerror(errno)); - name[0] = '\0'; - } - - // check to see if the device is on our excluded list - List<String8>::iterator iter = mExcludedDevices.begin(); - List<String8>::iterator end = mExcludedDevices.end(); - for ( ; iter != end; iter++) { - const char* test = *iter; - if (strcmp(name, test) == 0) { - LOGI("ignoring event id %s driver %s\n", deviceName, test); - close(fd); - return -1; - } - } - - if(ioctl(fd, EVIOCGPHYS(sizeof(location) - 1), &location) < 1) { - //fprintf(stderr, "could not get location for %s, %s\n", deviceName, strerror(errno)); - location[0] = '\0'; - } - if(ioctl(fd, EVIOCGUNIQ(sizeof(idstr) - 1), &idstr) < 1) { - //fprintf(stderr, "could not get idstring for %s, %s\n", deviceName, strerror(errno)); - 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) { - break; - } - devid++; - } - if (devid >= mNumDevicesById) { - device_ent* new_devids = (device_ent*)realloc(mDevicesById, - sizeof(mDevicesById[0]) * (devid + 1)); - if (new_devids == NULL) { - LOGE("out of memory"); - return -1; - } - mDevicesById = new_devids; - mNumDevicesById = devid+1; - mDevicesById[devid].device = NULL; - mDevicesById[devid].seq = 0; - } - - mDevicesById[devid].seq = (mDevicesById[devid].seq+(1<<SEQ_SHIFT))&SEQ_MASK; - if (mDevicesById[devid].seq == 0) { - mDevicesById[devid].seq = 1<<SEQ_SHIFT; - } - - new_mFDs = (pollfd*)realloc(mFDs, sizeof(mFDs[0]) * (mFDCount + 1)); - new_devices = (device_t**)realloc(mDevices, sizeof(mDevices[0]) * (mFDCount + 1)); - if (new_mFDs == NULL || new_devices == NULL) { - LOGE("out of memory"); - return -1; - } - mFDs = new_mFDs; - mDevices = new_devices; - -#if 0 - LOGI("add device %d: %s\n", mFDCount, deviceName); - LOGI(" bus: %04x\n" - " vendor %04x\n" - " product %04x\n" - " version %04x\n", - id.bustype, id.vendor, id.product, id.version); - LOGI(" name: \"%s\"\n", name); - LOGI(" location: \"%s\"\n" - " id: \"%s\"\n", location, idstr); - LOGI(" version: %d.%d.%d\n", - version >> 16, (version >> 8) & 0xff, version & 0xff); -#endif - - device_t* device = new device_t(devid|mDevicesById[devid].seq, deviceName, name); - if (device == NULL) { - LOGE("out of memory"); - 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. - - 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 < sizeof(key_bitmask); i++) { - // LOGI("%d: 0x%02x\n", i, key_bitmask[i]); - //} - - // 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)); - } else { - delete device; - LOGE("out of memory allocating key bitmask"); - return -1; - } - } - } - - // See if this is a trackball (or mouse). - if (test_bit(BTN_MOUSE, key_bitmask)) { - 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 (test_bit(REL_X, rel_bitmask) && test_bit(REL_Y, rel_bitmask)) { - device->classes |= INPUT_DEVICE_CLASS_TRACKBALL; - } - } - } - - // 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..."); - 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[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 & INPUT_DEVICE_CLASS_KEYBOARD) != 0) { - char tmpfn[sizeof(name)]; - char keylayoutFilename[300]; - - // a more descriptive name - device->name = name; - - // replace all the spaces with underscores - strcpy(tmpfn, name); - for (char *p = strchr(tmpfn, ' '); p && *p; p = strchr(tmpfn, ' ')) - *p = '_'; - - // find the .kl file we need for this device - const char* root = getenv("ANDROID_ROOT"); - snprintf(keylayoutFilename, sizeof(keylayoutFilename), - "%s/usr/keylayout/%s.kl", root, tmpfn); - bool defaultKeymap = false; - if (access(keylayoutFilename, R_OK)) { - snprintf(keylayoutFilename, sizeof(keylayoutFilename), - "%s/usr/keylayout/%s", root, "qwerty.kl"); - defaultKeymap = true; - } - 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")) { - // the built-in keyboard has a well-known device ID of 0, - // this device better not go away. - mHaveFirstKeyboard = true; - mFirstKeyboardId = device->id; - property_set("hw.keyboards.0.devname", name); - } else { - // ensure mFirstKeyboardId is set to -something-. - if (mFirstKeyboardId == 0) { - mFirstKeyboardId = device->id; - } - } - char propName[100]; - sprintf(propName, "hw.keyboards.%u.devname", device->id); - property_set(propName, name); - - // 'Q' key support = cheap test of whether this is an alpha-capable kbd - if (hasKeycodeLocked(device, AKEYCODE_Q)) { - device->classes |= INPUT_DEVICE_CLASS_ALPHAKEY; - } - - // See if this device has a DPAD. - if (hasKeycodeLocked(device, AKEYCODE_DPAD_UP) && - hasKeycodeLocked(device, AKEYCODE_DPAD_DOWN) && - hasKeycodeLocked(device, AKEYCODE_DPAD_LEFT) && - hasKeycodeLocked(device, AKEYCODE_DPAD_RIGHT) && - hasKeycodeLocked(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)/sizeof(GAMEPAD_KEYCODES[0]); i++) { - if (hasKeycodeLocked(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); - - LOGV("Adding device %s %p at %d, id = %d, classes = 0x%x\n", - deviceName, device, mFDCount, devid, device->classes); - - mDevicesById[devid].device = device; - device->next = mOpeningDevices; - mOpeningDevices = device; - mDevices[mFDCount] = device; - - mFDCount++; - return 0; -} - -bool EventHub::hasKeycodeLocked(device_t* device, int keycode) const -{ - if (device->keyBitmask == NULL || device->layoutMap == NULL) { - return false; - } - - Vector<int32_t> scanCodes; - device->layoutMap->findScancodes(keycode, &scanCodes); - const size_t N = scanCodes.size(); - for (size_t i=0; i<N && i<=KEY_MAX; i++) { - int32_t sc = scanCodes.itemAt(i); - if (sc >= 0 && sc <= KEY_MAX && test_bit(sc, device->keyBitmask)) { - return true; - } - } - - return false; -} - -int EventHub::closeDevice(const char *deviceName) { - AutoMutex _l(mLock); - - int i; - for(i = 1; i < mFDCount; i++) { - if(strcmp(mDevices[i]->path.string(), deviceName) == 0) { - //LOGD("remove device %d: %s\n", i, deviceName); - device_t* device = mDevices[i]; - - LOGI("Removed device: path=%s name=%s id=0x%x (of 0x%x) index=%d fd=%d classes=0x%x\n", - device->path.string(), device->name.string(), device->id, - mNumDevicesById, mFDCount, mFDs[i].fd, device->classes); - - // Clear this device's entry. - int index = (device->id&ID_MASK); - mDevicesById[index].device = NULL; - - // Close the file descriptor and compact the fd array. - close(mFDs[i].fd); - int count = mFDCount - i - 1; - memmove(mDevices + i, mDevices + i + 1, sizeof(mDevices[0]) * count); - memmove(mFDs + i, mFDs + i + 1, sizeof(mFDs[0]) * count); - mFDCount--; - -#ifdef EV_SW - for (int j=0; j<EV_SW; j++) { - if (mSwitches[j] == device->id) { - mSwitches[j] = 0; - } - } -#endif - - device->next = mClosingDevices; - mClosingDevices = device; - - if (device->id == mFirstKeyboardId) { - LOGW("built-in keyboard device %s (id=%d) is closing! the apps will not like this", - device->path.string(), mFirstKeyboardId); - mFirstKeyboardId = 0; - property_set("hw.keyboards.0.devname", NULL); - } - // clear the property - char propName[100]; - sprintf(propName, "hw.keyboards.%u.devname", device->id); - property_set(propName, NULL); - return 0; - } - } - LOGE("remove device: %s not found\n", deviceName); - return -1; -} - -int EventHub::readNotify(int nfd) { -#ifdef HAVE_INOTIFY - int res; - char devname[PATH_MAX]; - char *filename; - char event_buf[512]; - int event_size; - int event_pos = 0; - struct inotify_event *event; - - LOGV("EventHub::readNotify nfd: %d\n", nfd); - res = read(nfd, event_buf, sizeof(event_buf)); - if(res < (int)sizeof(*event)) { - if(errno == EINTR) - return 0; - LOGW("could not get event, %s\n", strerror(errno)); - return 1; - } - //printf("got %d bytes of event information\n", res); - - strcpy(devname, device_path); - filename = devname + strlen(devname); - *filename++ = '/'; - - while(res >= (int)sizeof(*event)) { - event = (struct inotify_event *)(event_buf + event_pos); - //printf("%d: %08x \"%s\"\n", event->wd, event->mask, event->len ? event->name : ""); - if(event->len) { - strcpy(filename, event->name); - if(event->mask & IN_CREATE) { - openDevice(devname); - } - else { - closeDevice(devname); - } - } - event_size = sizeof(*event) + event->len; - res -= event_size; - event_pos += event_size; - } -#endif - return 0; -} - - -int EventHub::scanDir(const char *dirname) -{ - char devname[PATH_MAX]; - char *filename; - DIR *dir; - struct dirent *de; - dir = opendir(dirname); - if(dir == NULL) - return -1; - strcpy(devname, dirname); - filename = devname + strlen(devname); - *filename++ = '/'; - while((de = readdir(dir))) { - if(de->d_name[0] == '.' && - (de->d_name[1] == '\0' || - (de->d_name[1] == '.' && de->d_name[2] == '\0'))) - continue; - strcpy(filename, de->d_name); - openDevice(devname); - } - closedir(dir); - return 0; -} - -void EventHub::dump(String8& dump) { - dump.append("Event Hub State:\n"); - - { // acquire lock - AutoMutex _l(mLock); - - dump.appendFormat(INDENT "HaveFirstKeyboard: %s\n", toString(mHaveFirstKeyboard)); - dump.appendFormat(INDENT "FirstKeyboardId: 0x%x\n", mFirstKeyboardId); - - dump.append(INDENT "Devices:\n"); - - for (int i = 0; i < mNumDevicesById; i++) { - const device_t* device = mDevicesById[i].device; - if (device) { - if (mFirstKeyboardId == device->id) { - dump.appendFormat(INDENT2 "0x%x: %s (aka device 0 - first keyboard)\n", - device->id, device->name.string()); - } else { - dump.appendFormat(INDENT2 "0x%x: %s\n", device->id, device->name.string()); - } - dump.appendFormat(INDENT3 "Classes: 0x%08x\n", device->classes); - dump.appendFormat(INDENT3 "Path: %s\n", device->path.string()); - dump.appendFormat(INDENT3 "KeyLayoutFile: %s\n", device->keylayoutFilename.string()); - } - } - } // release lock -} - -}; // namespace android diff --git a/libs/ui/FramebufferNativeWindow.cpp b/libs/ui/FramebufferNativeWindow.cpp index a36d555f07..0702d49667 100644 --- a/libs/ui/FramebufferNativeWindow.cpp +++ b/libs/ui/FramebufferNativeWindow.cpp @@ -83,6 +83,7 @@ FramebufferNativeWindow::FramebufferNativeWindow() if (hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module) == 0) { int stride; int err; + int i; err = framebuffer_open(module, &fbDev); LOGE_IF(err, "couldn't open framebuffer HAL (%s)", strerror(-err)); @@ -96,27 +97,33 @@ FramebufferNativeWindow::FramebufferNativeWindow() mUpdateOnDemand = (fbDev->setUpdateRect != 0); // initialize the buffer FIFO - mNumBuffers = 2; - mNumFreeBuffers = 2; + mNumBuffers = NUM_FRAME_BUFFERS; + mNumFreeBuffers = NUM_FRAME_BUFFERS; mBufferHead = mNumBuffers-1; - buffers[0] = new NativeBuffer( - fbDev->width, fbDev->height, fbDev->format, GRALLOC_USAGE_HW_FB); - buffers[1] = new NativeBuffer( - fbDev->width, fbDev->height, fbDev->format, GRALLOC_USAGE_HW_FB); - - err = grDev->alloc(grDev, - fbDev->width, fbDev->height, fbDev->format, - GRALLOC_USAGE_HW_FB, &buffers[0]->handle, &buffers[0]->stride); - - LOGE_IF(err, "fb buffer 0 allocation failed w=%d, h=%d, err=%s", - fbDev->width, fbDev->height, strerror(-err)); - err = grDev->alloc(grDev, - fbDev->width, fbDev->height, fbDev->format, - GRALLOC_USAGE_HW_FB, &buffers[1]->handle, &buffers[1]->stride); - - LOGE_IF(err, "fb buffer 1 allocation failed w=%d, h=%d, err=%s", - fbDev->width, fbDev->height, strerror(-err)); + for (i = 0; i < mNumBuffers; i++) + { + buffers[i] = new NativeBuffer( + fbDev->width, fbDev->height, fbDev->format, GRALLOC_USAGE_HW_FB); + } + + for (i = 0; i < mNumBuffers; i++) + { + err = grDev->alloc(grDev, + fbDev->width, fbDev->height, fbDev->format, + GRALLOC_USAGE_HW_FB, &buffers[i]->handle, &buffers[i]->stride); + + LOGE_IF(err, "fb buffer %d allocation failed w=%d, h=%d, err=%s", + i, fbDev->width, fbDev->height, strerror(-err)); + + if (err) + { + mNumBuffers = i; + mNumFreeBuffers = i; + mBufferHead = mNumBuffers-1; + break; + } + } const_cast<uint32_t&>(ANativeWindow::flags) = fbDev->flags; const_cast<float&>(ANativeWindow::xdpi) = fbDev->xdpi; @@ -175,6 +182,16 @@ int FramebufferNativeWindow::setSwapInterval( return fb->setSwapInterval(fb, interval); } +void FramebufferNativeWindow::dump(String8& result) { + if (fbDev->common.version >= 1 && fbDev->dump) { + const size_t SIZE = 4096; + char buffer[SIZE]; + + fbDev->dump(fbDev, buffer, SIZE); + result.append(buffer); + } +} + // only for debugging / logging int FramebufferNativeWindow::getCurrentBufferIndex() const { diff --git a/libs/ui/GraphicBuffer.cpp b/libs/ui/GraphicBuffer.cpp index 3671954397..97312a6d4b 100644 --- a/libs/ui/GraphicBuffer.cpp +++ b/libs/ui/GraphicBuffer.cpp @@ -45,7 +45,6 @@ GraphicBuffer::GraphicBuffer() stride = format = usage = 0; - transform = 0; handle = NULL; } @@ -58,8 +57,7 @@ GraphicBuffer::GraphicBuffer(uint32_t w, uint32_t h, height = stride = format = - usage = - transform = 0; + usage = 0; handle = NULL; mInitCheck = initSize(w, h, reqFormat, reqUsage); } @@ -76,10 +74,22 @@ GraphicBuffer::GraphicBuffer(uint32_t w, uint32_t h, stride = inStride; format = inFormat; usage = inUsage; - transform = 0; handle = inHandle; } +GraphicBuffer::GraphicBuffer(android_native_buffer_t* buffer, bool keepOwnership) + : BASE(), mOwner(keepOwnership ? ownHandle : ownNone), + mBufferMapper(GraphicBufferMapper::get()), + mInitCheck(NO_ERROR), mIndex(-1), mWrappedBuffer(buffer) +{ + width = buffer->width; + height = buffer->height; + stride = buffer->stride; + format = buffer->format; + usage = buffer->usage; + handle = buffer->handle; +} + GraphicBuffer::~GraphicBuffer() { if (handle) { @@ -90,12 +100,14 @@ GraphicBuffer::~GraphicBuffer() void GraphicBuffer::free_handle() { if (mOwner == ownHandle) { + mBufferMapper.unregisterBuffer(handle); native_handle_close(handle); native_handle_delete(const_cast<native_handle*>(handle)); } else if (mOwner == ownData) { GraphicBufferAllocator& allocator(GraphicBufferAllocator::get()); allocator.free(handle); } + mWrappedBuffer = 0; } status_t GraphicBuffer::initCheck() const { @@ -185,10 +197,8 @@ status_t GraphicBuffer::lock(GGLSurface* sur, uint32_t usage) return res; } -const int kFlattenFdsOffset = 9; - size_t GraphicBuffer::getFlattenedSize() const { - return (kFlattenFdsOffset + (handle ? handle->numInts : 0))*sizeof(int); + return (8 + (handle ? handle->numInts : 0))*sizeof(int); } size_t GraphicBuffer::getFdCount() const { @@ -213,14 +223,13 @@ status_t GraphicBuffer::flatten(void* buffer, size_t size, buf[5] = usage; buf[6] = 0; buf[7] = 0; - buf[8] = transform; if (handle) { buf[6] = handle->numFds; buf[7] = handle->numInts; native_handle_t const* const h = handle; memcpy(fds, h->data, h->numFds*sizeof(int)); - memcpy(&buf[kFlattenFdsOffset], h->data + h->numFds, h->numInts*sizeof(int)); + memcpy(&buf[8], h->data + h->numFds, h->numInts*sizeof(int)); } return NO_ERROR; @@ -229,7 +238,7 @@ status_t GraphicBuffer::flatten(void* buffer, size_t size, status_t GraphicBuffer::unflatten(void const* buffer, size_t size, int fds[], size_t count) { - if (size < kFlattenFdsOffset*sizeof(int)) return NO_MEMORY; + if (size < 8*sizeof(int)) return NO_MEMORY; int const* buf = static_cast<int const*>(buffer); if (buf[0] != 'GBFR') return BAD_TYPE; @@ -237,7 +246,7 @@ status_t GraphicBuffer::unflatten(void const* buffer, size_t size, const size_t numFds = buf[6]; const size_t numInts = buf[7]; - const size_t sizeNeeded = (kFlattenFdsOffset + numInts) * sizeof(int); + const size_t sizeNeeded = (8 + numInts) * sizeof(int); if (size < sizeNeeded) return NO_MEMORY; size_t fdCountNeeded = 0; @@ -254,10 +263,9 @@ status_t GraphicBuffer::unflatten(void const* buffer, size_t size, stride = buf[3]; format = buf[4]; usage = buf[5]; - transform = buf[8]; native_handle* h = native_handle_create(numFds, numInts); memcpy(h->data, fds, numFds*sizeof(int)); - memcpy(h->data + numFds, &buf[kFlattenFdsOffset], numInts*sizeof(int)); + memcpy(h->data + numFds, &buf[8], numInts*sizeof(int)); handle = h; } else { width = height = stride = format = usage = 0; @@ -265,6 +273,11 @@ status_t GraphicBuffer::unflatten(void const* buffer, size_t size, } mOwner = ownHandle; + + if (handle != 0) { + mBufferMapper.registerBuffer(handle); + } + return NO_ERROR; } diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp index 9847a5fab7..33ef1fc894 100644 --- a/libs/ui/GraphicBufferAllocator.cpp +++ b/libs/ui/GraphicBufferAllocator.cpp @@ -24,8 +24,6 @@ #include <ui/GraphicBufferAllocator.h> -#include <private/ui/sw_gralloc_handle.h> - namespace android { // --------------------------------------------------------------------------- @@ -56,7 +54,7 @@ void GraphicBufferAllocator::dump(String8& result) const Mutex::Autolock _l(sLock); KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList); size_t total = 0; - const size_t SIZE = 512; + const size_t SIZE = 4096; char buffer[SIZE]; snprintf(buffer, SIZE, "Allocated buffers:\n"); result.append(buffer); @@ -71,6 +69,10 @@ void GraphicBufferAllocator::dump(String8& result) const } snprintf(buffer, SIZE, "Total allocated: %.2f KB\n", total/1024.0f); result.append(buffer); + if (mAllocDev->common.version >= 1 && mAllocDev->dump) { + mAllocDev->dump(mAllocDev, buffer, SIZE); + result.append(buffer); + } } void GraphicBufferAllocator::dumpToSystemLog() @@ -91,11 +93,7 @@ status_t GraphicBufferAllocator::alloc(uint32_t w, uint32_t h, PixelFormat forma // we have a h/w allocator and h/w buffer is requested status_t err; - if (usage & GRALLOC_USAGE_HW_MASK) { - err = mAllocDev->alloc(mAllocDev, w, h, format, usage, handle, stride); - } else { - err = sw_gralloc_handle_t::alloc(w, h, format, usage, handle, stride); - } + err = mAllocDev->alloc(mAllocDev, w, h, format, usage, handle, stride); LOGW_IF(err, "alloc(%u, %u, %d, %08x, ...) failed %d (%s)", w, h, format, usage, err, strerror(-err)); @@ -119,11 +117,8 @@ status_t GraphicBufferAllocator::alloc(uint32_t w, uint32_t h, PixelFormat forma status_t GraphicBufferAllocator::free(buffer_handle_t handle) { status_t err; - if (sw_gralloc_handle_t::validate(handle) < 0) { - err = mAllocDev->free(mAllocDev, handle); - } else { - err = sw_gralloc_handle_t::free((sw_gralloc_handle_t*)handle); - } + + err = mAllocDev->free(mAllocDev, handle); LOGW_IF(err, "free(...) failed %d (%s)", err, strerror(-err)); if (err == NO_ERROR) { diff --git a/libs/ui/GraphicBufferMapper.cpp b/libs/ui/GraphicBufferMapper.cpp index ce2acd01df..07c067474d 100644 --- a/libs/ui/GraphicBufferMapper.cpp +++ b/libs/ui/GraphicBufferMapper.cpp @@ -17,15 +17,7 @@ #define LOG_TAG "GraphicBufferMapper" #include <stdint.h> -#ifdef HAVE_ANDROID_OS // just want PAGE_SIZE define -# include <asm/page.h> -#else -# include <sys/user.h> -#endif #include <errno.h> -#include <sys/mman.h> - -#include <cutils/ashmem.h> #include <utils/Errors.h> #include <utils/Log.h> @@ -35,8 +27,6 @@ #include <hardware/gralloc.h> -#include <private/ui/sw_gralloc_handle.h> - namespace android { // --------------------------------------------------------------------------- @@ -57,11 +47,9 @@ GraphicBufferMapper::GraphicBufferMapper() status_t GraphicBufferMapper::registerBuffer(buffer_handle_t handle) { status_t err; - if (sw_gralloc_handle_t::validate(handle) < 0) { - err = mAllocMod->registerBuffer(mAllocMod, handle); - } else { - err = sw_gralloc_handle_t::registerBuffer((sw_gralloc_handle_t*)handle); - } + + err = mAllocMod->registerBuffer(mAllocMod, handle); + LOGW_IF(err, "registerBuffer(%p) failed %d (%s)", handle, err, strerror(-err)); return err; @@ -70,11 +58,9 @@ status_t GraphicBufferMapper::registerBuffer(buffer_handle_t handle) status_t GraphicBufferMapper::unregisterBuffer(buffer_handle_t handle) { status_t err; - if (sw_gralloc_handle_t::validate(handle) < 0) { - err = mAllocMod->unregisterBuffer(mAllocMod, handle); - } else { - err = sw_gralloc_handle_t::unregisterBuffer((sw_gralloc_handle_t*)handle); - } + + err = mAllocMod->unregisterBuffer(mAllocMod, handle); + LOGW_IF(err, "unregisterBuffer(%p) failed %d (%s)", handle, err, strerror(-err)); return err; @@ -84,15 +70,11 @@ status_t GraphicBufferMapper::lock(buffer_handle_t handle, int usage, const Rect& bounds, void** vaddr) { status_t err; - if (sw_gralloc_handle_t::validate(handle) < 0) { - err = mAllocMod->lock(mAllocMod, handle, usage, - bounds.left, bounds.top, bounds.width(), bounds.height(), - vaddr); - } else { - err = sw_gralloc_handle_t::lock((sw_gralloc_handle_t*)handle, usage, - bounds.left, bounds.top, bounds.width(), bounds.height(), - vaddr); - } + + err = mAllocMod->lock(mAllocMod, handle, usage, + bounds.left, bounds.top, bounds.width(), bounds.height(), + vaddr); + LOGW_IF(err, "lock(...) failed %d (%s)", err, strerror(-err)); return err; } @@ -100,128 +82,11 @@ status_t GraphicBufferMapper::lock(buffer_handle_t handle, status_t GraphicBufferMapper::unlock(buffer_handle_t handle) { status_t err; - if (sw_gralloc_handle_t::validate(handle) < 0) { - err = mAllocMod->unlock(mAllocMod, handle); - } else { - err = sw_gralloc_handle_t::unlock((sw_gralloc_handle_t*)handle); - } - LOGW_IF(err, "unlock(...) failed %d (%s)", err, strerror(-err)); - return err; -} - -// --------------------------------------------------------------------------- - -status_t sw_gralloc_handle_t::alloc(uint32_t w, uint32_t h, int format, - int usage, buffer_handle_t* pHandle, int32_t* pStride) -{ - int align = 4; - int bpp = 0; - switch (format) { - case HAL_PIXEL_FORMAT_RGBA_8888: - case HAL_PIXEL_FORMAT_RGBX_8888: - case HAL_PIXEL_FORMAT_BGRA_8888: - bpp = 4; - break; - case HAL_PIXEL_FORMAT_RGB_888: - bpp = 3; - break; - case HAL_PIXEL_FORMAT_RGB_565: - case HAL_PIXEL_FORMAT_RGBA_5551: - case HAL_PIXEL_FORMAT_RGBA_4444: - bpp = 2; - break; - default: - return -EINVAL; - } - size_t bpr = (w*bpp + (align-1)) & ~(align-1); - size_t size = bpr * h; - size_t stride = bpr / bpp; - size = (size + (PAGE_SIZE-1)) & ~(PAGE_SIZE-1); - - int fd = ashmem_create_region("sw-gralloc-buffer", size); - if (fd < 0) { - LOGE("ashmem_create_region(size=%d) failed (%s)", - size, strerror(-errno)); - return -errno; - } - - int prot = PROT_READ; - if (usage & GRALLOC_USAGE_SW_WRITE_MASK) - prot |= PROT_WRITE; - - if (ashmem_set_prot_region(fd, prot) < 0) { - LOGE("ashmem_set_prot_region(fd=%d, prot=%x) failed (%s)", - fd, prot, strerror(-errno)); - close(fd); - return -errno; - } - void* base = mmap(0, size, prot, MAP_SHARED, fd, 0); - if (base == MAP_FAILED) { - LOGE("alloc mmap(fd=%d, size=%d, prot=%x) failed (%s)", - fd, size, prot, strerror(-errno)); - close(fd); - return -errno; - } - - sw_gralloc_handle_t* hnd = new sw_gralloc_handle_t(); - hnd->fd = fd; - hnd->size = size; - hnd->base = intptr_t(base); - hnd->prot = prot; - *pStride = stride; - *pHandle = hnd; - - return NO_ERROR; -} - -status_t sw_gralloc_handle_t::free(sw_gralloc_handle_t* hnd) -{ - if (hnd->base) { - munmap((void*)hnd->base, hnd->size); - } - if (hnd->fd >= 0) { - close(hnd->fd); - } - delete hnd; - return NO_ERROR; -} - -status_t sw_gralloc_handle_t::registerBuffer(sw_gralloc_handle_t* hnd) -{ - if (hnd->pid != getpid()) { - void* base = mmap(0, hnd->size, hnd->prot, MAP_SHARED, hnd->fd, 0); - if (base == MAP_FAILED) { - LOGE("registerBuffer mmap(fd=%d, size=%d, prot=%x) failed (%s)", - hnd->fd, hnd->size, hnd->prot, strerror(-errno)); - return -errno; - } - hnd->base = intptr_t(base); - } - return NO_ERROR; -} - -status_t sw_gralloc_handle_t::unregisterBuffer(sw_gralloc_handle_t* hnd) -{ - if (hnd->pid != getpid()) { - if (hnd->base) { - munmap((void*)hnd->base, hnd->size); - } - hnd->base = 0; - } - return NO_ERROR; -} + err = mAllocMod->unlock(mAllocMod, handle); -status_t sw_gralloc_handle_t::lock(sw_gralloc_handle_t* hnd, int usage, - int l, int t, int w, int h, void** vaddr) -{ - *vaddr = (void*)hnd->base; - return NO_ERROR; -} - -status_t sw_gralloc_handle_t::unlock(sw_gralloc_handle_t* hnd) -{ - return NO_ERROR; + LOGW_IF(err, "unlock(...) failed %d (%s)", err, strerror(-err)); + return err; } // --------------------------------------------------------------------------- diff --git a/libs/ui/IOverlay.cpp b/libs/ui/IOverlay.cpp deleted file mode 100644 index 65e6b4f376..0000000000 --- a/libs/ui/IOverlay.cpp +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <stdio.h> -#include <stdint.h> -#include <sys/types.h> - -#include <binder/Parcel.h> -#include <binder/IInterface.h> - -#include <ui/IOverlay.h> - -namespace android { - -enum { - DESTROY = IBinder::FIRST_CALL_TRANSACTION, // one-way transaction -}; - -class BpOverlay : public BpInterface<IOverlay> -{ -public: - BpOverlay(const sp<IBinder>& impl) - : BpInterface<IOverlay>(impl) - { - } - - virtual void destroy() - { - Parcel data, reply; - data.writeInterfaceToken(IOverlay::getInterfaceDescriptor()); - remote()->transact(DESTROY, data, &reply, IBinder::FLAG_ONEWAY); - } -}; - -IMPLEMENT_META_INTERFACE(Overlay, "android.ui.IOverlay"); - -// ---------------------------------------------------------------------- - -status_t BnOverlay::onTransact( - uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) -{ - switch(code) { - case DESTROY: { - CHECK_INTERFACE(IOverlay, data, reply); - destroy(); - return NO_ERROR; - } break; - default: - return BBinder::onTransact(code, data, reply, flags); - } -} - -}; // namespace android diff --git a/libs/ui/Input.cpp b/libs/ui/Input.cpp index 811edaf8a2..b8d59e68e6 100644 --- a/libs/ui/Input.cpp +++ b/libs/ui/Input.cpp @@ -7,11 +7,117 @@ //#define LOG_NDEBUG 0 +#define DEBUG_PROBE 0 + +#include <stdlib.h> +#include <unistd.h> +#include <ctype.h> + #include <ui/Input.h> namespace android { -// class InputEvent +static const char* CONFIGURATION_FILE_DIR[] = { + "idc/", + "keylayout/", + "keychars/", +}; + +static const char* CONFIGURATION_FILE_EXTENSION[] = { + ".idc", + ".kl", + ".kcm", +}; + +static bool isValidNameChar(char ch) { + return isascii(ch) && (isdigit(ch) || isalpha(ch) || ch == '-' || ch == '_'); +} + +static void appendInputDeviceConfigurationFileRelativePath(String8& path, + const String8& name, InputDeviceConfigurationFileType type) { + path.append(CONFIGURATION_FILE_DIR[type]); + for (size_t i = 0; i < name.length(); i++) { + char ch = name[i]; + if (!isValidNameChar(ch)) { + ch = '_'; + } + path.append(&ch, 1); + } + path.append(CONFIGURATION_FILE_EXTENSION[type]); +} + +String8 getInputDeviceConfigurationFilePathByDeviceIdentifier( + const InputDeviceIdentifier& deviceIdentifier, + InputDeviceConfigurationFileType type) { + if (deviceIdentifier.vendor !=0 && deviceIdentifier.product != 0) { + if (deviceIdentifier.version != 0) { + // Try vendor product version. + String8 versionPath(getInputDeviceConfigurationFilePathByName( + String8::format("Vendor_%04x_Product_%04x_Version_%04x", + deviceIdentifier.vendor, deviceIdentifier.product, + deviceIdentifier.version), + type)); + if (!versionPath.isEmpty()) { + return versionPath; + } + } + + // Try vendor product. + String8 productPath(getInputDeviceConfigurationFilePathByName( + String8::format("Vendor_%04x_Product_%04x", + deviceIdentifier.vendor, deviceIdentifier.product), + type)); + if (!productPath.isEmpty()) { + return productPath; + } + } + + // Try device name. + return getInputDeviceConfigurationFilePathByName(deviceIdentifier.name, type); +} + +String8 getInputDeviceConfigurationFilePathByName( + const String8& name, InputDeviceConfigurationFileType type) { + // Search system repository. + String8 path; + path.setTo(getenv("ANDROID_ROOT")); + path.append("/usr/"); + appendInputDeviceConfigurationFileRelativePath(path, name, type); +#if DEBUG_PROBE + LOGD("Probing for system provided input device configuration file: path='%s'", path.string()); +#endif + if (!access(path.string(), R_OK)) { +#if DEBUG_PROBE + LOGD("Found"); +#endif + return path; + } + + // Search user repository. + // TODO Should only look here if not in safe mode. + path.setTo(getenv("ANDROID_DATA")); + path.append("/system/devices/"); + appendInputDeviceConfigurationFileRelativePath(path, name, type); +#if DEBUG_PROBE + LOGD("Probing for system user input device configuration file: path='%s'", path.string()); +#endif + if (!access(path.string(), R_OK)) { +#if DEBUG_PROBE + LOGD("Found"); +#endif + return path; + } + + // Not found. +#if DEBUG_PROBE + LOGD("Probe failed to find input device configuration file: name='%s', type=%d", + name.string(), type); +#endif + return String8(); +} + + +// --- InputEvent --- void InputEvent::initialize(int32_t deviceId, int32_t source) { mDeviceId = deviceId; @@ -23,7 +129,7 @@ void InputEvent::initialize(const InputEvent& from) { mSource = from.mSource; } -// class KeyEvent +// --- KeyEvent --- bool KeyEvent::hasDefaultAction(int32_t keyCode) { switch (keyCode) { @@ -33,6 +139,7 @@ bool KeyEvent::hasDefaultAction(int32_t keyCode) { case AKEYCODE_ENDCALL: case AKEYCODE_VOLUME_UP: case AKEYCODE_VOLUME_DOWN: + case AKEYCODE_VOLUME_MUTE: case AKEYCODE_POWER: case AKEYCODE_CAMERA: case AKEYCODE_HEADSETHOOK: @@ -40,11 +147,14 @@ bool KeyEvent::hasDefaultAction(int32_t keyCode) { case AKEYCODE_NOTIFICATION: case AKEYCODE_FOCUS: case AKEYCODE_SEARCH: + case AKEYCODE_MEDIA_PLAY: + case AKEYCODE_MEDIA_PAUSE: case AKEYCODE_MEDIA_PLAY_PAUSE: case AKEYCODE_MEDIA_STOP: case AKEYCODE_MEDIA_NEXT: case AKEYCODE_MEDIA_PREVIOUS: case AKEYCODE_MEDIA_REWIND: + case AKEYCODE_MEDIA_RECORD: case AKEYCODE_MEDIA_FAST_FORWARD: case AKEYCODE_MUTE: return true; @@ -67,14 +177,18 @@ bool KeyEvent::isSystemKey(int32_t keyCode) { case AKEYCODE_ENDCALL: case AKEYCODE_VOLUME_UP: case AKEYCODE_VOLUME_DOWN: + case AKEYCODE_VOLUME_MUTE: case AKEYCODE_MUTE: case AKEYCODE_POWER: case AKEYCODE_HEADSETHOOK: + case AKEYCODE_MEDIA_PLAY: + case AKEYCODE_MEDIA_PAUSE: case AKEYCODE_MEDIA_PLAY_PAUSE: case AKEYCODE_MEDIA_STOP: case AKEYCODE_MEDIA_NEXT: case AKEYCODE_MEDIA_PREVIOUS: case AKEYCODE_MEDIA_REWIND: + case AKEYCODE_MEDIA_RECORD: case AKEYCODE_MEDIA_FAST_FORWARD: case AKEYCODE_CAMERA: case AKEYCODE_FOCUS: @@ -123,7 +237,7 @@ void KeyEvent::initialize(const KeyEvent& from) { mEventTime = from.mEventTime; } -// class MotionEvent +// --- MotionEvent --- void MotionEvent::initialize( int32_t deviceId, @@ -170,7 +284,7 @@ void MotionEvent::offsetLocation(float xOffset, float yOffset) { mYOffset += yOffset; } -// class InputDeviceInfo +// --- InputDeviceInfo --- InputDeviceInfo::InputDeviceInfo() { initialize(-1, String8("uninitialized device info")); diff --git a/libs/ui/InputDispatcher.cpp b/libs/ui/InputDispatcher.cpp deleted file mode 100644 index 421ad663f8..0000000000 --- a/libs/ui/InputDispatcher.cpp +++ /dev/null @@ -1,3518 +0,0 @@ -// -// 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> - -#define INDENT " " -#define INDENT2 " " - -namespace android { - -// Delay before reporting long touch events to the power manager. -const nsecs_t LONG_TOUCH_DELAY = 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"; -} - -static inline int32_t getMotionEventActionPointerIndex(int32_t action) { - return (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) - >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; -} - -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 validateKeyEvent(int32_t action) { - if (! isValidKeyAction(action)) { - LOGE("Key event has invalid action code 0x%x", action); - return false; - } - return true; -} - -static bool isValidMotionAction(int32_t action, size_t pointerCount) { - 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_OUTSIDE: - return true; - case AMOTION_EVENT_ACTION_POINTER_DOWN: - case AMOTION_EVENT_ACTION_POINTER_UP: { - int32_t index = getMotionEventActionPointerIndex(action); - return index >= 0 && size_t(index) < pointerCount; - } - default: - return false; - } -} - -static bool validateMotionEvent(int32_t action, size_t pointerCount, - const int32_t* pointerIds) { - if (! isValidMotionAction(action, pointerCount)) { - LOGE("Motion event has invalid action code 0x%x", action); - return false; - } - if (pointerCount < 1 || pointerCount > MAX_POINTERS) { - LOGE("Motion event has invalid pointer count %d; value must be between 1 and %d.", - pointerCount, MAX_POINTERS); - return false; - } - BitSet32 pointerIdBits; - for (size_t i = 0; i < pointerCount; i++) { - int32_t id = pointerIds[i]; - if (id < 0 || id > MAX_POINTER_ID) { - LOGE("Motion event has invalid pointer id %d; value must be between 0 and %d", - id, MAX_POINTER_ID); - return false; - } - if (pointerIdBits.hasBit(id)) { - LOGE("Motion event has duplicate pointer id %d", id); - return false; - } - pointerIdBits.markBit(id); - } - return true; -} - - -// --- InputWindow --- - -bool InputWindow::touchableAreaContainsPoint(int32_t x, int32_t y) const { - return x >= touchableAreaLeft && x <= touchableAreaRight - && y >= touchableAreaTop && y <= touchableAreaBottom; -} - -bool InputWindow::frameContainsPoint(int32_t x, int32_t y) const { - return x >= frameLeft && x <= frameRight - && y >= frameTop && y <= frameBottom; -} - -bool InputWindow::isTrustedOverlay() const { - return layoutParamsType == TYPE_INPUT_METHOD - || layoutParamsType == TYPE_INPUT_METHOD_DIALOG - || layoutParamsType == TYPE_SECURE_SYSTEM_OVERLAY; -} - - -// --- InputDispatcher --- - -InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy) : - mPolicy(policy), - mPendingEvent(NULL), mAppSwitchDueTime(LONG_LONG_MAX), - mDispatchEnabled(true), mDispatchFrozen(false), - mFocusedWindow(NULL), - mFocusedApplication(NULL), - mCurrentInputTargetsValid(false), - mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE) { - mLooper = new Looper(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(); - 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; - } - - mLooper->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 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; - } - - // 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 - && !isAppSwitchDue - && mDispatchEnabled - && (entry->policyFlags & POLICY_FLAG_PASS_TO_USER) - && !entry->isInjected()) { - 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; - } - - // Poke user activity for this event. - if (mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER) { - pokeUserActivityLocked(mPendingEvent); - } - } - - // Now we have an event to dispatch. - assert(mPendingEvent != NULL); - bool done = false; - DropReason dropReason = DROP_REASON_NOT_DROPPED; - if (!(mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER)) { - dropReason = DROP_REASON_POLICY; - } else if (!mDispatchEnabled) { - dropReason = DROP_REASON_DISABLED; - } - switch (mPendingEvent->type) { - case EventEntry::TYPE_CONFIGURATION_CHANGED: { - ConfigurationChangedEntry* typedEntry = - static_cast<ConfigurationChangedEntry*>(mPendingEvent); - done = dispatchConfigurationChangedLocked(currentTime, typedEntry); - dropReason = DROP_REASON_NOT_DROPPED; // configuration changes are never dropped - break; - } - - case EventEntry::TYPE_KEY: { - KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent); - if (isAppSwitchDue) { - if (isAppSwitchKeyEventLocked(typedEntry)) { - resetPendingAppSwitchLocked(true); - isAppSwitchDue = false; - } else if (dropReason == DROP_REASON_NOT_DROPPED) { - dropReason = DROP_REASON_APP_SWITCH; - } - } - done = dispatchKeyLocked(currentTime, typedEntry, keyRepeatTimeout, - &dropReason, nextWakeupTime); - break; - } - - case EventEntry::TYPE_MOTION: { - MotionEntry* typedEntry = static_cast<MotionEntry*>(mPendingEvent); - if (dropReason == DROP_REASON_NOT_DROPPED && isAppSwitchDue) { - dropReason = DROP_REASON_APP_SWITCH; - } - done = dispatchMotionLocked(currentTime, typedEntry, - &dropReason, nextWakeupTime); - break; - } - - default: - assert(false); - break; - } - - if (done) { - if (dropReason != DROP_REASON_NOT_DROPPED) { - dropInboundEventLocked(mPendingEvent, dropReason); - } - - releasePendingEventLocked(); - *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: { - KeyEntry* keyEntry = static_cast<KeyEntry*>(entry); - if (isAppSwitchKeyEventLocked(keyEntry)) { - if (keyEntry->action == AKEY_EVENT_ACTION_DOWN) { - mAppSwitchSawKeyDown = true; - } else if (keyEntry->action == AKEY_EVENT_ACTION_UP) { - if (mAppSwitchSawKeyDown) { -#if DEBUG_APP_SWITCH - LOGD("App switch is pending!"); -#endif - mAppSwitchDueTime = keyEntry->eventTime + APP_SWITCH_TIMEOUT; - mAppSwitchSawKeyDown = false; - needWake = true; - } - } - } - break; - } - } - - return needWake; -} - -void InputDispatcher::dropInboundEventLocked(EventEntry* entry, DropReason dropReason) { - const char* reason; - switch (dropReason) { - case DROP_REASON_POLICY: -#if DEBUG_INBOUND_EVENT_DETAILS - LOGD("Dropped event because policy consumed it."); -#endif - reason = "inbound event was dropped because the policy consumed it"; - break; - case DROP_REASON_DISABLED: - LOGI("Dropped event because input dispatch is disabled."); - reason = "inbound event was dropped because input dispatch is disabled"; - break; - case DROP_REASON_APP_SWITCH: - LOGI("Dropped event because of pending overdue app switch."); - reason = "inbound event was dropped because of pending overdue app switch"; - break; - default: - assert(false); - return; - } - - switch (entry->type) { - case EventEntry::TYPE_KEY: - synthesizeCancelationEventsForAllConnectionsLocked( - InputState::CANCEL_NON_POINTER_EVENTS, reason); - break; - case EventEntry::TYPE_MOTION: { - MotionEntry* motionEntry = static_cast<MotionEntry*>(entry); - if (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) { - synthesizeCancelationEventsForAllConnectionsLocked( - InputState::CANCEL_POINTER_EVENTS, reason); - } else { - synthesizeCancelationEventsForAllConnectionsLocked( - InputState::CANCEL_NON_POINTER_EVENTS, reason); - } - break; - } - } -} - -bool InputDispatcher::isAppSwitchKeyCode(int32_t keyCode) { - return keyCode == AKEYCODE_HOME || keyCode == AKEYCODE_ENDCALL; -} - -bool InputDispatcher::isAppSwitchKeyEventLocked(KeyEntry* keyEntry) { - return ! (keyEntry->flags & AKEY_EVENT_FLAG_CANCELED) - && isAppSwitchKeyCode(keyEntry->keyCode) - && (keyEntry->policyFlags & POLICY_FLAG_TRUSTED) - && (keyEntry->policyFlags & POLICY_FLAG_PASS_TO_USER); -} - -bool InputDispatcher::isAppSwitchPendingLocked() { - return mAppSwitchDueTime != LONG_LONG_MAX; -} - -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); - } -} - -void InputDispatcher::releasePendingEventLocked() { - if (mPendingEvent) { - releaseInboundEventLocked(mPendingEvent); - mPendingEvent = NULL; - } -} - -void InputDispatcher::releaseInboundEventLocked(EventEntry* entry) { - InjectionState* injectionState = entry->injectionState; - if (injectionState && injectionState->injectionResult == INPUT_EVENT_INJECTION_PENDING) { -#if DEBUG_DISPATCH_CYCLE - LOGD("Injected inbound event was dropped."); -#endif - setInjectionResultLocked(entry, INPUT_EVENT_INJECTION_FAILED); - } - mAllocator.releaseEventEntry(entry); -} - -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) - | POLICY_FLAG_PASS_TO_USER | POLICY_FLAG_TRUSTED; - if (entry->refCount == 1) { - mAllocator.recycleKeyEntry(entry); - 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, - DropReason* dropReason, nsecs_t* nextWakeupTime) { - // Preprocessing. - if (! entry->dispatchInProgress) { - if (entry->repeatCount == 0 - && entry->action == AKEY_EVENT_ACTION_DOWN - && (entry->policyFlags & POLICY_FLAG_TRUSTED) - && !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; - resetTargetsLocked(); - - logOutboundKeyDetailsLocked("dispatchKey - ", entry); - } - - // Give the policy a chance to intercept the key. - if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) { - if (entry->policyFlags & POLICY_FLAG_PASS_TO_USER) { - CommandEntry* commandEntry = postCommandLocked( - & InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible); - if (mFocusedWindow) { - commandEntry->inputChannel = mFocusedWindow->inputChannel; - } - commandEntry->keyEntry = entry; - entry->refCount += 1; - return false; // wait for the command to run - } else { - entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE; - } - } else if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_SKIP) { - if (*dropReason == DROP_REASON_NOT_DROPPED) { - *dropReason = DROP_REASON_POLICY; - } - } - - // Clean up if dropping the event. - if (*dropReason != DROP_REASON_NOT_DROPPED) { - resetTargetsLocked(); - setInjectionResultLocked(entry, *dropReason == DROP_REASON_POLICY - ? INPUT_EVENT_INJECTION_SUCCEEDED : INPUT_EVENT_INJECTION_FAILED); - return true; - } - - // Identify targets. - if (! mCurrentInputTargetsValid) { - int32_t injectionResult = findFocusedWindowTargetsLocked(currentTime, - entry, nextWakeupTime); - if (injectionResult == INPUT_EVENT_INJECTION_PENDING) { - return false; - } - - setInjectionResultLocked(entry, injectionResult); - if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) { - return true; - } - - addMonitoringTargetsLocked(); - commitTargetsLocked(); - } - - // Dispatch the key. - dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false); - 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, " - "repeatCount=%d, downTime=%lld", - prefix, - entry->eventTime, entry->deviceId, entry->source, entry->policyFlags, - entry->action, entry->flags, entry->keyCode, entry->scanCode, entry->metaState, - entry->repeatCount, entry->downTime); -#endif -} - -bool InputDispatcher::dispatchMotionLocked( - nsecs_t currentTime, MotionEntry* entry, DropReason* dropReason, nsecs_t* nextWakeupTime) { - // Preprocessing. - if (! entry->dispatchInProgress) { - entry->dispatchInProgress = true; - resetTargetsLocked(); - - logOutboundMotionDetailsLocked("dispatchMotion - ", entry); - } - - // Clean up if dropping the event. - if (*dropReason != DROP_REASON_NOT_DROPPED) { - resetTargetsLocked(); - setInjectionResultLocked(entry, *dropReason == DROP_REASON_POLICY - ? INPUT_EVENT_INJECTION_SUCCEEDED : INPUT_EVENT_INJECTION_FAILED); - return true; - } - - bool isPointerEvent = entry->source & AINPUT_SOURCE_CLASS_POINTER; - - // Identify targets. - if (! mCurrentInputTargetsValid) { - int32_t injectionResult; - if (isPointerEvent) { - // Pointer event. (eg. touchscreen) - injectionResult = findTouchedWindowTargetsLocked(currentTime, - entry, nextWakeupTime); - } else { - // Non touch event. (eg. trackball) - injectionResult = findFocusedWindowTargetsLocked(currentTime, - entry, nextWakeupTime); - } - if (injectionResult == INPUT_EVENT_INJECTION_PENDING) { - return false; - } - - setInjectionResultLocked(entry, injectionResult); - if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) { - return true; - } - - addMonitoringTargetsLocked(); - commitTargetsLocked(); - } - - // Dispatch the motion. - dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false); - 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 - - pokeUserActivityLocked(eventEntry); - - for (size_t i = 0; i < mCurrentInputTargets.size(); i++) { - const InputTarget& inputTarget = mCurrentInputTargets.itemAt(i); - - ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel); - if (connectionIndex >= 0) { - sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex); - prepareDispatchCycleLocked(currentTime, connection, eventEntry, & inputTarget, - resumeWithAppendedMotionSample); - } else { -#if DEBUG_FOCUS - LOGD("Dropping event delivery to target with channel '%s' because it " - "is no longer registered with the input dispatcher.", - inputTarget.inputChannel->getName().string()); -#endif - } - } -} - -void InputDispatcher::resetTargetsLocked() { - mCurrentInputTargetsValid = false; - mCurrentInputTargets.clear(); - mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_NONE; -} - -void InputDispatcher::commitTargetsLocked() { - 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: %s", - getApplicationWindowLabelLocked(application, window).string()); -#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) { - onANRLocked(currentTime, application, window, entry->eventTime, mInputTargetWaitStartTime); - - // 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, - const sp<InputChannel>& inputChannel) { - if (newTimeout > 0) { - // Extend the timeout. - mInputTargetWaitTimeoutTime = now() + newTimeout; - } else { - // Give up. - mInputTargetWaitTimeoutExpired = true; - - // Release the touch targets. - mTouchState.reset(); - - // Input state will not be realistic. Mark it out of sync. - if (inputChannel.get()) { - ssize_t connectionIndex = getConnectionIndexLocked(inputChannel); - if (connectionIndex >= 0) { - sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex); - synthesizeCancelationEventsForConnectionLocked( - connection, InputState::CANCEL_ALL_EVENTS, - "application not responding"); - } - } - } -} - -nsecs_t InputDispatcher::getTimeSpentWaitingForApplicationLocked( - 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 input target wait timeout. - mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_NONE; -} - -int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime, - const EventEntry* entry, nsecs_t* nextWakeupTime) { - 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.", - getApplicationWindowLabelLocked(mFocusedApplication, NULL).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->injectionState)) { - 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; - } - - // If the currently focused window is still working on previous events then keep waiting. - if (! isWindowFinishedWithPreviousInputLocked(mFocusedWindow)) { -#if DEBUG_FOCUS - LOGD("Waiting because focused window still processing previous input."); -#endif - injectionResult = handleTargetsNotReadyLocked(currentTime, entry, - mFocusedApplication, mFocusedWindow, nextWakeupTime); - goto Unresponsive; - } - - // Success! Output targets. - injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED; - addWindowTargetLocked(mFocusedWindow, InputTarget::FLAG_FOREGROUND, BitSet32(0)); - - // Done. -Failed: -Unresponsive: - nsecs_t timeSpentWaitingForApplication = getTimeSpentWaitingForApplicationLocked(currentTime); - updateDispatchStatisticsLocked(currentTime, entry, - injectionResult, timeSpentWaitingForApplication); -#if DEBUG_FOCUS - LOGD("findFocusedWindow finished: injectionResult=%d, " - "timeSpendWaitingForApplication=%0.1fms", - injectionResult, timeSpentWaitingForApplication / 1000000.0); -#endif - return injectionResult; -} - -int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, - const MotionEntry* entry, nsecs_t* nextWakeupTime) { - enum InjectionPermission { - INJECTION_PERMISSION_UNKNOWN, - INJECTION_PERMISSION_GRANTED, - INJECTION_PERMISSION_DENIED - }; - - 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; - int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK; - - // Update the touch state as needed based on the properties of the touch event. - int32_t injectionResult = INPUT_EVENT_INJECTION_PENDING; - InjectionPermission injectionPermission = INJECTION_PERMISSION_UNKNOWN; - if (maskedAction == AMOTION_EVENT_ACTION_DOWN) { - mTempTouchState.reset(); - mTempTouchState.down = true; - } else { - mTempTouchState.copyFrom(mTouchState); - } - - bool isSplit = mTempTouchState.split && mTempTouchState.down; - if (maskedAction == AMOTION_EVENT_ACTION_DOWN - || (isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN)) { - /* Case 1: New splittable pointer going down. */ - - int32_t pointerIndex = getMotionEventActionPointerIndex(action); - int32_t x = int32_t(entry->firstSample.pointerCoords[pointerIndex].x); - int32_t y = int32_t(entry->firstSample.pointerCoords[pointerIndex].y); - const InputWindow* newTouchedWindow = NULL; - const InputWindow* topErrorWindow = NULL; - - // 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++) { - const 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; - } - break; // found touched window, exit window loop - } - } - - if (maskedAction == AMOTION_EVENT_ACTION_DOWN - && (flags & InputWindow::FLAG_WATCH_OUTSIDE_TOUCH)) { - int32_t outsideTargetFlags = InputTarget::FLAG_OUTSIDE; - if (isWindowObscuredAtPointLocked(window, x, y)) { - outsideTargetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED; - } - - mTempTouchState.addOrUpdateWindow(window, outsideTargetFlags, BitSet32(0)); - } - } - } - - // 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; - } - - // Figure out whether splitting will be allowed for this window. - if (newTouchedWindow - && (newTouchedWindow->layoutParamsFlags & InputWindow::FLAG_SPLIT_TOUCH)) { - // New window supports splitting. - isSplit = true; - } else if (isSplit) { - // New window does not support splitting but we have already split events. - // Assign the pointer to the first foreground window we find. - // (May be NULL which is why we put this code block before the next check.) - newTouchedWindow = mTempTouchState.getFirstForegroundWindow(); - } - - // 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.", - getApplicationWindowLabelLocked(mFocusedApplication, NULL).string()); -#endif - injectionResult = handleTargetsNotReadyLocked(currentTime, entry, - mFocusedApplication, NULL, nextWakeupTime); - goto Unresponsive; - } - - LOGI("Dropping event because there is no touched window or focused application."); - injectionResult = INPUT_EVENT_INJECTION_FAILED; - goto Failed; - } - - // Set target flags. - int32_t targetFlags = InputTarget::FLAG_FOREGROUND; - if (isSplit) { - targetFlags |= InputTarget::FLAG_SPLIT; - } - if (isWindowObscuredAtPointLocked(newTouchedWindow, x, y)) { - targetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED; - } - - // Update the temporary touch state. - BitSet32 pointerIds; - if (isSplit) { - uint32_t pointerId = entry->pointerIds[pointerIndex]; - pointerIds.markBit(pointerId); - } - mTempTouchState.addOrUpdateWindow(newTouchedWindow, targetFlags, pointerIds); - } else { - /* Case 2: Pointer move, up, cancel or non-splittable pointer down. */ - - // If the pointer is not currently down, then ignore the event. - if (! mTempTouchState.down) { - LOGI("Dropping event because the pointer is not down."); - injectionResult = INPUT_EVENT_INJECTION_FAILED; - goto Failed; - } - } - - // Check permission to inject into all touched foreground windows and ensure there - // is at least one touched foreground window. - { - bool haveForegroundWindow = false; - for (size_t i = 0; i < mTempTouchState.windows.size(); i++) { - const TouchedWindow& touchedWindow = mTempTouchState.windows[i]; - if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) { - haveForegroundWindow = true; - if (! checkInjectionPermission(touchedWindow.window, entry->injectionState)) { - injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED; - injectionPermission = INJECTION_PERMISSION_DENIED; - goto Failed; - } - } - } - if (! haveForegroundWindow) { -#if DEBUG_INPUT_DISPATCHER_POLICY - LOGD("Dropping event because there is no touched foreground window to receive it."); -#endif - injectionResult = INPUT_EVENT_INJECTION_FAILED; - goto Failed; - } - - // Permission granted to injection into all touched foreground windows. - injectionPermission = INJECTION_PERMISSION_GRANTED; - } - - // Ensure all touched foreground windows are ready for new input. - for (size_t i = 0; i < mTempTouchState.windows.size(); i++) { - const TouchedWindow& touchedWindow = mTempTouchState.windows[i]; - if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) { - // If the touched window is paused then keep waiting. - if (touchedWindow.window->paused) { -#if DEBUG_INPUT_DISPATCHER_POLICY - LOGD("Waiting because touched window is paused."); -#endif - injectionResult = handleTargetsNotReadyLocked(currentTime, entry, - NULL, touchedWindow.window, nextWakeupTime); - goto Unresponsive; - } - - // If the touched window is still working on previous events then keep waiting. - if (! isWindowFinishedWithPreviousInputLocked(touchedWindow.window)) { -#if DEBUG_FOCUS - LOGD("Waiting because touched window still processing previous input."); -#endif - injectionResult = handleTargetsNotReadyLocked(currentTime, entry, - NULL, touchedWindow.window, nextWakeupTime); - goto Unresponsive; - } - } - } - - // If this is the first pointer going down and the touched window has a wallpaper - // then also add the touched wallpaper windows so they are locked in for the duration - // of the touch gesture. - if (maskedAction == AMOTION_EVENT_ACTION_DOWN) { - const InputWindow* foregroundWindow = mTempTouchState.getFirstForegroundWindow(); - if (foregroundWindow->hasWallpaper) { - for (size_t i = 0; i < mWindows.size(); i++) { - const InputWindow* window = & mWindows[i]; - if (window->layoutParamsType == InputWindow::TYPE_WALLPAPER) { - mTempTouchState.addOrUpdateWindow(window, - InputTarget::FLAG_WINDOW_IS_OBSCURED, BitSet32(0)); - } - } - } - } - - // Success! Output targets. - injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED; - - for (size_t i = 0; i < mTempTouchState.windows.size(); i++) { - const TouchedWindow& touchedWindow = mTempTouchState.windows.itemAt(i); - addWindowTargetLocked(touchedWindow.window, touchedWindow.targetFlags, - touchedWindow.pointerIds); - } - - // Drop the outside touch window since we will not care about them in the next iteration. - mTempTouchState.removeOutsideTouchWindows(); - -Failed: - // Check injection permission once and for all. - if (injectionPermission == INJECTION_PERMISSION_UNKNOWN) { - if (checkInjectionPermission(NULL, entry->injectionState)) { - 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 (maskedAction == AMOTION_EVENT_ACTION_UP - || maskedAction == AMOTION_EVENT_ACTION_CANCEL) { - // All pointers up or canceled. - mTempTouchState.reset(); - } else if (maskedAction == AMOTION_EVENT_ACTION_DOWN) { - // First pointer went down. - if (mTouchState.down) { -#if DEBUG_FOCUS - LOGD("Pointer down received while already down."); -#endif - } - } else if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) { - // One pointer went up. - if (isSplit) { - int32_t pointerIndex = getMotionEventActionPointerIndex(action); - uint32_t pointerId = entry->pointerIds[pointerIndex]; - - for (size_t i = 0; i < mTempTouchState.windows.size(); ) { - TouchedWindow& touchedWindow = mTempTouchState.windows.editItemAt(i); - if (touchedWindow.targetFlags & InputTarget::FLAG_SPLIT) { - touchedWindow.pointerIds.clearBit(pointerId); - if (touchedWindow.pointerIds.isEmpty()) { - mTempTouchState.windows.removeAt(i); - continue; - } - } - i += 1; - } - } - } - - // Save changes to touch state. - mTouchState.copyFrom(mTempTouchState); - } else { -#if DEBUG_FOCUS - LOGD("Not updating touch focus because injection was denied."); -#endif - } - -Unresponsive: - // Reset temporary touch state to ensure we release unnecessary references to input channels. - mTempTouchState.reset(); - - nsecs_t timeSpentWaitingForApplication = getTimeSpentWaitingForApplicationLocked(currentTime); - updateDispatchStatisticsLocked(currentTime, entry, - injectionResult, timeSpentWaitingForApplication); -#if DEBUG_FOCUS - LOGD("findTouchedWindow finished: injectionResult=%d, injectionPermission=%d, " - "timeSpentWaitingForApplication=%0.1fms", - injectionResult, injectionPermission, timeSpentWaitingForApplication / 1000000.0); -#endif - return injectionResult; -} - -void InputDispatcher::addWindowTargetLocked(const InputWindow* window, int32_t targetFlags, - BitSet32 pointerIds) { - mCurrentInputTargets.push(); - - InputTarget& target = mCurrentInputTargets.editTop(); - target.inputChannel = window->inputChannel; - target.flags = targetFlags; - target.xOffset = - window->frameLeft; - target.yOffset = - window->frameTop; - target.pointerIds = pointerIds; -} - -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.xOffset = 0; - target.yOffset = 0; - } -} - -bool InputDispatcher::checkInjectionPermission(const InputWindow* window, - const InjectionState* injectionState) { - if (injectionState - && (window == NULL || window->ownerUid != injectionState->injectorUid) - && !hasInjectionPermission(injectionState->injectorPid, injectionState->injectorUid)) { - if (window) { - LOGW("Permission denied: injecting event from pid %d uid %d to window " - "with input channel %s owned by uid %d", - injectionState->injectorPid, injectionState->injectorUid, - window->inputChannel->getName().string(), - window->ownerUid); - } else { - LOGW("Permission denied: injecting event from pid %d uid %d", - injectionState->injectorPid, injectionState->injectorUid); - } - return false; - } - return true; -} - -bool InputDispatcher::isWindowObscuredAtPointLocked( - const InputWindow* window, int32_t x, int32_t y) const { - 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 && ! other->isTrustedOverlay() && other->frameContainsPoint(x, y)) { - return true; - } - } - return false; -} - -bool InputDispatcher::isWindowFinishedWithPreviousInputLocked(const InputWindow* window) { - ssize_t connectionIndex = getConnectionIndexLocked(window->inputChannel); - if (connectionIndex >= 0) { - sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex); - return connection->outboundQueue.isEmpty(); - } else { - return true; - } -} - -String8 InputDispatcher::getApplicationWindowLabelLocked(const InputApplication* application, - const InputWindow* window) { - if (application) { - if (window) { - String8 label(application->name); - label.append(" - "); - label.append(window->name); - return label; - } else { - return application->name; - } - } else if (window) { - return window->name; - } else { - return String8("<unknown application or window>"); - } -} - -void InputDispatcher::pokeUserActivityLocked(const EventEntry* eventEntry) { - int32_t eventType = POWER_MANAGER_BUTTON_EVENT; - switch (eventEntry->type) { - case EventEntry::TYPE_MOTION: { - const MotionEntry* motionEntry = static_cast<const MotionEntry*>(eventEntry); - if (motionEntry->action == AMOTION_EVENT_ACTION_CANCEL) { - return; - } - - if (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) { - switch (motionEntry->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 (motionEntry->eventTime - motionEntry->downTime < LONG_TOUCH_DELAY) { - eventType = POWER_MANAGER_TOUCH_EVENT; - } else { - eventType = POWER_MANAGER_LONG_TOUCH_EVENT; - } - break; - } - } - break; - } - case EventEntry::TYPE_KEY: { - const KeyEntry* keyEntry = static_cast<const KeyEntry*>(eventEntry); - if (keyEntry->flags & AKEY_EVENT_FLAG_CANCELED) { - return; - } - break; - } - } - - CommandEntry* commandEntry = postCommandLocked( - & InputDispatcher::doPokeUserActivityLockedInterruptible); - commandEntry->eventTime = eventEntry->eventTime; - 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, " - "xOffset=%f, yOffset=%f, " - "windowType=%d, pointerIds=0x%x, " - "resumeWithAppendedMotionSample=%s", - connection->getInputChannelName(), inputTarget->flags, - inputTarget->xOffset, inputTarget->yOffset, - inputTarget->windowType, inputTarget->pointerIds.value, - toString(resumeWithAppendedMotionSample)); -#endif - - // Make sure we are never called for streaming when splitting across multiple windows. - bool isSplit = inputTarget->flags & InputTarget::FLAG_SPLIT; - assert(! (resumeWithAppendedMotionSample && isSplit)); - - // Skip this event if the connection status is not normal. - // We don't want to enqueue additional outbound events if the connection is broken. - if (connection->status != Connection::STATUS_NORMAL) { -#if DEBUG_DISPATCH_CYCLE - LOGD("channel '%s' ~ Dropping event because the channel status is %s", - connection->getInputChannelName(), connection->getStatusLabel()); -#endif - return; - } - - // Split a motion event if needed. - if (isSplit) { - assert(eventEntry->type == EventEntry::TYPE_MOTION); - - MotionEntry* originalMotionEntry = static_cast<MotionEntry*>(eventEntry); - if (inputTarget->pointerIds.count() != originalMotionEntry->pointerCount) { - MotionEntry* splitMotionEntry = splitMotionEvent( - originalMotionEntry, inputTarget->pointerIds); -#if DEBUG_FOCUS - LOGD("channel '%s' ~ Split motion event.", - connection->getInputChannelName()); - logOutboundMotionDetailsLocked(" ", splitMotionEntry); -#endif - eventEntry = splitMotionEntry; - } - } - - // 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). - MotionEntry* motionEntry = static_cast<MotionEntry*>(eventEntry); - MotionSample* appendedMotionSample = motionEntry->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; - } - } - - // 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); - if (dispatchEntry->hasForegroundTarget()) { - incrementPendingForegroundDispatchesLocked(eventEntry); - } - - // 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); - } -} - -void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, - const sp<Connection>& connection) { -#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. - EventEntry* eventEntry = dispatchEntry->eventEntry; - InputState::Consistency consistency = connection->inputState.trackEvent(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 (eventEntry->type) { - case EventEntry::TYPE_KEY: { - KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry); - - // Apply target flags. - int32_t action = keyEntry->action; - int32_t flags = keyEntry->flags; - - // 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); - abortBrokenDispatchCycleLocked(currentTime, connection); - return; - } - break; - } - - case EventEntry::TYPE_MOTION: { - MotionEntry* motionEntry = static_cast<MotionEntry*>(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_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); - abortBrokenDispatchCycleLocked(currentTime, connection); - 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); - abortBrokenDispatchCycleLocked(currentTime, connection); - 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); - abortBrokenDispatchCycleLocked(currentTime, connection); - return; - } - - // Record information about the newly started dispatch cycle. - connection->lastEventTime = eventEntry->eventTime; - connection->lastDispatchTime = currentTime; - - // 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; - } - - // Notify other system components. - onDispatchCycleFinishedLocked(currentTime, connection); - - // 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); - abortBrokenDispatchCycleLocked(currentTime, connection); - 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); - return; - } - // Finished. - connection->outboundQueue.dequeueAtHead(); - if (dispatchEntry->hasForegroundTarget()) { - decrementPendingForegroundDispatchesLocked(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. - // So just start the next event for this connection. - startDispatchCycleLocked(currentTime, connection); - return; - } - } - - // Outbound queue is empty, deactivate the connection. - deactivateConnectionLocked(connection.get()); -} - -void InputDispatcher::abortBrokenDispatchCycleLocked(nsecs_t currentTime, - const sp<Connection>& connection) { -#if DEBUG_DISPATCH_CYCLE - LOGD("channel '%s' ~ abortBrokenDispatchCycle - broken=%s", - connection->getInputChannelName(), toString(broken)); -#endif - - // Clear the outbound queue. - drainOutboundQueueLocked(connection.get()); - - // The connection appears to be unrecoverably broken. - // Ignore already broken or zombie connections. - if (connection->status == Connection::STATUS_NORMAL) { - connection->status = Connection::STATUS_BROKEN; - - // Notify other system components. - onDispatchCycleBrokenLocked(currentTime, connection); - } -} - -void InputDispatcher::drainOutboundQueueLocked(Connection* connection) { - while (! connection->outboundQueue.isEmpty()) { - DispatchEntry* dispatchEntry = connection->outboundQueue.dequeueAtHead(); - if (dispatchEntry->hasForegroundTarget()) { - decrementPendingForegroundDispatchesLocked(dispatchEntry->eventEntry); - } - mAllocator.releaseDispatchEntry(dispatchEntry); - } - - deactivateConnectionLocked(connection); -} - -int 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 0; // remove the callback - } - - nsecs_t currentTime = now(); - - sp<Connection> connection = d->mConnectionsByReceiveFd.valueAt(connectionIndex); - if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) { - LOGE("channel '%s' ~ Consumer closed input channel or an error occurred. " - "events=0x%x", connection->getInputChannelName(), events); - d->abortBrokenDispatchCycleLocked(currentTime, connection); - d->runCommandsLockedInterruptible(); - return 0; // remove the callback - } - - if (! (events & ALOOPER_EVENT_INPUT)) { - LOGW("channel '%s' ~ Received spurious callback for unhandled poll event. " - "events=0x%x", connection->getInputChannelName(), events); - return 1; - } - - status_t status = connection->inputPublisher.receiveFinishedSignal(); - if (status) { - LOGE("channel '%s' ~ Failed to receive finished signal. status=%d", - connection->getInputChannelName(), status); - d->abortBrokenDispatchCycleLocked(currentTime, connection); - d->runCommandsLockedInterruptible(); - return 0; // remove the callback - } - - d->finishDispatchCycleLocked(currentTime, connection); - d->runCommandsLockedInterruptible(); - return 1; - } // release lock -} - -void InputDispatcher::synthesizeCancelationEventsForAllConnectionsLocked( - InputState::CancelationOptions options, const char* reason) { - for (size_t i = 0; i < mConnectionsByReceiveFd.size(); i++) { - synthesizeCancelationEventsForConnectionLocked( - mConnectionsByReceiveFd.valueAt(i), options, reason); - } -} - -void InputDispatcher::synthesizeCancelationEventsForInputChannelLocked( - const sp<InputChannel>& channel, InputState::CancelationOptions options, - const char* reason) { - ssize_t index = getConnectionIndexLocked(channel); - if (index >= 0) { - synthesizeCancelationEventsForConnectionLocked( - mConnectionsByReceiveFd.valueAt(index), options, reason); - } -} - -void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( - const sp<Connection>& connection, InputState::CancelationOptions options, - const char* reason) { - nsecs_t currentTime = now(); - - mTempCancelationEvents.clear(); - connection->inputState.synthesizeCancelationEvents(currentTime, & mAllocator, - mTempCancelationEvents, options); - - if (! mTempCancelationEvents.isEmpty() - && connection->status != Connection::STATUS_BROKEN) { -#if DEBUG_OUTBOUND_EVENT_DETAILS - LOGD("channel '%s' ~ Synthesized %d cancelation events to bring channel back in sync " - "with reality: %s, options=%d.", - connection->getInputChannelName(), mTempCancelationEvents.size(), reason, options); -#endif - for (size_t i = 0; i < mTempCancelationEvents.size(); i++) { - EventEntry* cancelationEventEntry = mTempCancelationEvents.itemAt(i); - switch (cancelationEventEntry->type) { - case EventEntry::TYPE_KEY: - logOutboundKeyDetailsLocked("cancel - ", - static_cast<KeyEntry*>(cancelationEventEntry)); - break; - case EventEntry::TYPE_MOTION: - logOutboundMotionDetailsLocked("cancel - ", - static_cast<MotionEntry*>(cancelationEventEntry)); - break; - } - - int32_t xOffset, yOffset; - const InputWindow* window = getWindowLocked(connection->inputChannel); - if (window) { - xOffset = -window->frameLeft; - yOffset = -window->frameTop; - } else { - xOffset = 0; - yOffset = 0; - } - - DispatchEntry* cancelationDispatchEntry = - mAllocator.obtainDispatchEntry(cancelationEventEntry, // increments ref - 0, xOffset, yOffset); - connection->outboundQueue.enqueueAtTail(cancelationDispatchEntry); - - mAllocator.releaseEventEntry(cancelationEventEntry); - } - - if (!connection->outboundQueue.headSentinel.next->inProgress) { - startDispatchCycleLocked(currentTime, connection); - } - } -} - -InputDispatcher::MotionEntry* -InputDispatcher::splitMotionEvent(const MotionEntry* originalMotionEntry, BitSet32 pointerIds) { - assert(pointerIds.value != 0); - - uint32_t splitPointerIndexMap[MAX_POINTERS]; - int32_t splitPointerIds[MAX_POINTERS]; - PointerCoords splitPointerCoords[MAX_POINTERS]; - - uint32_t originalPointerCount = originalMotionEntry->pointerCount; - uint32_t splitPointerCount = 0; - - for (uint32_t originalPointerIndex = 0; originalPointerIndex < originalPointerCount; - originalPointerIndex++) { - int32_t pointerId = uint32_t(originalMotionEntry->pointerIds[originalPointerIndex]); - if (pointerIds.hasBit(pointerId)) { - splitPointerIndexMap[splitPointerCount] = originalPointerIndex; - splitPointerIds[splitPointerCount] = pointerId; - splitPointerCoords[splitPointerCount] = - originalMotionEntry->firstSample.pointerCoords[originalPointerIndex]; - splitPointerCount += 1; - } - } - assert(splitPointerCount == pointerIds.count()); - - int32_t action = originalMotionEntry->action; - int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK; - if (maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN - || maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) { - int32_t originalPointerIndex = getMotionEventActionPointerIndex(action); - int32_t pointerId = originalMotionEntry->pointerIds[originalPointerIndex]; - if (pointerIds.hasBit(pointerId)) { - if (pointerIds.count() == 1) { - // The first/last pointer went down/up. - action = maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN - ? AMOTION_EVENT_ACTION_DOWN : AMOTION_EVENT_ACTION_UP; - } else { - // A secondary pointer went down/up. - uint32_t splitPointerIndex = 0; - while (pointerId != splitPointerIds[splitPointerIndex]) { - splitPointerIndex += 1; - } - action = maskedAction | (splitPointerIndex - << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); - } - } else { - // An unrelated pointer changed. - action = AMOTION_EVENT_ACTION_MOVE; - } - } - - MotionEntry* splitMotionEntry = mAllocator.obtainMotionEntry( - originalMotionEntry->eventTime, - originalMotionEntry->deviceId, - originalMotionEntry->source, - originalMotionEntry->policyFlags, - action, - originalMotionEntry->flags, - originalMotionEntry->metaState, - originalMotionEntry->edgeFlags, - originalMotionEntry->xPrecision, - originalMotionEntry->yPrecision, - originalMotionEntry->downTime, - splitPointerCount, splitPointerIds, splitPointerCoords); - - for (MotionSample* originalMotionSample = originalMotionEntry->firstSample.next; - originalMotionSample != NULL; originalMotionSample = originalMotionSample->next) { - for (uint32_t splitPointerIndex = 0; splitPointerIndex < splitPointerCount; - splitPointerIndex++) { - uint32_t originalPointerIndex = splitPointerIndexMap[splitPointerIndex]; - splitPointerCoords[splitPointerIndex] = - originalMotionSample->pointerCoords[originalPointerIndex]; - } - - mAllocator.appendMotionSample(splitMotionEntry, originalMotionSample->eventTime, - splitPointerCoords); - } - - return splitMotionEntry; -} - -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) { - mLooper->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 - if (! validateKeyEvent(action)) { - return; - } - - policyFlags |= POLICY_FLAG_TRUSTED; - mPolicy->interceptKeyBeforeQueueing(eventTime, deviceId, action, /*byref*/ flags, - keyCode, scanCode, /*byref*/ policyFlags); - - 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) { - mLooper->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 - if (! validateMotionEvent(action, pointerCount, pointerIds)) { - return; - } - - policyFlags |= POLICY_FLAG_TRUSTED; - mPolicy->interceptGenericBeforeQueueing(eventTime, /*byref*/ policyFlags); - - 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 queue for the current foreground targets to find a dispatched - // motion event that is still in progress. If found, then, appen the new sample to - // that event and push it out to all current targets. The logic in - // prepareDispatchCycleLocked takes care of the case where some targets may - // already have consumed the motion event by starting a new dispatch cycle if needed. - if (mCurrentInputTargetsValid) { - for (size_t i = 0; i < mCurrentInputTargets.size(); i++) { - const InputTarget& inputTarget = mCurrentInputTargets[i]; - if ((inputTarget.flags & InputTarget::FLAG_FOREGROUND) == 0) { - // Skip non-foreground targets. We only want to stream if there is at - // least one foreground target whose dispatch is still in progress. - continue; - } - - ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel); - if (connectionIndex < 0) { - // Connection must no longer be valid. - continue; - } - - sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex); - if (connection->outboundQueue.isEmpty()) { - // This foreground target has an empty outbound queue. - continue; - } - - DispatchEntry* dispatchEntry = connection->outboundQueue.headSentinel.next; - if (! dispatchEntry->inProgress - || dispatchEntry->eventEntry->type != EventEntry::TYPE_MOTION - || dispatchEntry->isSplit()) { - // No motion event is being dispatched, or it is being split across - // windows in which case we cannot stream. - continue; - } - - MotionEntry* motionEntry = static_cast<MotionEntry*>( - dispatchEntry->eventEntry); - if (motionEntry->action != AMOTION_EVENT_ACTION_MOVE - || motionEntry->deviceId != deviceId - || motionEntry->pointerCount != pointerCount - || motionEntry->isInjected()) { - // The motion event is not compatible with this move. - continue; - } - - // Hurray! This foreground target is currently dispatching a move event - // that we can stream onto. Append the motion sample and resume dispatch. - mAllocator.appendMotionSample(motionEntry, eventTime, pointerCoords); -#if DEBUG_BATCHING - LOGD("Appended motion sample onto batch for most recently dispatched " - "motion event for this device in the outbound queues. " - "Attempting to stream the motion sample."); -#endif - nsecs_t currentTime = now(); - dispatchEventToCurrentInputTargetsLocked(currentTime, motionEntry, - 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) { - mLooper->wake(); - } -} - -void InputDispatcher::notifySwitch(nsecs_t when, int32_t switchCode, int32_t switchValue, - uint32_t policyFlags) { -#if DEBUG_INBOUND_EVENT_DETAILS - LOGD("notifySwitch - switchCode=%d, switchValue=%d, policyFlags=0x%x", - switchCode, switchValue, policyFlags); -#endif - - policyFlags |= POLICY_FLAG_TRUSTED; - mPolicy->notifySwitch(when, switchCode, switchValue, policyFlags); -} - -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); - - uint32_t policyFlags = POLICY_FLAG_INJECTED; - if (hasInjectionPermission(injectorPid, injectorUid)) { - policyFlags |= POLICY_FLAG_TRUSTED; - } - - EventEntry* injectedEntry; - switch (event->getType()) { - case AINPUT_EVENT_TYPE_KEY: { - const KeyEvent* keyEvent = static_cast<const KeyEvent*>(event); - int32_t action = keyEvent->getAction(); - if (! validateKeyEvent(action)) { - return INPUT_EVENT_INJECTION_FAILED; - } - - nsecs_t eventTime = keyEvent->getEventTime(); - int32_t deviceId = keyEvent->getDeviceId(); - int32_t flags = keyEvent->getFlags(); - int32_t keyCode = keyEvent->getKeyCode(); - int32_t scanCode = keyEvent->getScanCode(); - mPolicy->interceptKeyBeforeQueueing(eventTime, deviceId, action, /*byref*/ flags, - keyCode, scanCode, /*byref*/ policyFlags); - - mLock.lock(); - injectedEntry = mAllocator.obtainKeyEntry(eventTime, deviceId, keyEvent->getSource(), - policyFlags, action, flags, keyCode, scanCode, keyEvent->getMetaState(), - keyEvent->getRepeatCount(), keyEvent->getDownTime()); - break; - } - - case AINPUT_EVENT_TYPE_MOTION: { - const MotionEvent* motionEvent = static_cast<const MotionEvent*>(event); - int32_t action = motionEvent->getAction(); - size_t pointerCount = motionEvent->getPointerCount(); - const int32_t* pointerIds = motionEvent->getPointerIds(); - if (! validateMotionEvent(action, pointerCount, pointerIds)) { - return INPUT_EVENT_INJECTION_FAILED; - } - - nsecs_t eventTime = motionEvent->getEventTime(); - mPolicy->interceptGenericBeforeQueueing(eventTime, /*byref*/ policyFlags); - - mLock.lock(); - const nsecs_t* sampleEventTimes = motionEvent->getSampleEventTimes(); - const PointerCoords* samplePointerCoords = motionEvent->getSamplePointerCoords(); - MotionEntry* motionEntry = mAllocator.obtainMotionEntry(*sampleEventTimes, - motionEvent->getDeviceId(), motionEvent->getSource(), policyFlags, - action, motionEvent->getFlags(), - motionEvent->getMetaState(), motionEvent->getEdgeFlags(), - motionEvent->getXPrecision(), motionEvent->getYPrecision(), - motionEvent->getDownTime(), uint32_t(pointerCount), - pointerIds, samplePointerCoords); - for (size_t i = motionEvent->getHistorySize(); i > 0; i--) { - sampleEventTimes += 1; - samplePointerCoords += pointerCount; - mAllocator.appendMotionSample(motionEntry, *sampleEventTimes, samplePointerCoords); - } - injectedEntry = motionEntry; - break; - } - - default: - LOGW("Cannot inject event of type %d", event->getType()); - return INPUT_EVENT_INJECTION_FAILED; - } - - InjectionState* injectionState = mAllocator.obtainInjectionState(injectorPid, injectorUid); - if (syncMode == INPUT_EVENT_INJECTION_SYNC_NONE) { - injectionState->injectionIsAsync = true; - } - - injectionState->refCount += 1; - injectedEntry->injectionState = injectionState; - - bool needWake = enqueueInboundEventLocked(injectedEntry); - mLock.unlock(); - - if (needWake) { - mLooper->wake(); - } - - int32_t injectionResult; - { // acquire lock - AutoMutex _l(mLock); - - if (syncMode == INPUT_EVENT_INJECTION_SYNC_NONE) { - injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED; - } else { - for (;;) { - injectionResult = injectionState->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 (injectionState->pendingForegroundDispatches != 0) { -#if DEBUG_INJECTION - LOGD("injectInputEvent - Waiting for %d pending foreground dispatches.", - injectionState->pendingForegroundDispatches); -#endif - nsecs_t remainingTimeout = endTime - now(); - if (remainingTimeout <= 0) { -#if DEBUG_INJECTION - LOGD("injectInputEvent - Timed out waiting for pending foreground " - "dispatches to finish."); -#endif - injectionResult = INPUT_EVENT_INJECTION_TIMED_OUT; - break; - } - - mInjectionSyncFinishedCondition.waitRelative(mLock, remainingTimeout); - } - } - } - - mAllocator.releaseInjectionState(injectionState); - } // release lock - -#if DEBUG_INJECTION - LOGD("injectInputEvent - Finished with result %d. " - "injectorPid=%d, injectorUid=%d", - injectionResult, injectorPid, injectorUid); -#endif - - return injectionResult; -} - -bool InputDispatcher::hasInjectionPermission(int32_t injectorPid, int32_t injectorUid) { - return injectorUid == 0 - || mPolicy->checkInjectEventsPermissionNonReentrant(injectorPid, injectorUid); -} - -void InputDispatcher::setInjectionResultLocked(EventEntry* entry, int32_t injectionResult) { - InjectionState* injectionState = entry->injectionState; - if (injectionState) { -#if DEBUG_INJECTION - LOGD("Setting input event injection result to %d. " - "injectorPid=%d, injectorUid=%d", - injectionResult, injectionState->injectorPid, injectionState->injectorUid); -#endif - - if (injectionState->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; - } - } - - injectionState->injectionResult = injectionResult; - mInjectionResultAvailableCondition.broadcast(); - } -} - -void InputDispatcher::incrementPendingForegroundDispatchesLocked(EventEntry* entry) { - InjectionState* injectionState = entry->injectionState; - if (injectionState) { - injectionState->pendingForegroundDispatches += 1; - } -} - -void InputDispatcher::decrementPendingForegroundDispatchesLocked(EventEntry* entry) { - InjectionState* injectionState = entry->injectionState; - if (injectionState) { - injectionState->pendingForegroundDispatches -= 1; - - if (injectionState->pendingForegroundDispatches == 0) { - mInjectionSyncFinishedCondition.broadcast(); - } - } -} - -const InputWindow* InputDispatcher::getWindowLocked(const sp<InputChannel>& inputChannel) { - for (size_t i = 0; i < mWindows.size(); i++) { - const InputWindow* window = & mWindows[i]; - if (window->inputChannel == inputChannel) { - return window; - } - } - return NULL; -} - -void InputDispatcher::setInputWindows(const Vector<InputWindow>& inputWindows) { -#if DEBUG_FOCUS - LOGD("setInputWindows"); -#endif - { // acquire lock - AutoMutex _l(mLock); - - // Clear old window pointers. - sp<InputChannel> oldFocusedWindowChannel; - if (mFocusedWindow) { - oldFocusedWindowChannel = mFocusedWindow->inputChannel; - mFocusedWindow = NULL; - } - - mWindows.clear(); - - // Loop over new windows and rebuild the necessary window pointers for - // tracking focus and touch. - mWindows.appendVector(inputWindows); - - size_t numWindows = mWindows.size(); - for (size_t i = 0; i < numWindows; i++) { - const InputWindow* window = & mWindows.itemAt(i); - if (window->hasFocus) { - mFocusedWindow = window; - break; - } - } - - if (oldFocusedWindowChannel != NULL) { - if (!mFocusedWindow || oldFocusedWindowChannel != mFocusedWindow->inputChannel) { -#if DEBUG_FOCUS - LOGD("Focus left window: %s", - oldFocusedWindowChannel->getName().string()); -#endif - synthesizeCancelationEventsForInputChannelLocked(oldFocusedWindowChannel, - InputState::CANCEL_NON_POINTER_EVENTS, "focus left window"); - oldFocusedWindowChannel.clear(); - } - } - if (mFocusedWindow && oldFocusedWindowChannel == NULL) { -#if DEBUG_FOCUS - LOGD("Focus entered window: %s", - mFocusedWindow->inputChannel->getName().string()); -#endif - } - - for (size_t i = 0; i < mTouchState.windows.size(); ) { - TouchedWindow& touchedWindow = mTouchState.windows.editItemAt(i); - const InputWindow* window = getWindowLocked(touchedWindow.channel); - if (window) { - touchedWindow.window = window; - i += 1; - } else { -#if DEBUG_FOCUS - LOGD("Touched window was removed: %s", touchedWindow.channel->getName().string()); -#endif - synthesizeCancelationEventsForInputChannelLocked(touchedWindow.channel, - InputState::CANCEL_POINTER_EVENTS, "touched window was removed"); - mTouchState.windows.removeAt(i); - } - } - -#if DEBUG_FOCUS - //logDispatchStateLocked(); -#endif - } // release lock - - // Wake up poll loop since it may need to make new input dispatching choices. - mLooper->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. - mLooper->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(); - } - - if (mDispatchEnabled && !enabled) { - resetAndDropEverythingLocked("dispatcher is being disabled"); - } - - 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. - mLooper->wake(); - } -} - -void InputDispatcher::resetAndDropEverythingLocked(const char* reason) { -#if DEBUG_FOCUS - LOGD("Resetting and dropping all events (%s).", reason); -#endif - - synthesizeCancelationEventsForAllConnectionsLocked(InputState::CANCEL_ALL_EVENTS, reason); - - resetKeyRepeatLocked(); - releasePendingEventLocked(); - drainInboundQueueLocked(); - resetTargetsLocked(); - - mTouchState.reset(); -} - -void InputDispatcher::logDispatchStateLocked() { - String8 dump; - dumpDispatchStateLocked(dump); - - char* text = dump.lockBuffer(dump.size()); - char* start = text; - while (*start != '\0') { - char* end = strchr(start, '\n'); - if (*end == '\n') { - *(end++) = '\0'; - } - LOGD("%s", start); - start = end; - } -} - -void InputDispatcher::dumpDispatchStateLocked(String8& dump) { - dump.appendFormat(INDENT "DispatchEnabled: %d\n", mDispatchEnabled); - dump.appendFormat(INDENT "DispatchFrozen: %d\n", mDispatchFrozen); - - if (mFocusedApplication) { - dump.appendFormat(INDENT "FocusedApplication: name='%s', dispatchingTimeout=%0.3fms\n", - mFocusedApplication->name.string(), - mFocusedApplication->dispatchingTimeout / 1000000.0); - } else { - dump.append(INDENT "FocusedApplication: <null>\n"); - } - dump.appendFormat(INDENT "FocusedWindow: name='%s'\n", - mFocusedWindow != NULL ? mFocusedWindow->name.string() : "<null>"); - - dump.appendFormat(INDENT "TouchDown: %s\n", toString(mTouchState.down)); - dump.appendFormat(INDENT "TouchSplit: %s\n", toString(mTouchState.split)); - if (!mTouchState.windows.isEmpty()) { - dump.append(INDENT "TouchedWindows:\n"); - for (size_t i = 0; i < mTouchState.windows.size(); i++) { - const TouchedWindow& touchedWindow = mTouchState.windows[i]; - dump.appendFormat(INDENT2 "%d: name='%s', pointerIds=0x%0x, targetFlags=0x%x\n", - i, touchedWindow.window->name.string(), touchedWindow.pointerIds.value, - touchedWindow.targetFlags); - } - } else { - dump.append(INDENT "TouchedWindows: <none>\n"); - } - - if (!mWindows.isEmpty()) { - dump.append(INDENT "Windows:\n"); - for (size_t i = 0; i < mWindows.size(); i++) { - const InputWindow& window = mWindows[i]; - dump.appendFormat(INDENT2 "%d: name='%s', paused=%s, hasFocus=%s, hasWallpaper=%s, " - "visible=%s, canReceiveKeys=%s, flags=0x%08x, type=0x%08x, layer=%d, " - "frame=[%d,%d][%d,%d], " - "visibleFrame=[%d,%d][%d,%d], " - "touchableArea=[%d,%d][%d,%d], " - "ownerPid=%d, ownerUid=%d, dispatchingTimeout=%0.3fms\n", - i, window.name.string(), - toString(window.paused), - toString(window.hasFocus), - toString(window.hasWallpaper), - toString(window.visible), - toString(window.canReceiveKeys), - window.layoutParamsFlags, window.layoutParamsType, - window.layer, - window.frameLeft, window.frameTop, - window.frameRight, window.frameBottom, - window.visibleFrameLeft, window.visibleFrameTop, - window.visibleFrameRight, window.visibleFrameBottom, - window.touchableAreaLeft, window.touchableAreaTop, - window.touchableAreaRight, window.touchableAreaBottom, - window.ownerPid, window.ownerUid, - window.dispatchingTimeout / 1000000.0); - } - } else { - dump.append(INDENT "Windows: <none>\n"); - } - - if (!mMonitoringChannels.isEmpty()) { - dump.append(INDENT "MonitoringChannels:\n"); - for (size_t i = 0; i < mMonitoringChannels.size(); i++) { - const sp<InputChannel>& channel = mMonitoringChannels[i]; - dump.appendFormat(INDENT2 "%d: '%s'\n", i, channel->getName().string()); - } - } else { - dump.append(INDENT "MonitoringChannels: <none>\n"); - } - - dump.appendFormat(INDENT "InboundQueue: length=%u\n", mInboundQueue.count()); - - if (!mActiveConnections.isEmpty()) { - dump.append(INDENT "ActiveConnections:\n"); - for (size_t i = 0; i < mActiveConnections.size(); i++) { - const Connection* connection = mActiveConnections[i]; - dump.appendFormat(INDENT2 "%d: '%s', status=%s, outboundQueueLength=%u" - "inputState.isNeutral=%s\n", - i, connection->getInputChannelName(), connection->getStatusLabel(), - connection->outboundQueue.count(), - toString(connection->inputState.isNeutral())); - } - } else { - dump.append(INDENT "ActiveConnections: <none>\n"); - } - - if (isAppSwitchPendingLocked()) { - dump.appendFormat(INDENT "AppSwitch: pending, due in %01.1fms\n", - (mAppSwitchDueTime - now()) / 1000000.0); - } else { - dump.append(INDENT "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 (getConnectionIndexLocked(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); - } - - mLooper->addFd(receiveFd, 0, ALOOPER_EVENT_INPUT, 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 = getConnectionIndexLocked(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; - } - } - - mLooper->removeFd(inputChannel->getReceivePipeFd()); - - nsecs_t currentTime = now(); - abortBrokenDispatchCycleLocked(currentTime, connection); - - runCommandsLockedInterruptible(); - } // release lock - - // Wake the poll loop because removing the connection may have changed the current - // synchronization state. - mLooper->wake(); - return OK; -} - -ssize_t InputDispatcher::getConnectionIndexLocked(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) { -} - -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::onANRLocked( - nsecs_t currentTime, const InputApplication* application, const InputWindow* window, - nsecs_t eventTime, nsecs_t waitStartTime) { - LOGI("Application is not responding: %s. " - "%01.1fms since event, %01.1fms since wait started", - getApplicationWindowLabelLocked(application, window).string(), - (currentTime - eventTime) / 1000000.0, - (currentTime - waitStartTime) / 1000000.0); - - CommandEntry* commandEntry = postCommandLocked( - & InputDispatcher::doNotifyANRLockedInterruptible); - if (application) { - commandEntry->inputApplicationHandle = application->handle; - } - if (window) { - commandEntry->inputChannel = window->inputChannel; - } -} - -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::doNotifyANRLockedInterruptible( - CommandEntry* commandEntry) { - mLock.unlock(); - - nsecs_t newTimeout = mPolicy->notifyANR( - commandEntry->inputApplicationHandle, commandEntry->inputChannel); - - mLock.lock(); - - resumeAfterTargetsNotReadyTimeoutLocked(newTimeout, commandEntry->inputChannel); -} - -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->userActivityEventType); - - mLock.lock(); -} - -void InputDispatcher::updateDispatchStatisticsLocked(nsecs_t currentTime, const EventEntry* entry, - int32_t injectionResult, nsecs_t timeSpentWaitingForApplication) { - // TODO Write some statistics about how long we spend waiting. -} - -void InputDispatcher::dump(String8& dump) { - dump.append("Input Dispatcher State:\n"); - dumpDispatchStateLocked(dump); -} - - -// --- InputDispatcher::Queue --- - -template <typename T> -uint32_t InputDispatcher::Queue<T>::count() const { - uint32_t result = 0; - for (const T* entry = headSentinel.next; entry != & tailSentinel; entry = entry->next) { - result += 1; - } - return result; -} - - -// --- InputDispatcher::Allocator --- - -InputDispatcher::Allocator::Allocator() { -} - -InputDispatcher::InjectionState* -InputDispatcher::Allocator::obtainInjectionState(int32_t injectorPid, int32_t injectorUid) { - InjectionState* injectionState = mInjectionStatePool.alloc(); - injectionState->refCount = 1; - injectionState->injectorPid = injectorPid; - injectionState->injectorUid = injectorUid; - injectionState->injectionIsAsync = false; - injectionState->injectionResult = INPUT_EVENT_INJECTION_PENDING; - injectionState->pendingForegroundDispatches = 0; - return injectionState; -} - -void InputDispatcher::Allocator::initializeEventEntry(EventEntry* entry, int32_t type, - nsecs_t eventTime, uint32_t policyFlags) { - entry->type = type; - entry->refCount = 1; - entry->dispatchInProgress = false; - entry->eventTime = eventTime; - entry->policyFlags = policyFlags; - entry->injectionState = NULL; -} - -void InputDispatcher::Allocator::releaseEventEntryInjectionState(EventEntry* entry) { - if (entry->injectionState) { - releaseInjectionState(entry->injectionState); - entry->injectionState = NULL; - } -} - -InputDispatcher::ConfigurationChangedEntry* -InputDispatcher::Allocator::obtainConfigurationChangedEntry(nsecs_t eventTime) { - ConfigurationChangedEntry* entry = mConfigurationChangeEntryPool.alloc(); - initializeEventEntry(entry, EventEntry::TYPE_CONFIGURATION_CHANGED, eventTime, 0); - 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, policyFlags); - - entry->deviceId = deviceId; - entry->source = source; - 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, policyFlags); - - entry->eventTime = eventTime; - entry->deviceId = deviceId; - entry->source = source; - 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) { - DispatchEntry* entry = mDispatchEntryPool.alloc(); - entry->eventEntry = eventEntry; - eventEntry->refCount += 1; - entry->targetFlags = targetFlags; - entry->xOffset = xOffset; - entry->yOffset = yOffset; - 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::releaseInjectionState(InjectionState* injectionState) { - injectionState->refCount -= 1; - if (injectionState->refCount == 0) { - mInjectionStatePool.free(injectionState); - } else { - assert(injectionState->refCount > 0); - } -} - -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) { - releaseEventEntryInjectionState(entry); - mConfigurationChangeEntryPool.free(entry); - } else { - assert(entry->refCount > 0); - } -} - -void InputDispatcher::Allocator::releaseKeyEntry(KeyEntry* entry) { - entry->refCount -= 1; - if (entry->refCount == 0) { - releaseEventEntryInjectionState(entry); - mKeyEntryPool.free(entry); - } else { - assert(entry->refCount > 0); - } -} - -void InputDispatcher::Allocator::releaseMotionEntry(MotionEntry* entry) { - entry->refCount -= 1; - if (entry->refCount == 0) { - releaseEventEntryInjectionState(entry); - 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; -} - -void InputDispatcher::Allocator::recycleKeyEntry(KeyEntry* keyEntry) { - releaseEventEntryInjectionState(keyEntry); - - keyEntry->dispatchInProgress = false; - keyEntry->syntheticRepeat = false; - keyEntry->interceptKeyResult = KeyEntry::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() { -} - -InputDispatcher::InputState::~InputState() { -} - -bool InputDispatcher::InputState::isNeutral() const { - return mKeyMementos.isEmpty() && mMotionMementos.isEmpty(); -} - -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); - 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); - 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(nsecs_t currentTime, - Allocator* allocator, Vector<EventEntry*>& outEvents, - CancelationOptions options) { - for (size_t i = 0; i < mKeyMementos.size(); ) { - const KeyMemento& memento = mKeyMementos.itemAt(i); - if (shouldCancelEvent(memento.source, options)) { - outEvents.push(allocator->obtainKeyEntry(currentTime, - memento.deviceId, memento.source, 0, - AKEY_EVENT_ACTION_UP, AKEY_EVENT_FLAG_CANCELED, - memento.keyCode, memento.scanCode, 0, 0, memento.downTime)); - mKeyMementos.removeAt(i); - } else { - i += 1; - } - } - - for (size_t i = 0; i < mMotionMementos.size(); ) { - const MotionMemento& memento = mMotionMementos.itemAt(i); - if (shouldCancelEvent(memento.source, options)) { - outEvents.push(allocator->obtainMotionEntry(currentTime, - memento.deviceId, memento.source, 0, - AMOTION_EVENT_ACTION_CANCEL, 0, 0, 0, - memento.xPrecision, memento.yPrecision, memento.downTime, - memento.pointerCount, memento.pointerIds, memento.pointerCoords)); - mMotionMementos.removeAt(i); - } else { - i += 1; - } - } -} - -void InputDispatcher::InputState::clear() { - mKeyMementos.clear(); - mMotionMementos.clear(); -} - -bool InputDispatcher::InputState::shouldCancelEvent(int32_t eventSource, - CancelationOptions options) { - switch (options) { - case CANCEL_POINTER_EVENTS: - return eventSource & AINPUT_SOURCE_CLASS_POINTER; - case CANCEL_NON_POINTER_EVENTS: - return !(eventSource & AINPUT_SOURCE_CLASS_POINTER); - default: - return true; - } -} - - -// --- InputDispatcher::Connection --- - -InputDispatcher::Connection::Connection(const sp<InputChannel>& inputChannel) : - status(STATUS_NORMAL), inputChannel(inputChannel), inputPublisher(inputChannel), - lastEventTime(LONG_LONG_MAX), lastDispatchTime(LONG_LONG_MAX) { -} - -InputDispatcher::Connection::~Connection() { -} - -status_t InputDispatcher::Connection::initialize() { - return inputPublisher.initialize(); -} - -const char* InputDispatcher::Connection::getStatusLabel() const { - switch (status) { - case STATUS_NORMAL: - return "NORMAL"; - - case STATUS_BROKEN: - return "BROKEN"; - - 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() { -} - - -// --- InputDispatcher::TouchState --- - -InputDispatcher::TouchState::TouchState() : - down(false), split(false) { -} - -InputDispatcher::TouchState::~TouchState() { -} - -void InputDispatcher::TouchState::reset() { - down = false; - split = false; - windows.clear(); -} - -void InputDispatcher::TouchState::copyFrom(const TouchState& other) { - down = other.down; - split = other.split; - windows.clear(); - windows.appendVector(other.windows); -} - -void InputDispatcher::TouchState::addOrUpdateWindow(const InputWindow* window, - int32_t targetFlags, BitSet32 pointerIds) { - if (targetFlags & InputTarget::FLAG_SPLIT) { - split = true; - } - - for (size_t i = 0; i < windows.size(); i++) { - TouchedWindow& touchedWindow = windows.editItemAt(i); - if (touchedWindow.window == window) { - touchedWindow.targetFlags |= targetFlags; - touchedWindow.pointerIds.value |= pointerIds.value; - return; - } - } - - windows.push(); - - TouchedWindow& touchedWindow = windows.editTop(); - touchedWindow.window = window; - touchedWindow.targetFlags = targetFlags; - touchedWindow.pointerIds = pointerIds; - touchedWindow.channel = window->inputChannel; -} - -void InputDispatcher::TouchState::removeOutsideTouchWindows() { - for (size_t i = 0 ; i < windows.size(); ) { - if (windows[i].targetFlags & InputTarget::FLAG_OUTSIDE) { - windows.removeAt(i); - } else { - i += 1; - } - } -} - -const InputWindow* InputDispatcher::TouchState::getFirstForegroundWindow() { - for (size_t i = 0; i < windows.size(); i++) { - if (windows[i].targetFlags & InputTarget::FLAG_FOREGROUND) { - return windows[i].window; - } - } - return NULL; -} - - -// --- 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 deleted file mode 100644 index 09fce38d9c..0000000000 --- a/libs/ui/InputManager.cpp +++ /dev/null @@ -1,83 +0,0 @@ -// -// 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 deleted file mode 100644 index 83b382b9a7..0000000000 --- a/libs/ui/InputReader.cpp +++ /dev/null @@ -1,3528 +0,0 @@ -// -// 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 " " -#define INDENT2 " " -#define INDENT3 " " -#define INDENT4 " " - -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); -} - -static inline const char* toString(bool value) { - return value ? "true" : "false"; -} - - -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), mDisableVirtualKeysTimeout(-1) { - 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->deviceId); - break; - - case EventHubInterface::DEVICE_REMOVED: - removeDevice(rawEvent->deviceId); - break; - - case EventHubInterface::FINISHED_DEVICE_SCAN: - handleConfigurationChanged(rawEvent->when); - break; - - default: - consumeEvent(rawEvent); - break; - } -} - -void InputReader::addDevice(int32_t deviceId) { - String8 name = mEventHub->getDeviceName(deviceId); - uint32_t classes = mEventHub->getDeviceClasses(deviceId); - - InputDevice* device = createDevice(deviceId, name, classes); - device->configure(); - - if (device->isIgnored()) { - LOGI("Device added: id=0x%x, name=%s (ignored non-input device)", deviceId, name.string()); - } else { - LOGI("Device added: id=0x%x, name=%s, sources=%08x", deviceId, name.string(), - 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; - } -} - -void InputReader::removeDevice(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; - } - - 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; -} - -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 (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::disableVirtualKeysUntil(nsecs_t time) { - mDisableVirtualKeysTimeout = time; -} - -bool InputReader::shouldDropVirtualKey(nsecs_t now, - InputDevice* device, int32_t keyCode, int32_t scanCode) { - if (now < mDisableVirtualKeysTimeout) { - LOGI("Dropping virtual key from device %s because virtual keys are " - "temporarily disabled for the next %0.3fms. keyCode=%d, scanCode=%d", - device->getName().string(), - (mDisableVirtualKeysTimeout - now) * 0.000001, - keyCode, scanCode); - return true; - } else { - return false; - } -} - -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) { - mEventHub->dump(dump); - dump.append("\n"); - - dump.append("Input Reader State:\n"); - - { // acquire device registry reader lock - RWLock::AutoRLock _rl(mDeviceRegistryLock); - - for (size_t i = 0; i < mDevices.size(); i++) { - mDevices.valueAt(i)->dump(dump); - } - } // release device registy reader lock -} - - -// --- 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(); -} - -static void dumpMotionRange(String8& dump, const InputDeviceInfo& deviceInfo, - int32_t rangeType, const char* name) { - const InputDeviceInfo::MotionRange* range = deviceInfo.getMotionRange(rangeType); - if (range) { - dump.appendFormat(INDENT3 "%s: min=%0.3f, max=%0.3f, flat=%0.3f, fuzz=%0.3f\n", - name, range->min, range->max, range->flat, range->fuzz); - } -} - -void InputDevice::dump(String8& dump) { - InputDeviceInfo deviceInfo; - getDeviceInfo(& deviceInfo); - - dump.appendFormat(INDENT "Device 0x%x: %s\n", deviceInfo.getId(), - deviceInfo.getName().string()); - dump.appendFormat(INDENT2 "Sources: 0x%08x\n", deviceInfo.getSources()); - dump.appendFormat(INDENT2 "KeyboardType: %d\n", deviceInfo.getKeyboardType()); - if (!deviceInfo.getMotionRanges().isEmpty()) { - dump.append(INDENT2 "Motion Ranges:\n"); - dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_X, "X"); - dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_Y, "Y"); - dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_PRESSURE, "Pressure"); - dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_SIZE, "Size"); - dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_TOUCH_MAJOR, "TouchMajor"); - dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_TOUCH_MINOR, "TouchMinor"); - dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_TOOL_MAJOR, "ToolMajor"); - dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_TOOL_MINOR, "ToolMinor"); - dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_ORIENTATION, "Orientation"); - } - - size_t numMappers = mMappers.size(); - for (size_t i = 0; i < numMappers; i++) { - InputMapper* mapper = mMappers[i]; - mapper->dump(dump); - } -} - -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::dump(String8& dump) { -} - -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; -} - - -// --- 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) { - getDispatcher()->notifySwitch(when, switchCode, switchValue, 0); -} - -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::dump(String8& dump) { - { // acquire lock - AutoMutex _l(mLock); - dump.append(INDENT2 "Keyboard Input Mapper:\n"); - dump.appendFormat(INDENT3 "AssociatedDisplayId: %d\n", mAssociatedDisplayId); - dump.appendFormat(INDENT3 "KeyboardType: %d\n", mKeyboardType); - dump.appendFormat(INDENT3 "KeyDowns: %d keys currently down\n", mLocked.keyDowns.size()); - dump.appendFormat(INDENT3 "MetaState: 0x%0x\n", mLocked.metaState); - dump.appendFormat(INDENT3 "DownTime: %lld\n", mLocked.downTime); - } // release lock -} - -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.itemAt(keyDownIndex).keyCode; - } else { - // key down - if ((policyFlags & POLICY_FLAG_VIRTUAL) - && mContext->shouldDropVirtualKey(when, getDevice(), keyCode, scanCode)) { - return; - } - - 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.itemAt(keyDownIndex).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(); - } - - getDispatcher()->notifyKey(when, getDeviceId(), AINPUT_SOURCE_KEYBOARD, policyFlags, - down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP, - AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, newMetaState, 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::dump(String8& dump) { - { // acquire lock - AutoMutex _l(mLock); - dump.append(INDENT2 "Trackball Input Mapper:\n"); - dump.appendFormat(INDENT3 "AssociatedDisplayId: %d\n", mAssociatedDisplayId); - dump.appendFormat(INDENT3 "XPrecision: %0.3f\n", mXPrecision); - dump.appendFormat(INDENT3 "YPrecision: %0.3f\n", mYPrecision); - dump.appendFormat(INDENT3 "Down: %s\n", toString(mLocked.down)); - dump.appendFormat(INDENT3 "DownTime: %lld\n", mLocked.downTime); - } // release lock -} - -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 - - int32_t metaState = mContext->getGlobalMetaState(); - int32_t pointerId = 0; - getDispatcher()->notifyMotion(when, getDeviceId(), AINPUT_SOURCE_TRACKBALL, 0, - motionEventAction, 0, metaState, AMOTION_EVENT_EDGE_FLAG_NONE, - 1, &pointerId, &pointerCoords, mXPrecision, mYPrecision, downTime); - - mAccumulator.clear(); -} - -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.haveTouchSize) { - info->addMotionRange(AINPUT_MOTION_RANGE_TOUCH_MAJOR, - mLocked.orientedRanges.touchMajor); - info->addMotionRange(AINPUT_MOTION_RANGE_TOUCH_MINOR, - mLocked.orientedRanges.touchMinor); - } - - if (mLocked.orientedRanges.haveToolSize) { - 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::dump(String8& dump) { - { // acquire lock - AutoMutex _l(mLock); - dump.append(INDENT2 "Touch Input Mapper:\n"); - dump.appendFormat(INDENT3 "AssociatedDisplayId: %d\n", mAssociatedDisplayId); - dumpParameters(dump); - dumpVirtualKeysLocked(dump); - dumpRawAxes(dump); - dumpCalibration(dump); - dumpSurfaceLocked(dump); - dump.appendFormat(INDENT3 "Translation and Scaling Factors:"); - dump.appendFormat(INDENT4 "XOrigin: %d\n", mLocked.xOrigin); - dump.appendFormat(INDENT4 "YOrigin: %d\n", mLocked.yOrigin); - dump.appendFormat(INDENT4 "XScale: %0.3f\n", mLocked.xScale); - dump.appendFormat(INDENT4 "YScale: %0.3f\n", mLocked.yScale); - dump.appendFormat(INDENT4 "XPrecision: %0.3f\n", mLocked.xPrecision); - dump.appendFormat(INDENT4 "YPrecision: %0.3f\n", mLocked.yPrecision); - dump.appendFormat(INDENT4 "GeometricScale: %0.3f\n", mLocked.geometricScale); - dump.appendFormat(INDENT4 "ToolSizeLinearScale: %0.3f\n", mLocked.toolSizeLinearScale); - dump.appendFormat(INDENT4 "ToolSizeLinearBias: %0.3f\n", mLocked.toolSizeLinearBias); - dump.appendFormat(INDENT4 "ToolSizeAreaScale: %0.3f\n", mLocked.toolSizeAreaScale); - dump.appendFormat(INDENT4 "ToolSizeAreaBias: %0.3f\n", mLocked.toolSizeAreaBias); - dump.appendFormat(INDENT4 "PressureScale: %0.3f\n", mLocked.pressureScale); - dump.appendFormat(INDENT4 "SizeScale: %0.3f\n", mLocked.sizeScale); - dump.appendFormat(INDENT4 "OrientationSCale: %0.3f\n", mLocked.orientationScale); - } // 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.haveTouchSize = false; - mLocked.orientedRanges.haveToolSize = false; - mLocked.orientedRanges.haveOrientation = false; -} - -void TouchInputMapper::configure() { - InputMapper::configure(); - - // Configure basic parameters. - configureParameters(); - - // Configure absolute axis information. - configureRawAxes(); - - // Prepare input device calibration. - parseCalibration(); - resolveCalibration(); - - { // 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(); - mParameters.virtualKeyQuietTime = getPolicy()->getVirtualKeyQuietTime(); -} - -void TouchInputMapper::dumpParameters(String8& dump) { - dump.appendFormat(INDENT3 "UseBadTouchFilter: %s\n", - toString(mParameters.useBadTouchFilter)); - dump.appendFormat(INDENT3 "UseAveragingTouchFilter: %s\n", - toString(mParameters.useAveragingTouchFilter)); - dump.appendFormat(INDENT3 "UseJumpyTouchFilter: %s\n", - toString(mParameters.useJumpyTouchFilter)); -} - -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 dumpAxisInfo(String8& dump, RawAbsoluteAxisInfo axis, const char* name) { - if (axis.valid) { - dump.appendFormat(INDENT4 "%s: min=%d, max=%d, flat=%d, fuzz=%d\n", - name, axis.minValue, axis.maxValue, axis.flat, axis.fuzz); - } else { - dump.appendFormat(INDENT4 "%s: unknown range\n", name); - } -} - -void TouchInputMapper::dumpRawAxes(String8& dump) { - dump.append(INDENT3 "Raw Axes:\n"); - dumpAxisInfo(dump, mRawAxes.x, "X"); - dumpAxisInfo(dump, mRawAxes.y, "Y"); - dumpAxisInfo(dump, mRawAxes.pressure, "Pressure"); - dumpAxisInfo(dump, mRawAxes.touchMajor, "TouchMajor"); - dumpAxisInfo(dump, mRawAxes.touchMinor, "TouchMinor"); - dumpAxisInfo(dump, mRawAxes.toolMajor, "ToolMajor"); - dumpAxisInfo(dump, mRawAxes.toolMinor, "ToolMinor"); - dumpAxisInfo(dump, 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: id=0x%x, name=%s, display size is now %dx%d", - getDeviceId(), getDeviceName().string(), width, 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.touchSizeCalibration != Calibration::TOUCH_SIZE_CALIBRATION_NONE) { - mLocked.orientedRanges.haveTouchSize = 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. - mLocked.toolSizeLinearScale = 0; - mLocked.toolSizeLinearBias = 0; - mLocked.toolSizeAreaScale = 0; - mLocked.toolSizeAreaBias = 0; - if (mCalibration.toolSizeCalibration != Calibration::TOOL_SIZE_CALIBRATION_NONE) { - if (mCalibration.toolSizeCalibration == Calibration::TOOL_SIZE_CALIBRATION_LINEAR) { - if (mCalibration.haveToolSizeLinearScale) { - mLocked.toolSizeLinearScale = mCalibration.toolSizeLinearScale; - } else if (mRawAxes.toolMajor.valid && mRawAxes.toolMajor.maxValue != 0) { - mLocked.toolSizeLinearScale = float(min(width, height)) - / mRawAxes.toolMajor.maxValue; - } - - if (mCalibration.haveToolSizeLinearBias) { - mLocked.toolSizeLinearBias = mCalibration.toolSizeLinearBias; - } - } else if (mCalibration.toolSizeCalibration == - Calibration::TOOL_SIZE_CALIBRATION_AREA) { - if (mCalibration.haveToolSizeLinearScale) { - mLocked.toolSizeLinearScale = mCalibration.toolSizeLinearScale; - } else { - mLocked.toolSizeLinearScale = min(width, height); - } - - if (mCalibration.haveToolSizeLinearBias) { - mLocked.toolSizeLinearBias = mCalibration.toolSizeLinearBias; - } - - if (mCalibration.haveToolSizeAreaScale) { - mLocked.toolSizeAreaScale = mCalibration.toolSizeAreaScale; - } else if (mRawAxes.toolMajor.valid && mRawAxes.toolMajor.maxValue != 0) { - mLocked.toolSizeAreaScale = 1.0f / mRawAxes.toolMajor.maxValue; - } - - if (mCalibration.haveToolSizeAreaBias) { - mLocked.toolSizeAreaBias = mCalibration.toolSizeAreaBias; - } - } - - mLocked.orientedRanges.haveToolSize = 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. - mLocked.pressureScale = 0; - 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(); - } - - 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. - mLocked.sizeScale = 0; - if (mCalibration.sizeCalibration != Calibration::SIZE_CALIBRATION_NONE) { - 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 - mLocked.orientationScale = 0; - if (mCalibration.orientationCalibration != Calibration::ORIENTATION_CALIBRATION_NONE) { - 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; - } - - return true; -} - -void TouchInputMapper::dumpSurfaceLocked(String8& dump) { - dump.appendFormat(INDENT3 "SurfaceWidth: %dpx\n", mLocked.surfaceWidth); - dump.appendFormat(INDENT3 "SurfaceHeight: %dpx\n", mLocked.surfaceHeight); - dump.appendFormat(INDENT3 "SurfaceOrientation: %d\n", mLocked.surfaceOrientation); -} - -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; - - } -} - -void TouchInputMapper::dumpVirtualKeysLocked(String8& dump) { - if (!mLocked.virtualKeys.isEmpty()) { - dump.append(INDENT3 "Virtual Keys:\n"); - - for (size_t i = 0; i < mLocked.virtualKeys.size(); i++) { - const VirtualKey& virtualKey = mLocked.virtualKeys.itemAt(i); - dump.appendFormat(INDENT4 "%d: scanCode=%d, keyCode=%d, " - "hitLeft=%d, hitRight=%d, hitTop=%d, hitBottom=%d\n", - i, virtualKey.scanCode, virtualKey.keyCode, - virtualKey.hitLeft, virtualKey.hitRight, - virtualKey.hitTop, virtualKey.hitBottom); - } - } -} - -void TouchInputMapper::parseCalibration() { - const InputDeviceCalibration& in = getDevice()->getCalibration(); - Calibration& out = mCalibration; - - // Touch Size - out.touchSizeCalibration = Calibration::TOUCH_SIZE_CALIBRATION_DEFAULT; - String8 touchSizeCalibrationString; - if (in.tryGetProperty(String8("touch.touchSize.calibration"), touchSizeCalibrationString)) { - if (touchSizeCalibrationString == "none") { - out.touchSizeCalibration = Calibration::TOUCH_SIZE_CALIBRATION_NONE; - } else if (touchSizeCalibrationString == "geometric") { - out.touchSizeCalibration = Calibration::TOUCH_SIZE_CALIBRATION_GEOMETRIC; - } else if (touchSizeCalibrationString == "pressure") { - out.touchSizeCalibration = Calibration::TOUCH_SIZE_CALIBRATION_PRESSURE; - } else if (touchSizeCalibrationString != "default") { - LOGW("Invalid value for touch.touchSize.calibration: '%s'", - touchSizeCalibrationString.string()); - } - } - - // Tool Size - out.toolSizeCalibration = Calibration::TOOL_SIZE_CALIBRATION_DEFAULT; - String8 toolSizeCalibrationString; - if (in.tryGetProperty(String8("touch.toolSize.calibration"), toolSizeCalibrationString)) { - if (toolSizeCalibrationString == "none") { - out.toolSizeCalibration = Calibration::TOOL_SIZE_CALIBRATION_NONE; - } else if (toolSizeCalibrationString == "geometric") { - out.toolSizeCalibration = Calibration::TOOL_SIZE_CALIBRATION_GEOMETRIC; - } else if (toolSizeCalibrationString == "linear") { - out.toolSizeCalibration = Calibration::TOOL_SIZE_CALIBRATION_LINEAR; - } else if (toolSizeCalibrationString == "area") { - out.toolSizeCalibration = Calibration::TOOL_SIZE_CALIBRATION_AREA; - } else if (toolSizeCalibrationString != "default") { - LOGW("Invalid value for touch.toolSize.calibration: '%s'", - toolSizeCalibrationString.string()); - } - } - - out.haveToolSizeLinearScale = in.tryGetProperty(String8("touch.toolSize.linearScale"), - out.toolSizeLinearScale); - out.haveToolSizeLinearBias = in.tryGetProperty(String8("touch.toolSize.linearBias"), - out.toolSizeLinearBias); - out.haveToolSizeAreaScale = in.tryGetProperty(String8("touch.toolSize.areaScale"), - out.toolSizeAreaScale); - out.haveToolSizeAreaBias = in.tryGetProperty(String8("touch.toolSize.areaBias"), - out.toolSizeAreaBias); - out.haveToolSizeIsSummed = in.tryGetProperty(String8("touch.toolSize.isSummed"), - out.toolSizeIsSummed); - - // Pressure - out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_DEFAULT; - String8 pressureCalibrationString; - if (in.tryGetProperty(String8("touch.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 touch.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("touch.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 touch.size.calibration: '%s'", - sizeCalibrationString.string()); - } - } - - // Orientation - out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_DEFAULT; - String8 orientationCalibrationString; - if (in.tryGetProperty(String8("touch.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 touch.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 Size - switch (mCalibration.toolSizeCalibration) { - case Calibration::TOOL_SIZE_CALIBRATION_DEFAULT: - if (mRawAxes.toolMajor.valid) { - mCalibration.toolSizeCalibration = Calibration::TOOL_SIZE_CALIBRATION_LINEAR; - } else { - mCalibration.toolSizeCalibration = Calibration::TOOL_SIZE_CALIBRATION_NONE; - } - break; - - default: - break; - } - - // Touch Size - switch (mCalibration.touchSizeCalibration) { - case Calibration::TOUCH_SIZE_CALIBRATION_DEFAULT: - if (mCalibration.pressureCalibration != Calibration::PRESSURE_CALIBRATION_NONE - && mCalibration.toolSizeCalibration != Calibration::TOOL_SIZE_CALIBRATION_NONE) { - mCalibration.touchSizeCalibration = Calibration::TOUCH_SIZE_CALIBRATION_PRESSURE; - } else { - mCalibration.touchSizeCalibration = Calibration::TOUCH_SIZE_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::dumpCalibration(String8& dump) { - dump.append(INDENT3 "Calibration:\n"); - - // Touch Size - switch (mCalibration.touchSizeCalibration) { - case Calibration::TOUCH_SIZE_CALIBRATION_NONE: - dump.append(INDENT4 "touch.touchSize.calibration: none\n"); - break; - case Calibration::TOUCH_SIZE_CALIBRATION_GEOMETRIC: - dump.append(INDENT4 "touch.touchSize.calibration: geometric\n"); - break; - case Calibration::TOUCH_SIZE_CALIBRATION_PRESSURE: - dump.append(INDENT4 "touch.touchSize.calibration: pressure\n"); - break; - default: - assert(false); - } - - // Tool Size - switch (mCalibration.toolSizeCalibration) { - case Calibration::TOOL_SIZE_CALIBRATION_NONE: - dump.append(INDENT4 "touch.toolSize.calibration: none\n"); - break; - case Calibration::TOOL_SIZE_CALIBRATION_GEOMETRIC: - dump.append(INDENT4 "touch.toolSize.calibration: geometric\n"); - break; - case Calibration::TOOL_SIZE_CALIBRATION_LINEAR: - dump.append(INDENT4 "touch.toolSize.calibration: linear\n"); - break; - case Calibration::TOOL_SIZE_CALIBRATION_AREA: - dump.append(INDENT4 "touch.toolSize.calibration: area\n"); - break; - default: - assert(false); - } - - if (mCalibration.haveToolSizeLinearScale) { - dump.appendFormat(INDENT4 "touch.toolSize.linearScale: %0.3f\n", - mCalibration.toolSizeLinearScale); - } - - if (mCalibration.haveToolSizeLinearBias) { - dump.appendFormat(INDENT4 "touch.toolSize.linearBias: %0.3f\n", - mCalibration.toolSizeLinearBias); - } - - if (mCalibration.haveToolSizeAreaScale) { - dump.appendFormat(INDENT4 "touch.toolSize.areaScale: %0.3f\n", - mCalibration.toolSizeAreaScale); - } - - if (mCalibration.haveToolSizeAreaBias) { - dump.appendFormat(INDENT4 "touch.toolSize.areaBias: %0.3f\n", - mCalibration.toolSizeAreaBias); - } - - if (mCalibration.haveToolSizeIsSummed) { - dump.appendFormat(INDENT4 "touch.toolSize.isSummed: %d\n", - mCalibration.toolSizeIsSummed); - } - - // Pressure - switch (mCalibration.pressureCalibration) { - case Calibration::PRESSURE_CALIBRATION_NONE: - dump.append(INDENT4 "touch.pressure.calibration: none\n"); - break; - case Calibration::PRESSURE_CALIBRATION_PHYSICAL: - dump.append(INDENT4 "touch.pressure.calibration: physical\n"); - break; - case Calibration::PRESSURE_CALIBRATION_AMPLITUDE: - dump.append(INDENT4 "touch.pressure.calibration: amplitude\n"); - break; - default: - assert(false); - } - - switch (mCalibration.pressureSource) { - case Calibration::PRESSURE_SOURCE_PRESSURE: - dump.append(INDENT4 "touch.pressure.source: pressure\n"); - break; - case Calibration::PRESSURE_SOURCE_TOUCH: - dump.append(INDENT4 "touch.pressure.source: touch\n"); - break; - case Calibration::PRESSURE_SOURCE_DEFAULT: - break; - default: - assert(false); - } - - if (mCalibration.havePressureScale) { - dump.appendFormat(INDENT4 "touch.pressure.scale: %0.3f\n", - mCalibration.pressureScale); - } - - // Size - switch (mCalibration.sizeCalibration) { - case Calibration::SIZE_CALIBRATION_NONE: - dump.append(INDENT4 "touch.size.calibration: none\n"); - break; - case Calibration::SIZE_CALIBRATION_NORMALIZED: - dump.append(INDENT4 "touch.size.calibration: normalized\n"); - break; - default: - assert(false); - } - - // Orientation - switch (mCalibration.orientationCalibration) { - case Calibration::ORIENTATION_CALIBRATION_NONE: - dump.append(INDENT4 "touch.orientation.calibration: none\n"); - break; - case Calibration::ORIENTATION_CALIBRATION_INTERPOLATED: - dump.append(INDENT4 "touch.orientation.calibration: interpolated\n"); - 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) { - uint32_t policyFlags = 0; - - // 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) { - detectGestures(when); - 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", - mLocked.currentVirtualKey.keyCode, mLocked.currentVirtualKey.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", - mLocked.currentVirtualKey.keyCode, mLocked.currentVirtualKey.scanCode); -#endif - keyEventAction = AKEY_EVENT_ACTION_UP; - keyEventFlags = AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY - | AKEY_EVENT_FLAG_CANCELED; - - // Check whether the pointer moved inside the display area where we should - // start a new stroke. - int32_t x = mCurrentTouch.pointers[0].x; - int32_t y = mCurrentTouch.pointers[0].y; - if (isPointInsideSurfaceLocked(x, y)) { - mLastTouch.clear(); - touchResult = DISPATCH_TOUCH; - } else { - touchResult = DROP_STROKE; - } - } 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) { - if (mContext->shouldDropVirtualKey(when, getDevice(), - virtualKey->keyCode, virtualKey->scanCode)) { - return DROP_STROKE; - } - - 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", - mLocked.currentVirtualKey.keyCode, - mLocked.currentVirtualKey.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. - int32_t metaState = mContext->getGlobalMetaState(); - policyFlags |= POLICY_FLAG_VIRTUAL; - getDispatcher()->notifyKey(when, getDeviceId(), AINPUT_SOURCE_KEYBOARD, policyFlags, - keyEventAction, keyEventFlags, keyCode, scanCode, metaState, downTime); - return touchResult; -} - -void TouchInputMapper::detectGestures(nsecs_t when) { - // Disable all virtual key touches that happen within a short time interval of the - // most recent touch. The idea is to filter out stray virtual key presses when - // interacting with the touch screen. - // - // Problems we're trying to solve: - // - // 1. While scrolling a list or dragging the window shade, the user swipes down into a - // virtual key area that is implemented by a separate touch panel and accidentally - // triggers a virtual key. - // - // 2. While typing in the on screen keyboard, the user taps slightly outside the screen - // area and accidentally triggers a virtual key. This often happens when virtual keys - // are layed out below the screen near to where the on screen keyboard's space bar - // is displayed. - if (mParameters.virtualKeyQuietTime > 0 && mCurrentTouch.pointerCount != 0) { - mContext->disableVirtualKeysUntil(when + mParameters.virtualKeyQuietTime); - } -} - -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 and pointers moving - // all at the same time. - BitSet32 upIdBits(lastIdBits.value & ~ currentIdBits.value); - BitSet32 downIdBits(currentIdBits.value & ~ lastIdBits.value); - BitSet32 activeIdBits(lastIdBits.value); - uint32_t pointerCount = lastPointerCount; - - // Produce an intermediate representation of the touch data that consists of the - // old location of pointers that have just gone up and the new location of pointers that - // have just moved but omits the location of pointers that have just gone down. - TouchData interimTouch; - interimTouch.copyFrom(mLastTouch); - - BitSet32 moveIdBits(lastIdBits.value & currentIdBits.value); - bool moveNeeded = false; - while (!moveIdBits.isEmpty()) { - uint32_t moveId = moveIdBits.firstMarkedBit(); - moveIdBits.clearBit(moveId); - - int32_t oldIndex = mLastTouch.idToIndex[moveId]; - int32_t newIndex = mCurrentTouch.idToIndex[moveId]; - if (mLastTouch.pointers[oldIndex] != mCurrentTouch.pointers[newIndex]) { - interimTouch.pointers[oldIndex] = mCurrentTouch.pointers[newIndex]; - moveNeeded = true; - } - } - - // Dispatch pointer up events using the interim pointer locations. - 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, &interimTouch, - oldActiveIdBits, upId, pointerCount, motionEventAction); - pointerCount -= 1; - } - - // Dispatch move events if any of the remaining pointers moved from their old locations. - // Although applications receive new locations as part of individual pointer up - // events, they do not generally handle them except when presented in a move event. - if (moveNeeded) { - dispatchTouch(when, policyFlags, &mCurrentTouch, - activeIdBits, -1, pointerCount, AMOTION_EVENT_ACTION_MOVE); - } - - // Dispatch pointer down events using the new pointer locations. - 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.toolSizeCalibration) { - case Calibration::TOOL_SIZE_CALIBRATION_GEOMETRIC: - toolMajor = in.toolMajor * mLocked.geometricScale; - if (mRawAxes.toolMinor.valid) { - toolMinor = in.toolMinor * mLocked.geometricScale; - } else { - toolMinor = toolMajor; - } - break; - case Calibration::TOOL_SIZE_CALIBRATION_LINEAR: - toolMajor = in.toolMajor != 0 - ? in.toolMajor * mLocked.toolSizeLinearScale + mLocked.toolSizeLinearBias - : 0; - if (mRawAxes.toolMinor.valid) { - toolMinor = in.toolMinor != 0 - ? in.toolMinor * mLocked.toolSizeLinearScale - + mLocked.toolSizeLinearBias - : 0; - } else { - toolMinor = toolMajor; - } - break; - case Calibration::TOOL_SIZE_CALIBRATION_AREA: - if (in.toolMajor != 0) { - float diameter = sqrtf(in.toolMajor - * mLocked.toolSizeAreaScale + mLocked.toolSizeAreaBias); - toolMajor = diameter * mLocked.toolSizeLinearScale + mLocked.toolSizeLinearBias; - } else { - toolMajor = 0; - } - toolMinor = toolMajor; - break; - default: - toolMajor = 0; - toolMinor = 0; - break; - } - - if (mCalibration.haveToolSizeIsSummed && mCalibration.toolSizeIsSummed) { - 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.touchSizeCalibration) { - case Calibration::TOUCH_SIZE_CALIBRATION_GEOMETRIC: - touchMajor = in.touchMajor * mLocked.geometricScale; - if (mRawAxes.touchMinor.valid) { - touchMinor = in.touchMinor * mLocked.geometricScale; - } else { - touchMinor = touchMajor; - } - break; - case Calibration::TOUCH_SIZE_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(), getSources(), 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 pressure to indicate - // a pointer going 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", - 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 index 2c6346ebee..83d955682a 100644 --- a/libs/ui/InputTransport.cpp +++ b/libs/ui/InputTransport.cpp @@ -35,8 +35,12 @@ static const int DEFAULT_MESSAGE_BUFFER_SIZE = 16384; 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'; +// consuming the most recent message and it handled it. +static const char INPUT_SIGNAL_FINISHED_HANDLED = 'f'; + +// Signal sent by the consumer to the producer to inform it that it has finished +// consuming the most recent message but it did not handle it. +static const char INPUT_SIGNAL_FINISHED_UNHANDLED = 'u'; // --- InputChannel --- @@ -497,7 +501,7 @@ status_t InputPublisher::sendDispatchSignal() { return mChannel->sendSignal(INPUT_SIGNAL_DISPATCH); } -status_t InputPublisher::receiveFinishedSignal() { +status_t InputPublisher::receiveFinishedSignal(bool* outHandled) { #if DEBUG_TRANSPORT_ACTIONS LOGD("channel '%s' publisher ~ receiveFinishedSignal", mChannel->getName().string()); @@ -506,9 +510,14 @@ status_t InputPublisher::receiveFinishedSignal() { char signal; status_t result = mChannel->receiveSignal(& signal); if (result) { + *outHandled = false; return result; } - if (signal != INPUT_SIGNAL_FINISHED) { + if (signal == INPUT_SIGNAL_FINISHED_HANDLED) { + *outHandled = true; + } else if (signal == INPUT_SIGNAL_FINISHED_UNHANDLED) { + *outHandled = false; + } else { LOGE("channel '%s' publisher ~ Received unexpected signal '%c' from consumer", mChannel->getName().string(), signal); return UNKNOWN_ERROR; @@ -626,13 +635,15 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, InputEvent* return OK; } -status_t InputConsumer::sendFinishedSignal() { +status_t InputConsumer::sendFinishedSignal(bool handled) { #if DEBUG_TRANSPORT_ACTIONS - LOGD("channel '%s' consumer ~ sendFinishedSignal", - mChannel->getName().string()); + LOGD("channel '%s' consumer ~ sendFinishedSignal: handled=%d", + mChannel->getName().string(), handled); #endif - return mChannel->sendSignal(INPUT_SIGNAL_FINISHED); + return mChannel->sendSignal(handled + ? INPUT_SIGNAL_FINISHED_HANDLED + : INPUT_SIGNAL_FINISHED_UNHANDLED); } status_t InputConsumer::receiveDispatchSignal() { diff --git a/libs/ui/KeyCharacterMap.cpp b/libs/ui/KeyCharacterMap.cpp index e891181c82..2decfe9321 100644 --- a/libs/ui/KeyCharacterMap.cpp +++ b/libs/ui/KeyCharacterMap.cpp @@ -1,263 +1,849 @@ -#define LOG_TAG "KeyCharacterMap" +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ -#include <ui/KeyCharacterMap.h> -#include <cutils/properties.h> +#define LOG_TAG "KeyCharacterMap" -#include <utils/Log.h> -#include <sys/types.h> -#include <unistd.h> #include <stdlib.h> -#include <fcntl.h> -#include <limits.h> #include <string.h> +#include <android/keycodes.h> +#include <ui/Keyboard.h> +#include <ui/KeyCharacterMap.h> +#include <utils/Log.h> +#include <utils/Errors.h> +#include <utils/Tokenizer.h> +#include <utils/Timers.h> + +// Enables debug output for the parser. +#define DEBUG_PARSER 0 + +// Enables debug output for parser performance. +#define DEBUG_PARSER_PERFORMANCE 0 + +// Enables debug output for mapping. +#define DEBUG_MAPPING 0 + + +namespace android { -struct Header -{ - char magic[8]; - unsigned int endian; - unsigned int version; - unsigned int keycount; - unsigned char kbdtype; - char padding[11]; +static const char* WHITESPACE = " \t\r"; +static const char* WHITESPACE_OR_PROPERTY_DELIMITER = " \t\r,:"; + +struct Modifier { + const char* label; + int32_t metaState; +}; +static const Modifier modifiers[] = { + { "shift", AMETA_SHIFT_ON }, + { "lshift", AMETA_SHIFT_LEFT_ON }, + { "rshift", AMETA_SHIFT_RIGHT_ON }, + { "alt", AMETA_ALT_ON }, + { "lalt", AMETA_ALT_LEFT_ON }, + { "ralt", AMETA_ALT_RIGHT_ON }, + { "ctrl", AMETA_CTRL_ON }, + { "lctrl", AMETA_CTRL_LEFT_ON }, + { "rctrl", AMETA_CTRL_RIGHT_ON }, + { "meta", AMETA_META_ON }, + { "lmeta", AMETA_META_LEFT_ON }, + { "rmeta", AMETA_META_RIGHT_ON }, + { "sym", AMETA_SYM_ON }, + { "fn", AMETA_FUNCTION_ON }, + { "capslock", AMETA_CAPS_LOCK_ON }, + { "numlock", AMETA_NUM_LOCK_ON }, + { "scrolllock", AMETA_SCROLL_LOCK_ON }, }; -KeyCharacterMap::KeyCharacterMap() -{ +#if DEBUG_MAPPING +static String8 toString(const char16_t* chars, size_t numChars) { + String8 result; + for (size_t i = 0; i < numChars; i++) { + result.appendFormat(i == 0 ? "%d" : ", %d", chars[i]); + } + return result; } +#endif -KeyCharacterMap::~KeyCharacterMap() -{ - free(m_keys); + +// --- KeyCharacterMap --- + +KeyCharacterMap::KeyCharacterMap() : + mType(KEYBOARD_TYPE_UNKNOWN) { } -unsigned short -KeyCharacterMap::get(int keycode, int meta) -{ - Key* k = find_key(keycode); - if (k != NULL) { - return k->data[meta & META_MASK]; +KeyCharacterMap::~KeyCharacterMap() { + for (size_t i = 0; i < mKeys.size(); i++) { + Key* key = mKeys.editValueAt(i); + delete key; } - return 0; } -unsigned short -KeyCharacterMap::getNumber(int keycode) -{ - Key* k = find_key(keycode); - if (k != NULL) { - return k->number; +status_t KeyCharacterMap::load(const String8& filename, KeyCharacterMap** outMap) { + *outMap = NULL; + + Tokenizer* tokenizer; + status_t status = Tokenizer::open(filename, &tokenizer); + if (status) { + LOGE("Error %d opening key character map file %s.", status, filename.string()); + } else { + KeyCharacterMap* map = new KeyCharacterMap(); + if (!map) { + LOGE("Error allocating key character map."); + status = NO_MEMORY; + } else { +#if DEBUG_PARSER_PERFORMANCE + nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC); +#endif + Parser parser(map, tokenizer); + status = parser.parse(); +#if DEBUG_PARSER_PERFORMANCE + nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime; + LOGD("Parsed key character map file '%s' %d lines in %0.3fms.", + tokenizer->getFilename().string(), tokenizer->getLineNumber(), + elapsedTime / 1000000.0); +#endif + if (status) { + delete map; + } else { + *outMap = map; + } + } + delete tokenizer; } - return 0; + return status; } -unsigned short -KeyCharacterMap::getMatch(int keycode, const unsigned short* chars, - int charsize, uint32_t modifiers) -{ - Key* k = find_key(keycode); - modifiers &= 3; // ignore the SYM key because we don't have keymap entries for it - if (k != NULL) { - const uint16_t* data = k->data; - for (int j=0; j<charsize; j++) { - uint16_t c = chars[j]; - for (int i=0; i<(META_MASK + 1); i++) { - if ((modifiers == 0) || ((modifiers & i) != 0)) { - if (c == data[i]) { - return c; +status_t KeyCharacterMap::loadByDeviceId(int32_t deviceId, KeyCharacterMap** outMap) { + *outMap = NULL; + + String8 filename; + status_t result = getKeyCharacterMapFile(deviceId, filename); + if (!result) { + result = load(filename, outMap); + } + return result; +} + +int32_t KeyCharacterMap::getKeyboardType() const { + return mType; +} + +char16_t KeyCharacterMap::getDisplayLabel(int32_t keyCode) const { + char16_t result = 0; + const Key* key; + if (getKey(keyCode, &key)) { + result = key->label; + } +#if DEBUG_MAPPING + LOGD("getDisplayLabel: keyCode=%d ~ Result %d.", keyCode, result); +#endif + return result; +} + +char16_t KeyCharacterMap::getNumber(int32_t keyCode) const { + char16_t result = 0; + const Key* key; + if (getKey(keyCode, &key)) { + result = key->number; + } +#if DEBUG_MAPPING + LOGD("getNumber: keyCode=%d ~ Result %d.", keyCode, result); +#endif + return result; +} + +char16_t KeyCharacterMap::getCharacter(int32_t keyCode, int32_t metaState) const { + char16_t result = 0; + const Key* key; + const Behavior* behavior; + if (getKeyBehavior(keyCode, metaState, &key, &behavior)) { + result = behavior->character; + } +#if DEBUG_MAPPING + LOGD("getCharacter: keyCode=%d, metaState=0x%08x ~ Result %d.", keyCode, metaState, result); +#endif + return result; +} + +bool KeyCharacterMap::getFallbackAction(int32_t keyCode, int32_t metaState, + FallbackAction* outFallbackAction) const { + outFallbackAction->keyCode = 0; + outFallbackAction->metaState = 0; + + bool result = false; + const Key* key; + const Behavior* behavior; + if (getKeyBehavior(keyCode, metaState, &key, &behavior)) { + if (behavior->fallbackKeyCode) { + outFallbackAction->keyCode = behavior->fallbackKeyCode; + outFallbackAction->metaState = metaState & ~behavior->metaState; + result = true; + } + } +#if DEBUG_MAPPING + LOGD("getFallbackKeyCode: keyCode=%d, metaState=0x%08x ~ Result %s, " + "fallback keyCode=%d, fallback metaState=0x%08x.", + keyCode, metaState, result ? "true" : "false", + outFallbackAction->keyCode, outFallbackAction->metaState); +#endif + return result; +} + +char16_t KeyCharacterMap::getMatch(int32_t keyCode, const char16_t* chars, size_t numChars, + int32_t metaState) const { + char16_t result = 0; + const Key* key; + if (getKey(keyCode, &key)) { + // Try to find the most general behavior that maps to this character. + // For example, the base key behavior will usually be last in the list. + // However, if we find a perfect meta state match for one behavior then use that one. + for (const Behavior* behavior = key->firstBehavior; behavior; behavior = behavior->next) { + if (behavior->character) { + for (size_t i = 0; i < numChars; i++) { + if (behavior->character == chars[i]) { + result = behavior->character; + if ((behavior->metaState & metaState) == behavior->metaState) { + goto ExactMatch; + } + break; } } } } + ExactMatch: ; } - return 0; +#if DEBUG_MAPPING + LOGD("getMatch: keyCode=%d, chars=[%s], metaState=0x%08x ~ Result %d.", + keyCode, toString(chars, numChars).string(), metaState, result); +#endif + return result; } -unsigned short -KeyCharacterMap::getDisplayLabel(int keycode) -{ - Key* k = find_key(keycode); - if (k != NULL) { - return k->display_label; +bool KeyCharacterMap::getEvents(int32_t deviceId, const char16_t* chars, size_t numChars, + Vector<KeyEvent>& outEvents) const { + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + + for (size_t i = 0; i < numChars; i++) { + int32_t keyCode, metaState; + char16_t ch = chars[i]; + if (!findKey(ch, &keyCode, &metaState)) { +#if DEBUG_MAPPING + LOGD("getEvents: deviceId=%d, chars=[%s] ~ Failed to find mapping for character %d.", + deviceId, toString(chars, numChars).string(), ch); +#endif + return false; + } + + int32_t currentMetaState = 0; + addMetaKeys(outEvents, deviceId, metaState, true, now, ¤tMetaState); + addKey(outEvents, deviceId, keyCode, currentMetaState, true, now); + addKey(outEvents, deviceId, keyCode, currentMetaState, false, now); + addMetaKeys(outEvents, deviceId, metaState, false, now, ¤tMetaState); + } +#if DEBUG_MAPPING + LOGD("getEvents: deviceId=%d, chars=[%s] ~ Generated %d events.", + deviceId, toString(chars, numChars).string(), int32_t(outEvents.size())); + for (size_t i = 0; i < outEvents.size(); i++) { + LOGD(" Key: keyCode=%d, metaState=0x%08x, %s.", + outEvents[i].getKeyCode(), outEvents[i].getMetaState(), + outEvents[i].getAction() == AKEY_EVENT_ACTION_DOWN ? "down" : "up"); } - return 0; +#endif + return true; } -bool -KeyCharacterMap::getKeyData(int keycode, unsigned short *displayLabel, - unsigned short *number, unsigned short* results) -{ - Key* k = find_key(keycode); - if (k != NULL) { - memcpy(results, k->data, sizeof(short)*(META_MASK + 1)); - *number = k->number; - *displayLabel = k->display_label; +bool KeyCharacterMap::getKey(int32_t keyCode, const Key** outKey) const { + ssize_t index = mKeys.indexOfKey(keyCode); + if (index >= 0) { + *outKey = mKeys.valueAt(index); return true; - } else { - return false; } + return false; } -bool -KeyCharacterMap::find_char(uint16_t c, uint32_t* key, uint32_t* mods) -{ - uint32_t N = m_keyCount; - for (int j=0; j<(META_MASK + 1); j++) { - Key const* keys = m_keys; - for (uint32_t i=0; i<N; i++) { - if (keys->data[j] == c) { - *key = keys->keycode; - *mods = j; +bool KeyCharacterMap::getKeyBehavior(int32_t keyCode, int32_t metaState, + const Key** outKey, const Behavior** outBehavior) const { + const Key* key; + if (getKey(keyCode, &key)) { + const Behavior* behavior = key->firstBehavior; + while (behavior) { + if ((behavior->metaState & metaState) == behavior->metaState) { + *outKey = key; + *outBehavior = behavior; return true; } - keys++; + behavior = behavior->next; } } return false; } -bool -KeyCharacterMap::getEvents(uint16_t* chars, size_t len, - Vector<int32_t>* keys, Vector<uint32_t>* modifiers) -{ - for (size_t i=0; i<len; i++) { - uint32_t k, mods; - if (find_char(chars[i], &k, &mods)) { - keys->add(k); - modifiers->add(mods); - } else { - return false; +bool KeyCharacterMap::findKey(char16_t ch, int32_t* outKeyCode, int32_t* outMetaState) const { + if (!ch) { + return false; + } + + for (size_t i = 0; i < mKeys.size(); i++) { + const Key* key = mKeys.valueAt(i); + + // Try to find the most general behavior that maps to this character. + // For example, the base key behavior will usually be last in the list. + const Behavior* found = NULL; + for (const Behavior* behavior = key->firstBehavior; behavior; behavior = behavior->next) { + if (behavior->character == ch) { + found = behavior; + } + } + if (found) { + *outKeyCode = mKeys.keyAt(i); + *outMetaState = found->metaState; + return true; } } - return true; + return false; } -KeyCharacterMap::Key* -KeyCharacterMap::find_key(int keycode) -{ - Key* keys = m_keys; - int low = 0; - int high = m_keyCount - 1; - int mid; - int n; - while (low <= high) { - mid = (low + high) / 2; - n = keys[mid].keycode; - if (keycode < n) { - high = mid - 1; - } else if (keycode > n) { - low = mid + 1; - } else { - return keys + mid; - } +void KeyCharacterMap::addKey(Vector<KeyEvent>& outEvents, + int32_t deviceId, int32_t keyCode, int32_t metaState, bool down, nsecs_t time) { + outEvents.push(); + KeyEvent& event = outEvents.editTop(); + event.initialize(deviceId, AINPUT_SOURCE_KEYBOARD, + down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP, + 0, keyCode, 0, metaState, 0, time, time); +} + +void KeyCharacterMap::addMetaKeys(Vector<KeyEvent>& outEvents, + int32_t deviceId, int32_t metaState, bool down, nsecs_t time, + int32_t* currentMetaState) { + // Add and remove meta keys symmetrically. + if (down) { + addLockedMetaKey(outEvents, deviceId, metaState, time, + AKEYCODE_CAPS_LOCK, AMETA_CAPS_LOCK_ON, currentMetaState); + addLockedMetaKey(outEvents, deviceId, metaState, time, + AKEYCODE_NUM_LOCK, AMETA_NUM_LOCK_ON, currentMetaState); + addLockedMetaKey(outEvents, deviceId, metaState, time, + AKEYCODE_SCROLL_LOCK, AMETA_SCROLL_LOCK_ON, currentMetaState); + + addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, true, time, + AKEYCODE_SHIFT_LEFT, AMETA_SHIFT_LEFT_ON, + AKEYCODE_SHIFT_RIGHT, AMETA_SHIFT_RIGHT_ON, + AMETA_SHIFT_ON, currentMetaState); + addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, true, time, + AKEYCODE_ALT_LEFT, AMETA_ALT_LEFT_ON, + AKEYCODE_ALT_RIGHT, AMETA_ALT_RIGHT_ON, + AMETA_ALT_ON, currentMetaState); + addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, true, time, + AKEYCODE_CTRL_LEFT, AMETA_CTRL_LEFT_ON, + AKEYCODE_CTRL_RIGHT, AMETA_CTRL_RIGHT_ON, + AMETA_CTRL_ON, currentMetaState); + addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, true, time, + AKEYCODE_META_LEFT, AMETA_META_LEFT_ON, + AKEYCODE_META_RIGHT, AMETA_META_RIGHT_ON, + AMETA_META_ON, currentMetaState); + + addSingleEphemeralMetaKey(outEvents, deviceId, metaState, true, time, + AKEYCODE_SYM, AMETA_SYM_ON, currentMetaState); + addSingleEphemeralMetaKey(outEvents, deviceId, metaState, true, time, + AKEYCODE_FUNCTION, AMETA_FUNCTION_ON, currentMetaState); + } else { + addSingleEphemeralMetaKey(outEvents, deviceId, metaState, false, time, + AKEYCODE_FUNCTION, AMETA_FUNCTION_ON, currentMetaState); + addSingleEphemeralMetaKey(outEvents, deviceId, metaState, false, time, + AKEYCODE_SYM, AMETA_SYM_ON, currentMetaState); + + addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, false, time, + AKEYCODE_META_LEFT, AMETA_META_LEFT_ON, + AKEYCODE_META_RIGHT, AMETA_META_RIGHT_ON, + AMETA_META_ON, currentMetaState); + addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, false, time, + AKEYCODE_CTRL_LEFT, AMETA_CTRL_LEFT_ON, + AKEYCODE_CTRL_RIGHT, AMETA_CTRL_RIGHT_ON, + AMETA_CTRL_ON, currentMetaState); + addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, false, time, + AKEYCODE_ALT_LEFT, AMETA_ALT_LEFT_ON, + AKEYCODE_ALT_RIGHT, AMETA_ALT_RIGHT_ON, + AMETA_ALT_ON, currentMetaState); + addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, false, time, + AKEYCODE_SHIFT_LEFT, AMETA_SHIFT_LEFT_ON, + AKEYCODE_SHIFT_RIGHT, AMETA_SHIFT_RIGHT_ON, + AMETA_SHIFT_ON, currentMetaState); + + addLockedMetaKey(outEvents, deviceId, metaState, time, + AKEYCODE_SCROLL_LOCK, AMETA_SCROLL_LOCK_ON, currentMetaState); + addLockedMetaKey(outEvents, deviceId, metaState, time, + AKEYCODE_NUM_LOCK, AMETA_NUM_LOCK_ON, currentMetaState); + addLockedMetaKey(outEvents, deviceId, metaState, time, + AKEYCODE_CAPS_LOCK, AMETA_CAPS_LOCK_ON, currentMetaState); + } +} + +bool KeyCharacterMap::addSingleEphemeralMetaKey(Vector<KeyEvent>& outEvents, + int32_t deviceId, int32_t metaState, bool down, nsecs_t time, + int32_t keyCode, int32_t keyMetaState, + int32_t* currentMetaState) { + if ((metaState & keyMetaState) == keyMetaState) { + *currentMetaState = updateMetaState(keyCode, down, *currentMetaState); + addKey(outEvents, deviceId, keyCode, *currentMetaState, down, time); + return true; + } + return false; +} + +void KeyCharacterMap::addDoubleEphemeralMetaKey(Vector<KeyEvent>& outEvents, + int32_t deviceId, int32_t metaState, bool down, nsecs_t time, + int32_t leftKeyCode, int32_t leftKeyMetaState, + int32_t rightKeyCode, int32_t rightKeyMetaState, + int32_t eitherKeyMetaState, + int32_t* currentMetaState) { + bool specific = false; + specific |= addSingleEphemeralMetaKey(outEvents, deviceId, metaState, down, time, + leftKeyCode, leftKeyMetaState, currentMetaState); + specific |= addSingleEphemeralMetaKey(outEvents, deviceId, metaState, down, time, + rightKeyCode, rightKeyMetaState, currentMetaState); + + if (!specific) { + addSingleEphemeralMetaKey(outEvents, deviceId, metaState, down, time, + leftKeyCode, eitherKeyMetaState, currentMetaState); + } +} + +void KeyCharacterMap::addLockedMetaKey(Vector<KeyEvent>& outEvents, + int32_t deviceId, int32_t metaState, nsecs_t time, + int32_t keyCode, int32_t keyMetaState, + int32_t* currentMetaState) { + if ((metaState & keyMetaState) == keyMetaState) { + *currentMetaState = updateMetaState(keyCode, true, *currentMetaState); + addKey(outEvents, deviceId, keyCode, *currentMetaState, true, time); + *currentMetaState = updateMetaState(keyCode, false, *currentMetaState); + addKey(outEvents, deviceId, keyCode, *currentMetaState, false, time); + } +} + + +// --- KeyCharacterMap::Key --- + +KeyCharacterMap::Key::Key() : + label(0), number(0), firstBehavior(NULL) { +} + +KeyCharacterMap::Key::~Key() { + Behavior* behavior = firstBehavior; + while (behavior) { + Behavior* next = behavior->next; + delete behavior; + behavior = next; } - return NULL; } -KeyCharacterMap* -KeyCharacterMap::load(int id) -{ - KeyCharacterMap* rv = NULL; - char path[PATH_MAX]; - char propName[100]; - char dev[PROPERTY_VALUE_MAX]; - char tmpfn[PROPERTY_VALUE_MAX]; - int err; - const char* root = getenv("ANDROID_ROOT"); - - sprintf(propName, "hw.keyboards.%u.devname", id); - err = property_get(propName, dev, ""); - if (err > 0) { - // replace all the spaces with underscores - strcpy(tmpfn, dev); - for (char *p = strchr(tmpfn, ' '); p && *p; p = strchr(tmpfn, ' ')) - *p = '_'; - snprintf(path, sizeof(path), "%s/usr/keychars/%s.kcm.bin", root, tmpfn); - //LOGD("load: dev='%s' path='%s'\n", dev, path); - rv = try_file(path); - if (rv != NULL) { - return rv; + +// --- KeyCharacterMap::Behavior --- + +KeyCharacterMap::Behavior::Behavior() : + next(NULL), metaState(0), character(0), fallbackKeyCode(0) { +} + + +// --- KeyCharacterMap::Parser --- + +KeyCharacterMap::Parser::Parser(KeyCharacterMap* map, Tokenizer* tokenizer) : + mMap(map), mTokenizer(tokenizer), mState(STATE_TOP) { +} + +KeyCharacterMap::Parser::~Parser() { +} + +status_t KeyCharacterMap::Parser::parse() { + while (!mTokenizer->isEof()) { +#if DEBUG_PARSER + LOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(), + mTokenizer->peekRemainderOfLine().string()); +#endif + + mTokenizer->skipDelimiters(WHITESPACE); + + if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') { + switch (mState) { + case STATE_TOP: { + String8 keywordToken = mTokenizer->nextToken(WHITESPACE); + if (keywordToken == "type") { + mTokenizer->skipDelimiters(WHITESPACE); + status_t status = parseType(); + if (status) return status; + } else if (keywordToken == "key") { + mTokenizer->skipDelimiters(WHITESPACE); + status_t status = parseKey(); + if (status) return status; + } else { + LOGE("%s: Expected keyword, got '%s'.", mTokenizer->getLocation().string(), + keywordToken.string()); + return BAD_VALUE; + } + break; + } + + case STATE_KEY: { + status_t status = parseKeyProperty(); + if (status) return status; + break; + } + } + + mTokenizer->skipDelimiters(WHITESPACE); + if (!mTokenizer->isEol()) { + LOGE("%s: Expected end of line, got '%s'.", + mTokenizer->getLocation().string(), + mTokenizer->peekRemainderOfLine().string()); + return BAD_VALUE; + } } - LOGW("Error loading keycharmap file '%s'. %s='%s'", path, propName, dev); - } else { - LOGW("No keyboard for id %d", id); + + mTokenizer->nextLine(); + } + + if (mState != STATE_TOP) { + LOGE("%s: Unterminated key description at end of file.", + mTokenizer->getLocation().string()); + return BAD_VALUE; } - snprintf(path, sizeof(path), "%s/usr/keychars/qwerty.kcm.bin", root); - rv = try_file(path); - if (rv == NULL) { - LOGE("Can't find any keycharmaps (also tried %s)", path); - return NULL; + if (mMap->mType == KEYBOARD_TYPE_UNKNOWN) { + LOGE("%s: Missing required keyboard 'type' declaration.", + mTokenizer->getLocation().string()); + return BAD_VALUE; } - LOGW("Using default keymap: %s", path); - return rv; + return NO_ERROR; } -KeyCharacterMap* -KeyCharacterMap::try_file(const char* filename) -{ - KeyCharacterMap* rv = NULL; - Key* keys; - int fd; - off_t filesize; - Header header; - int err; - - fd = open(filename, O_RDONLY); - if (fd == -1) { - LOGW("Can't open keycharmap file"); - return NULL; +status_t KeyCharacterMap::Parser::parseType() { + if (mMap->mType != KEYBOARD_TYPE_UNKNOWN) { + LOGE("%s: Duplicate keyboard 'type' declaration.", + mTokenizer->getLocation().string()); + return BAD_VALUE; } - filesize = lseek(fd, 0, SEEK_END); - lseek(fd, 0, SEEK_SET); + KeyboardType type; + String8 typeToken = mTokenizer->nextToken(WHITESPACE); + if (typeToken == "NUMERIC") { + type = KEYBOARD_TYPE_NUMERIC; + } else if (typeToken == "PREDICTIVE") { + type = KEYBOARD_TYPE_PREDICTIVE; + } else if (typeToken == "ALPHA") { + type = KEYBOARD_TYPE_ALPHA; + } else if (typeToken == "FULL") { + type = KEYBOARD_TYPE_FULL; + } else if (typeToken == "SPECIAL_FUNCTION") { + type = KEYBOARD_TYPE_SPECIAL_FUNCTION; + } else { + LOGE("%s: Expected keyboard type label, got '%s'.", mTokenizer->getLocation().string(), + typeToken.string()); + return BAD_VALUE; + } - // validate the header - if (filesize <= (off_t)sizeof(header)) { - LOGW("Bad keycharmap - filesize=%d\n", (int)filesize); - goto cleanup1; +#if DEBUG_PARSER + LOGD("Parsed type: type=%d.", type); +#endif + mMap->mType = type; + return NO_ERROR; +} + +status_t KeyCharacterMap::Parser::parseKey() { + String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE); + int32_t keyCode = getKeyCodeByLabel(keyCodeToken.string()); + if (!keyCode) { + LOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().string(), + keyCodeToken.string()); + return BAD_VALUE; + } + if (mMap->mKeys.indexOfKey(keyCode) >= 0) { + LOGE("%s: Duplicate entry for key code '%s'.", mTokenizer->getLocation().string(), + keyCodeToken.string()); + return BAD_VALUE; } - err = read(fd, &header, sizeof(header)); - if (err == -1) { - LOGW("Error reading keycharmap file"); - goto cleanup1; + mTokenizer->skipDelimiters(WHITESPACE); + String8 openBraceToken = mTokenizer->nextToken(WHITESPACE); + if (openBraceToken != "{") { + LOGE("%s: Expected '{' after key code label, got '%s'.", + mTokenizer->getLocation().string(), openBraceToken.string()); + return BAD_VALUE; } - if (0 != memcmp(header.magic, "keychar", 8)) { - LOGW("Bad keycharmap magic token"); - goto cleanup1; +#if DEBUG_PARSER + LOGD("Parsed beginning of key: keyCode=%d.", keyCode); +#endif + mKeyCode = keyCode; + mMap->mKeys.add(keyCode, new Key()); + mState = STATE_KEY; + return NO_ERROR; +} + +status_t KeyCharacterMap::Parser::parseKeyProperty() { + String8 token = mTokenizer->nextToken(WHITESPACE_OR_PROPERTY_DELIMITER); + if (token == "}") { + mState = STATE_TOP; + return NO_ERROR; } - if (header.endian != 0x12345678) { - LOGW("Bad keycharmap endians"); - goto cleanup1; + + Vector<Property> properties; + + // Parse all comma-delimited property names up to the first colon. + for (;;) { + if (token == "label") { + properties.add(Property(PROPERTY_LABEL)); + } else if (token == "number") { + properties.add(Property(PROPERTY_NUMBER)); + } else { + int32_t metaState; + status_t status = parseModifier(token, &metaState); + if (status) { + LOGE("%s: Expected a property name or modifier, got '%s'.", + mTokenizer->getLocation().string(), token.string()); + return status; + } + properties.add(Property(PROPERTY_META, metaState)); + } + + mTokenizer->skipDelimiters(WHITESPACE); + if (!mTokenizer->isEol()) { + char ch = mTokenizer->nextChar(); + if (ch == ':') { + break; + } else if (ch == ',') { + mTokenizer->skipDelimiters(WHITESPACE); + token = mTokenizer->nextToken(WHITESPACE_OR_PROPERTY_DELIMITER); + continue; + } + } + + LOGE("%s: Expected ',' or ':' after property name.", + mTokenizer->getLocation().string()); + return BAD_VALUE; } - if ((header.version & 0xff) != 2) { - LOGW("Only support keycharmap version 2 (got 0x%08x)", header.version); - goto cleanup1; + + // Parse behavior after the colon. + mTokenizer->skipDelimiters(WHITESPACE); + + Behavior behavior; + bool haveCharacter = false; + bool haveFallback = false; + + do { + char ch = mTokenizer->peekChar(); + if (ch == '\'') { + char16_t character; + status_t status = parseCharacterLiteral(&character); + if (status || !character) { + LOGE("%s: Invalid character literal for key.", + mTokenizer->getLocation().string()); + return BAD_VALUE; + } + if (haveCharacter) { + LOGE("%s: Cannot combine multiple character literals or 'none'.", + mTokenizer->getLocation().string()); + return BAD_VALUE; + } + behavior.character = character; + haveCharacter = true; + } else { + token = mTokenizer->nextToken(WHITESPACE); + if (token == "none") { + if (haveCharacter) { + LOGE("%s: Cannot combine multiple character literals or 'none'.", + mTokenizer->getLocation().string()); + return BAD_VALUE; + } + haveCharacter = true; + } else if (token == "fallback") { + mTokenizer->skipDelimiters(WHITESPACE); + token = mTokenizer->nextToken(WHITESPACE); + int32_t keyCode = getKeyCodeByLabel(token.string()); + if (!keyCode) { + LOGE("%s: Invalid key code label for fallback behavior, got '%s'.", + mTokenizer->getLocation().string(), + token.string()); + return BAD_VALUE; + } + if (haveFallback) { + LOGE("%s: Cannot combine multiple fallback key codes.", + mTokenizer->getLocation().string()); + return BAD_VALUE; + } + behavior.fallbackKeyCode = keyCode; + haveFallback = true; + } else { + LOGE("%s: Expected a key behavior after ':'.", + mTokenizer->getLocation().string()); + return BAD_VALUE; + } + } + + mTokenizer->skipDelimiters(WHITESPACE); + } while (!mTokenizer->isEol()); + + // Add the behavior. + Key* key = mMap->mKeys.valueFor(mKeyCode); + for (size_t i = 0; i < properties.size(); i++) { + const Property& property = properties.itemAt(i); + switch (property.property) { + case PROPERTY_LABEL: + if (key->label) { + LOGE("%s: Duplicate label for key.", + mTokenizer->getLocation().string()); + return BAD_VALUE; + } + key->label = behavior.character; +#if DEBUG_PARSER + LOGD("Parsed key label: keyCode=%d, label=%d.", mKeyCode, key->label); +#endif + break; + case PROPERTY_NUMBER: + if (key->number) { + LOGE("%s: Duplicate number for key.", + mTokenizer->getLocation().string()); + return BAD_VALUE; + } + key->number = behavior.character; +#if DEBUG_PARSER + LOGD("Parsed key number: keyCode=%d, number=%d.", mKeyCode, key->number); +#endif + break; + case PROPERTY_META: { + for (Behavior* b = key->firstBehavior; b; b = b->next) { + if (b->metaState == property.metaState) { + LOGE("%s: Duplicate key behavior for modifier.", + mTokenizer->getLocation().string()); + return BAD_VALUE; + } + } + Behavior* newBehavior = new Behavior(behavior); + newBehavior->metaState = property.metaState; + newBehavior->next = key->firstBehavior; + key->firstBehavior = newBehavior; +#if DEBUG_PARSER + LOGD("Parsed key meta: keyCode=%d, meta=0x%x, char=%d, fallback=%d.", mKeyCode, + newBehavior->metaState, newBehavior->character, newBehavior->fallbackKeyCode); +#endif + break; + } + } + } + return NO_ERROR; +} + +status_t KeyCharacterMap::Parser::parseModifier(const String8& token, int32_t* outMetaState) { + if (token == "base") { + *outMetaState = 0; + return NO_ERROR; } - if (filesize < (off_t)(sizeof(Header)+(sizeof(Key)*header.keycount))) { - LOGW("Bad keycharmap file size\n"); - goto cleanup1; + + int32_t combinedMeta = 0; + + const char* str = token.string(); + const char* start = str; + for (const char* cur = str; ; cur++) { + char ch = *cur; + if (ch == '+' || ch == '\0') { + size_t len = cur - start; + int32_t metaState = 0; + for (size_t i = 0; i < sizeof(modifiers) / sizeof(Modifier); i++) { + if (strlen(modifiers[i].label) == len + && strncmp(modifiers[i].label, start, len) == 0) { + metaState = modifiers[i].metaState; + break; + } + } + if (!metaState) { + return BAD_VALUE; + } + if (combinedMeta & metaState) { + LOGE("%s: Duplicate modifier combination '%s'.", + mTokenizer->getLocation().string(), token.string()); + return BAD_VALUE; + } + + combinedMeta |= metaState; + start = cur + 1; + + if (ch == '\0') { + break; + } + } } + *outMetaState = combinedMeta; + return NO_ERROR; +} - // read the key data - keys = (Key*)malloc(sizeof(Key)*header.keycount); - err = read(fd, keys, sizeof(Key)*header.keycount); - if (err == -1) { - LOGW("Error reading keycharmap file"); - free(keys); - goto cleanup1; +status_t KeyCharacterMap::Parser::parseCharacterLiteral(char16_t* outCharacter) { + char ch = mTokenizer->nextChar(); + if (ch != '\'') { + goto Error; } - // return the object - rv = new KeyCharacterMap; - rv->m_keyCount = header.keycount; - rv->m_keys = keys; - rv->m_type = header.kbdtype; + ch = mTokenizer->nextChar(); + if (ch == '\\') { + // Escape sequence. + ch = mTokenizer->nextChar(); + if (ch == 'n') { + *outCharacter = '\n'; + } else if (ch == 't') { + *outCharacter = '\t'; + } else if (ch == '\\') { + *outCharacter = '\\'; + } else if (ch == '\'') { + *outCharacter = '\''; + } else if (ch == '"') { + *outCharacter = '"'; + } else if (ch == 'u') { + *outCharacter = 0; + for (int i = 0; i < 4; i++) { + ch = mTokenizer->nextChar(); + int digit; + if (ch >= '0' && ch <= '9') { + digit = ch - '0'; + } else if (ch >= 'A' && ch <= 'F') { + digit = ch - 'A' + 10; + } else if (ch >= 'a' && ch <= 'f') { + digit = ch - 'a' + 10; + } else { + goto Error; + } + *outCharacter = (*outCharacter << 4) | digit; + } + } else { + goto Error; + } + } else if (ch >= 32 && ch <= 126 && ch != '\'') { + // ASCII literal character. + *outCharacter = ch; + } else { + goto Error; + } -cleanup1: - close(fd); + ch = mTokenizer->nextChar(); + if (ch != '\'') { + goto Error; + } - return rv; + // Ensure that we consumed the entire token. + if (mTokenizer->nextToken(WHITESPACE).isEmpty()) { + return NO_ERROR; + } + +Error: + LOGE("%s: Malformed character literal.", mTokenizer->getLocation().string()); + return BAD_VALUE; } + +} // namespace android diff --git a/libs/ui/KeyLayoutMap.cpp b/libs/ui/KeyLayoutMap.cpp index 15ae54c6f6..56bc26fd6d 100644 --- a/libs/ui/KeyLayoutMap.cpp +++ b/libs/ui/KeyLayoutMap.cpp @@ -1,234 +1,213 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + #define LOG_TAG "KeyLayoutMap" -#include "KeyLayoutMap.h" -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <unistd.h> -#include <errno.h> -#include <utils/String8.h> #include <stdlib.h> -#include <ui/KeycodeLabels.h> +#include <android/keycodes.h> +#include <ui/Keyboard.h> +#include <ui/KeyLayoutMap.h> #include <utils/Log.h> +#include <utils/Errors.h> +#include <utils/Tokenizer.h> +#include <utils/Timers.h> + +// Enables debug output for the parser. +#define DEBUG_PARSER 0 + +// Enables debug output for parser performance. +#define DEBUG_PARSER_PERFORMANCE 0 + +// Enables debug output for mapping. +#define DEBUG_MAPPING 0 + namespace android { -KeyLayoutMap::KeyLayoutMap() - :m_status(NO_INIT), - m_keys() -{ -} +static const char* WHITESPACE = " \t\r"; -KeyLayoutMap::~KeyLayoutMap() -{ +// --- KeyLayoutMap --- + +KeyLayoutMap::KeyLayoutMap() { } -static String8 -next_token(char const** p, int *line) -{ - bool begun = false; - const char* begin = *p; - const char* end = *p; - while (true) { - if (*end == '\n') { - (*line)++; - } - switch (*end) - { - case '#': - if (begun) { - *p = end; - return String8(begin, end-begin); - } else { - do { - begin++; - end++; - } while (*begin != '\0' && *begin != '\n'); - } - case '\0': - case ' ': - case '\n': - case '\r': - case '\t': - if (begun || (*end == '\0')) { - *p = end; - return String8(begin, end-begin); - } else { - begin++; - end++; - break; - } - default: - end++; - begun = true; - } - } +KeyLayoutMap::~KeyLayoutMap() { } -static int32_t -token_to_value(const char *literal, const KeycodeLabel *list) -{ - while (list->literal) { - if (0 == strcmp(literal, list->literal)) { - return list->value; +status_t KeyLayoutMap::load(const String8& filename, KeyLayoutMap** outMap) { + *outMap = NULL; + + Tokenizer* tokenizer; + status_t status = Tokenizer::open(filename, &tokenizer); + if (status) { + LOGE("Error %d opening key layout map file %s.", status, filename.string()); + } else { + KeyLayoutMap* map = new KeyLayoutMap(); + if (!map) { + LOGE("Error allocating key layout map."); + status = NO_MEMORY; + } else { +#if DEBUG_PARSER_PERFORMANCE + nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC); +#endif + Parser parser(map, tokenizer); + status = parser.parse(); +#if DEBUG_PARSER_PERFORMANCE + nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime; + LOGD("Parsed key layout map file '%s' %d lines in %0.3fms.", + tokenizer->getFilename().string(), tokenizer->getLineNumber(), + elapsedTime / 1000000.0); +#endif + if (status) { + delete map; + } else { + *outMap = map; + } } - list++; + delete tokenizer; } - return list->value; + return status; } -status_t -KeyLayoutMap::load(const char* filename) -{ - int fd = open(filename, O_RDONLY); - if (fd < 0) { - LOGE("error opening file=%s err=%s\n", filename, strerror(errno)); - m_status = errno; - return errno; +status_t KeyLayoutMap::map(int32_t scanCode, int32_t* keyCode, uint32_t* flags) const { + ssize_t index = mKeys.indexOfKey(scanCode); + if (index < 0) { +#if DEBUG_MAPPING + LOGD("map: scanCode=%d ~ Failed.", scanCode); +#endif + *keyCode = AKEYCODE_UNKNOWN; + *flags = 0; + return NAME_NOT_FOUND; } - off_t len = lseek(fd, 0, SEEK_END); - off_t errlen = lseek(fd, 0, SEEK_SET); - if (len < 0 || errlen < 0) { - close(fd); - LOGE("error seeking file=%s err=%s\n", filename, strerror(errno)); - m_status = errno; - return errno; - } + const Key& k = mKeys.valueAt(index); + *keyCode = k.keyCode; + *flags = k.flags; - char* buf = (char*)malloc(len+1); - if (read(fd, buf, len) != len) { - LOGE("error reading file=%s err=%s\n", filename, strerror(errno)); - m_status = errno != 0 ? errno : ((int)NOT_ENOUGH_DATA); - return errno != 0 ? errno : ((int)NOT_ENOUGH_DATA); - } - errno = 0; - buf[len] = '\0'; +#if DEBUG_MAPPING + LOGD("map: scanCode=%d ~ Result keyCode=%d, flags=0x%08x.", scanCode, *keyCode, *flags); +#endif + return NO_ERROR; +} - int32_t scancode = -1; - int32_t keycode = -1; - uint32_t flags = 0; - uint32_t tmp; - char* end; - status_t err = NO_ERROR; - int line = 1; - char const* p = buf; - enum { BEGIN, SCANCODE, KEYCODE, FLAG } state = BEGIN; - while (true) { - String8 token = next_token(&p, &line); - if (*p == '\0') { - break; - } - switch (state) - { - case BEGIN: - if (token == "key") { - state = SCANCODE; - } else { - LOGE("%s:%d: expected key, got '%s'\n", filename, line, - token.string()); - err = BAD_VALUE; - goto done; - } - break; - case SCANCODE: - scancode = strtol(token.string(), &end, 0); - if (*end != '\0') { - LOGE("%s:%d: expected scancode (a number), got '%s'\n", - filename, line, token.string()); - goto done; - } - //LOGI("%s:%d: got scancode %d\n", filename, line, scancode ); - state = KEYCODE; - break; - case KEYCODE: - keycode = token_to_value(token.string(), KEYCODES); - //LOGI("%s:%d: got keycode %d for %s\n", filename, line, keycode, token.string() ); - if (keycode == 0) { - LOGE("%s:%d: expected keycode, got '%s'\n", - filename, line, token.string()); - goto done; - } - state = FLAG; - break; - case FLAG: - if (token == "key") { - if (scancode != -1) { - //LOGI("got key decl scancode=%d keycode=%d" - // " flags=0x%08x\n", scancode, keycode, flags); - Key k = { keycode, flags }; - m_keys.add(scancode, k); - state = SCANCODE; - scancode = -1; - keycode = -1; - flags = 0; - break; - } - } - tmp = token_to_value(token.string(), FLAGS); - //LOGI("%s:%d: got flags %x for %s\n", filename, line, tmp, token.string() ); - if (tmp == 0) { - LOGE("%s:%d: expected flag, got '%s'\n", - filename, line, token.string()); - goto done; - } - flags |= tmp; - break; +status_t KeyLayoutMap::findScanCodes(int32_t keyCode, Vector<int32_t>* outScanCodes) const { + const size_t N = mKeys.size(); + for (size_t i=0; i<N; i++) { + if (mKeys.valueAt(i).keyCode == keyCode) { + outScanCodes->add(mKeys.keyAt(i)); } } - if (state == FLAG && scancode != -1 ) { - //LOGI("got key decl scancode=%d keycode=%d" - // " flags=0x%08x\n", scancode, keycode, flags); - Key k = { keycode, flags }; - m_keys.add(scancode, k); - } - -done: - free(buf); - close(fd); - - m_status = err; - return err; + return NO_ERROR; } -status_t -KeyLayoutMap::map(int32_t scancode, int32_t *keycode, uint32_t *flags) const -{ - if (m_status != NO_ERROR) { - return m_status; - } +// --- KeyLayoutMap::Parser --- - ssize_t index = m_keys.indexOfKey(scancode); - if (index < 0) { - //LOGW("couldn't map scancode=%d\n", scancode); - return NAME_NOT_FOUND; - } - - const Key& k = m_keys.valueAt(index); +KeyLayoutMap::Parser::Parser(KeyLayoutMap* map, Tokenizer* tokenizer) : + mMap(map), mTokenizer(tokenizer) { +} - *keycode = k.keycode; - *flags = k.flags; +KeyLayoutMap::Parser::~Parser() { +} - //LOGD("mapped scancode=%d to keycode=%d flags=0x%08x\n", scancode, - // keycode, flags); +status_t KeyLayoutMap::Parser::parse() { + while (!mTokenizer->isEof()) { +#if DEBUG_PARSER + LOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(), + mTokenizer->peekRemainderOfLine().string()); +#endif + + mTokenizer->skipDelimiters(WHITESPACE); + + if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') { + String8 keywordToken = mTokenizer->nextToken(WHITESPACE); + if (keywordToken == "key") { + mTokenizer->skipDelimiters(WHITESPACE); + status_t status = parseKey(); + if (status) return status; + } else { + LOGE("%s: Expected keyword, got '%s'.", mTokenizer->getLocation().string(), + keywordToken.string()); + return BAD_VALUE; + } + + mTokenizer->skipDelimiters(WHITESPACE); + if (!mTokenizer->isEol()) { + LOGE("%s: Expected end of line, got '%s'.", + mTokenizer->getLocation().string(), + mTokenizer->peekRemainderOfLine().string()); + return BAD_VALUE; + } + } + mTokenizer->nextLine(); + } return NO_ERROR; } -status_t -KeyLayoutMap::findScancodes(int32_t keycode, Vector<int32_t>* outScancodes) const -{ - if (m_status != NO_ERROR) { - return m_status; +status_t KeyLayoutMap::Parser::parseKey() { + String8 scanCodeToken = mTokenizer->nextToken(WHITESPACE); + char* end; + int32_t scanCode = int32_t(strtol(scanCodeToken.string(), &end, 0)); + if (*end) { + LOGE("%s: Expected scan code number, got '%s'.", mTokenizer->getLocation().string(), + scanCodeToken.string()); + return BAD_VALUE; } - - const size_t N = m_keys.size(); - for (size_t i=0; i<N; i++) { - if (m_keys.valueAt(i).keycode == keycode) { - outScancodes->add(m_keys.keyAt(i)); + if (mMap->mKeys.indexOfKey(scanCode) >= 0) { + LOGE("%s: Duplicate entry for scan code '%s'.", mTokenizer->getLocation().string(), + scanCodeToken.string()); + return BAD_VALUE; + } + + mTokenizer->skipDelimiters(WHITESPACE); + String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE); + int32_t keyCode = getKeyCodeByLabel(keyCodeToken.string()); + if (!keyCode) { + LOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().string(), + keyCodeToken.string()); + return BAD_VALUE; + } + + uint32_t flags = 0; + for (;;) { + mTokenizer->skipDelimiters(WHITESPACE); + if (mTokenizer->isEol()) break; + + String8 flagToken = mTokenizer->nextToken(WHITESPACE); + uint32_t flag = getKeyFlagByLabel(flagToken.string()); + if (!flag) { + LOGE("%s: Expected flag label, got '%s'.", mTokenizer->getLocation().string(), + flagToken.string()); + return BAD_VALUE; } + if (flags & flag) { + LOGE("%s: Duplicate flag '%s'.", mTokenizer->getLocation().string(), + flagToken.string()); + return BAD_VALUE; + } + flags |= flag; } - + +#if DEBUG_PARSER + LOGD("Parsed key: scanCode=%d, keyCode=%d, flags=0x%08x.", scanCode, keyCode, flags); +#endif + Key key; + key.keyCode = keyCode; + key.flags = flags; + mMap->mKeys.add(scanCode, key); return NO_ERROR; } diff --git a/libs/ui/KeyLayoutMap.h b/libs/ui/KeyLayoutMap.h deleted file mode 100644 index 43f84ce496..0000000000 --- a/libs/ui/KeyLayoutMap.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef KEYLAYOUTMAP_H -#define KEYLAYOUTMAP_H - -#include <utils/KeyedVector.h> - -namespace android { - -class KeyLayoutMap -{ -public: - KeyLayoutMap(); - ~KeyLayoutMap(); - - status_t load(const char* filename); - - status_t map(int32_t scancode, int32_t *keycode, uint32_t *flags) const; - status_t findScancodes(int32_t keycode, Vector<int32_t>* outScancodes) const; - -private: - struct Key { - int32_t keycode; - uint32_t flags; - }; - - status_t m_status; - KeyedVector<int32_t,Key> m_keys; -}; - -}; - -#endif // KEYLAYOUTMAP_H diff --git a/libs/ui/Keyboard.cpp b/libs/ui/Keyboard.cpp new file mode 100644 index 0000000000..6faa600e6e --- /dev/null +++ b/libs/ui/Keyboard.cpp @@ -0,0 +1,308 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "Keyboard" + +#include <stdlib.h> +#include <unistd.h> +#include <limits.h> + +#include <ui/Keyboard.h> +#include <ui/KeycodeLabels.h> +#include <ui/KeyLayoutMap.h> +#include <ui/KeyCharacterMap.h> +#include <utils/Errors.h> +#include <utils/Log.h> +#include <cutils/properties.h> + +namespace android { + +// --- KeyMap --- + +KeyMap::KeyMap() : + keyLayoutMap(NULL), keyCharacterMap(NULL) { +} + +KeyMap::~KeyMap() { + delete keyLayoutMap; + delete keyCharacterMap; +} + +status_t KeyMap::load(const InputDeviceIdentifier& deviceIdenfifier, + const PropertyMap* deviceConfiguration) { + // Use the configured key layout if available. + if (deviceConfiguration) { + String8 keyLayoutName; + if (deviceConfiguration->tryGetProperty(String8("keyboard.layout"), + keyLayoutName)) { + status_t status = loadKeyLayout(deviceIdenfifier, keyLayoutName); + if (status == NAME_NOT_FOUND) { + LOGE("Configuration for keyboard device '%s' requested keyboard layout '%s' but " + "it was not found.", + deviceIdenfifier.name.string(), keyLayoutName.string()); + } + } + + String8 keyCharacterMapName; + if (deviceConfiguration->tryGetProperty(String8("keyboard.characterMap"), + keyCharacterMapName)) { + status_t status = loadKeyCharacterMap(deviceIdenfifier, keyCharacterMapName); + if (status == NAME_NOT_FOUND) { + LOGE("Configuration for keyboard device '%s' requested keyboard character " + "map '%s' but it was not found.", + deviceIdenfifier.name.string(), keyLayoutName.string()); + } + } + + if (isComplete()) { + return OK; + } + } + + // Try searching by device identifier. + if (probeKeyMap(deviceIdenfifier, String8::empty())) { + return OK; + } + + // Fall back on the Generic key map. + // TODO Apply some additional heuristics here to figure out what kind of + // generic key map to use (US English, etc.) for typical external keyboards. + if (probeKeyMap(deviceIdenfifier, String8("Generic"))) { + return OK; + } + + // Try the Virtual key map as a last resort. + if (probeKeyMap(deviceIdenfifier, String8("Virtual"))) { + return OK; + } + + // Give up! + LOGE("Could not determine key map for device '%s' and no default key maps were found!", + deviceIdenfifier.name.string()); + return NAME_NOT_FOUND; +} + +bool KeyMap::probeKeyMap(const InputDeviceIdentifier& deviceIdentifier, + const String8& keyMapName) { + if (!haveKeyLayout()) { + loadKeyLayout(deviceIdentifier, keyMapName); + } + if (!haveKeyCharacterMap()) { + loadKeyCharacterMap(deviceIdentifier, keyMapName); + } + return isComplete(); +} + +status_t KeyMap::loadKeyLayout(const InputDeviceIdentifier& deviceIdentifier, + const String8& name) { + String8 path(getPath(deviceIdentifier, name, + INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_LAYOUT)); + if (path.isEmpty()) { + return NAME_NOT_FOUND; + } + + KeyLayoutMap* map; + status_t status = KeyLayoutMap::load(path, &map); + if (status) { + return status; + } + + keyLayoutFile.setTo(path); + keyLayoutMap = map; + return OK; +} + +status_t KeyMap::loadKeyCharacterMap(const InputDeviceIdentifier& deviceIdentifier, + const String8& name) { + String8 path(getPath(deviceIdentifier, name, + INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_CHARACTER_MAP)); + if (path.isEmpty()) { + return NAME_NOT_FOUND; + } + + KeyCharacterMap* map; + status_t status = KeyCharacterMap::load(path, &map); + if (status) { + return status; + } + + keyCharacterMapFile.setTo(path); + keyCharacterMap = map; + return OK; +} + +String8 KeyMap::getPath(const InputDeviceIdentifier& deviceIdentifier, + const String8& name, InputDeviceConfigurationFileType type) { + return name.isEmpty() + ? getInputDeviceConfigurationFilePathByDeviceIdentifier(deviceIdentifier, type) + : getInputDeviceConfigurationFilePathByName(name, type); +} + + +// --- Global functions --- + +bool isEligibleBuiltInKeyboard(const InputDeviceIdentifier& deviceIdentifier, + const PropertyMap* deviceConfiguration, const KeyMap* keyMap) { + if (!keyMap->haveKeyCharacterMap() + || keyMap->keyCharacterMap->getKeyboardType() + == KeyCharacterMap::KEYBOARD_TYPE_SPECIAL_FUNCTION) { + return false; + } + + if (deviceConfiguration) { + bool builtIn = false; + if (deviceConfiguration->tryGetProperty(String8("keyboard.builtIn"), builtIn) + && builtIn) { + return true; + } + } + + return strstr(deviceIdentifier.name.string(), "-keypad"); +} + +void setKeyboardProperties(int32_t deviceId, + const InputDeviceIdentifier& deviceIdentifier, + const String8& keyLayoutFile, const String8& keyCharacterMapFile) { + char propName[PROPERTY_KEY_MAX]; + snprintf(propName, sizeof(propName), "hw.keyboards.%u.devname", deviceId); + property_set(propName, deviceIdentifier.name.string()); + snprintf(propName, sizeof(propName), "hw.keyboards.%u.klfile", deviceId); + property_set(propName, keyLayoutFile.string()); + snprintf(propName, sizeof(propName), "hw.keyboards.%u.kcmfile", deviceId); + property_set(propName, keyCharacterMapFile.string()); +} + +void clearKeyboardProperties(int32_t deviceId) { + char propName[PROPERTY_KEY_MAX]; + snprintf(propName, sizeof(propName), "hw.keyboards.%u.devname", deviceId); + property_set(propName, ""); + snprintf(propName, sizeof(propName), "hw.keyboards.%u.klfile", deviceId); + property_set(propName, ""); + snprintf(propName, sizeof(propName), "hw.keyboards.%u.kcmfile", deviceId); + property_set(propName, ""); +} + +status_t getKeyCharacterMapFile(int32_t deviceId, String8& outKeyCharacterMapFile) { + if (deviceId != DEVICE_ID_VIRTUAL_KEYBOARD) { + char propName[PROPERTY_KEY_MAX]; + char fn[PROPERTY_VALUE_MAX]; + snprintf(propName, sizeof(propName), "hw.keyboards.%u.kcmfile", deviceId); + if (property_get(propName, fn, "") > 0) { + outKeyCharacterMapFile.setTo(fn); + return OK; + } + } + + // Default to Virtual since the keyboard does not appear to be installed. + outKeyCharacterMapFile.setTo(getInputDeviceConfigurationFilePathByName(String8("Virtual"), + INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_CHARACTER_MAP)); + if (!outKeyCharacterMapFile.isEmpty()) { + return OK; + } + + LOGE("Can't find any key character map files including the Virtual key map!"); + return NAME_NOT_FOUND; +} + +static int lookupLabel(const char* literal, const KeycodeLabel *list) { + while (list->literal) { + if (strcmp(literal, list->literal) == 0) { + return list->value; + } + list++; + } + return list->value; +} + +int32_t getKeyCodeByLabel(const char* label) { + return int32_t(lookupLabel(label, KEYCODES)); +} + +uint32_t getKeyFlagByLabel(const char* label) { + return uint32_t(lookupLabel(label, FLAGS)); +} + +static int32_t setEphemeralMetaState(int32_t mask, bool down, int32_t oldMetaState) { + int32_t newMetaState; + if (down) { + newMetaState = oldMetaState | mask; + } else { + newMetaState = oldMetaState & + ~(mask | AMETA_ALT_ON | AMETA_SHIFT_ON | AMETA_CTRL_ON | AMETA_META_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; + } + + if (newMetaState & (AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON)) { + newMetaState |= AMETA_CTRL_ON; + } + + if (newMetaState & (AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON)) { + newMetaState |= AMETA_META_ON; + } + return newMetaState; +} + +static int32_t toggleLockedMetaState(int32_t mask, bool down, int32_t oldMetaState) { + if (down) { + return oldMetaState; + } else { + return oldMetaState ^ mask; + } +} + +int32_t updateMetaState(int32_t keyCode, bool down, int32_t oldMetaState) { + int32_t mask; + switch (keyCode) { + case AKEYCODE_ALT_LEFT: + return setEphemeralMetaState(AMETA_ALT_LEFT_ON, down, oldMetaState); + case AKEYCODE_ALT_RIGHT: + return setEphemeralMetaState(AMETA_ALT_RIGHT_ON, down, oldMetaState); + case AKEYCODE_SHIFT_LEFT: + return setEphemeralMetaState(AMETA_SHIFT_LEFT_ON, down, oldMetaState); + case AKEYCODE_SHIFT_RIGHT: + return setEphemeralMetaState(AMETA_SHIFT_RIGHT_ON, down, oldMetaState); + case AKEYCODE_SYM: + return setEphemeralMetaState(AMETA_SYM_ON, down, oldMetaState); + case AKEYCODE_FUNCTION: + return setEphemeralMetaState(AMETA_FUNCTION_ON, down, oldMetaState); + case AKEYCODE_CTRL_LEFT: + return setEphemeralMetaState(AMETA_CTRL_LEFT_ON, down, oldMetaState); + case AKEYCODE_CTRL_RIGHT: + return setEphemeralMetaState(AMETA_CTRL_RIGHT_ON, down, oldMetaState); + case AKEYCODE_META_LEFT: + return setEphemeralMetaState(AMETA_META_LEFT_ON, down, oldMetaState); + case AKEYCODE_META_RIGHT: + return setEphemeralMetaState(AMETA_META_RIGHT_ON, down, oldMetaState); + case AKEYCODE_CAPS_LOCK: + return toggleLockedMetaState(AMETA_CAPS_LOCK_ON, down, oldMetaState); + case AKEYCODE_NUM_LOCK: + return toggleLockedMetaState(AMETA_NUM_LOCK_ON, down, oldMetaState); + case AKEYCODE_SCROLL_LOCK: + return toggleLockedMetaState(AMETA_SCROLL_LOCK_ON, down, oldMetaState); + default: + return oldMetaState; + } +} + + +} // namespace android diff --git a/libs/ui/Overlay.cpp b/libs/ui/Overlay.cpp deleted file mode 100644 index 3aa8950af5..0000000000 --- a/libs/ui/Overlay.cpp +++ /dev/null @@ -1,210 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <binder/IMemory.h> -#include <binder/Parcel.h> -#include <utils/Errors.h> -#include <binder/MemoryHeapBase.h> - -#include <ui/IOverlay.h> -#include <ui/Overlay.h> - -#include <hardware/overlay.h> - -namespace android { - -Overlay::Overlay(const sp<OverlayRef>& overlayRef) - : mOverlayRef(overlayRef), mOverlayData(0), mStatus(NO_INIT) -{ - mOverlayData = NULL; - hw_module_t const* module; - if (overlayRef != 0) { - if (hw_get_module(OVERLAY_HARDWARE_MODULE_ID, &module) == 0) { - if (overlay_data_open(module, &mOverlayData) == NO_ERROR) { - mStatus = mOverlayData->initialize(mOverlayData, - overlayRef->mOverlayHandle); - } - } - } -} - -Overlay::~Overlay() { - if (mOverlayData) { - overlay_data_close(mOverlayData); - } -} - -status_t Overlay::dequeueBuffer(overlay_buffer_t* buffer) -{ - if (mStatus != NO_ERROR) return mStatus; - return mOverlayData->dequeueBuffer(mOverlayData, buffer); -} - -status_t Overlay::queueBuffer(overlay_buffer_t buffer) -{ - if (mStatus != NO_ERROR) return mStatus; - return mOverlayData->queueBuffer(mOverlayData, buffer); -} - -status_t Overlay::resizeInput(uint32_t width, uint32_t height) -{ - if (mStatus != NO_ERROR) return mStatus; - return mOverlayData->resizeInput(mOverlayData, width, height); -} - -status_t Overlay::setParameter(int param, int value) -{ - if (mStatus != NO_ERROR) return mStatus; - return mOverlayData->setParameter(mOverlayData, param, value); -} - -status_t Overlay::setCrop(uint32_t x, uint32_t y, uint32_t w, uint32_t h) -{ - if (mStatus != NO_ERROR) return mStatus; - return mOverlayData->setCrop(mOverlayData, x, y, w, h); -} - -status_t Overlay::getCrop(uint32_t* x, uint32_t* y, uint32_t* w, uint32_t* h) -{ - if (mStatus != NO_ERROR) return mStatus; - return mOverlayData->getCrop(mOverlayData, x, y, w, h); -} - -int32_t Overlay::getBufferCount() const -{ - if (mStatus != NO_ERROR) return mStatus; - return mOverlayData->getBufferCount(mOverlayData); -} - -void* Overlay::getBufferAddress(overlay_buffer_t buffer) -{ - if (mStatus != NO_ERROR) return NULL; - return mOverlayData->getBufferAddress(mOverlayData, buffer); -} - -void Overlay::destroy() { - if (mStatus != NO_ERROR) return; - - // Must delete the objects in reverse creation order, thus the - // data side must be closed first and then the destroy send to - // the control side. - if (mOverlayData) { - overlay_data_close(mOverlayData); - mOverlayData = NULL; - } - - mOverlayRef->mOverlayChannel->destroy(); -} - -status_t Overlay::getStatus() const { - return mStatus; -} - -overlay_handle_t Overlay::getHandleRef() const { - if (mStatus != NO_ERROR) return NULL; - return mOverlayRef->mOverlayHandle; -} - -uint32_t Overlay::getWidth() const { - if (mStatus != NO_ERROR) return 0; - return mOverlayRef->mWidth; -} - -uint32_t Overlay::getHeight() const { - if (mStatus != NO_ERROR) return 0; - return mOverlayRef->mHeight; -} - -int32_t Overlay::getFormat() const { - if (mStatus != NO_ERROR) return -1; - return mOverlayRef->mFormat; -} - -int32_t Overlay::getWidthStride() const { - if (mStatus != NO_ERROR) return 0; - return mOverlayRef->mWidthStride; -} - -int32_t Overlay::getHeightStride() const { - if (mStatus != NO_ERROR) return 0; - return mOverlayRef->mHeightStride; -} -// ---------------------------------------------------------------------------- - -OverlayRef::OverlayRef() - : mOverlayHandle(0), - mWidth(0), mHeight(0), mFormat(0), mWidthStride(0), mHeightStride(0), - mOwnHandle(true) -{ -} - -OverlayRef::OverlayRef(overlay_handle_t handle, const sp<IOverlay>& channel, - uint32_t w, uint32_t h, int32_t f, uint32_t ws, uint32_t hs) - : mOverlayHandle(handle), mOverlayChannel(channel), - mWidth(w), mHeight(h), mFormat(f), mWidthStride(ws), mHeightStride(hs), - mOwnHandle(false) -{ -} - -OverlayRef::~OverlayRef() -{ - if (mOwnHandle) { - native_handle_close(mOverlayHandle); - native_handle_delete(const_cast<native_handle*>(mOverlayHandle)); - } -} - -sp<OverlayRef> OverlayRef::readFromParcel(const Parcel& data) { - sp<OverlayRef> result; - sp<IOverlay> overlay = IOverlay::asInterface(data.readStrongBinder()); - if (overlay != NULL) { - uint32_t w = data.readInt32(); - uint32_t h = data.readInt32(); - uint32_t f = data.readInt32(); - uint32_t ws = data.readInt32(); - uint32_t hs = data.readInt32(); - native_handle* handle = data.readNativeHandle(); - - result = new OverlayRef(); - result->mOverlayHandle = handle; - result->mOverlayChannel = overlay; - result->mWidth = w; - result->mHeight = h; - result->mFormat = f; - result->mWidthStride = ws; - result->mHeightStride = hs; - } - return result; -} - -status_t OverlayRef::writeToParcel(Parcel* reply, const sp<OverlayRef>& o) { - if (o != NULL) { - reply->writeStrongBinder(o->mOverlayChannel->asBinder()); - reply->writeInt32(o->mWidth); - reply->writeInt32(o->mHeight); - reply->writeInt32(o->mFormat); - reply->writeInt32(o->mWidthStride); - reply->writeInt32(o->mHeightStride); - reply->writeNativeHandle(o->mOverlayHandle); - } else { - reply->writeStrongBinder(NULL); - } - return NO_ERROR; -} - -// ---------------------------------------------------------------------------- - -}; // namespace android diff --git a/libs/ui/VirtualKeyMap.cpp b/libs/ui/VirtualKeyMap.cpp new file mode 100644 index 0000000000..e756cdd782 --- /dev/null +++ b/libs/ui/VirtualKeyMap.cpp @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "VirtualKeyMap" + +#include <stdlib.h> +#include <string.h> +#include <ui/VirtualKeyMap.h> +#include <utils/Log.h> +#include <utils/Errors.h> +#include <utils/Tokenizer.h> +#include <utils/Timers.h> + +// Enables debug output for the parser. +#define DEBUG_PARSER 0 + +// Enables debug output for parser performance. +#define DEBUG_PARSER_PERFORMANCE 0 + + +namespace android { + +static const char* WHITESPACE = " \t\r"; +static const char* WHITESPACE_OR_FIELD_DELIMITER = " \t\r:"; + + +// --- VirtualKeyMap --- + +VirtualKeyMap::VirtualKeyMap() { +} + +VirtualKeyMap::~VirtualKeyMap() { +} + +status_t VirtualKeyMap::load(const String8& filename, VirtualKeyMap** outMap) { + *outMap = NULL; + + Tokenizer* tokenizer; + status_t status = Tokenizer::open(filename, &tokenizer); + if (status) { + LOGE("Error %d opening virtual key map file %s.", status, filename.string()); + } else { + VirtualKeyMap* map = new VirtualKeyMap(); + if (!map) { + LOGE("Error allocating virtual key map."); + status = NO_MEMORY; + } else { +#if DEBUG_PARSER_PERFORMANCE + nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC); +#endif + Parser parser(map, tokenizer); + status = parser.parse(); +#if DEBUG_PARSER_PERFORMANCE + nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime; + LOGD("Parsed key character map file '%s' %d lines in %0.3fms.", + tokenizer->getFilename().string(), tokenizer->getLineNumber(), + elapsedTime / 1000000.0); +#endif + if (status) { + delete map; + } else { + *outMap = map; + } + } + delete tokenizer; + } + return status; +} + + +// --- VirtualKeyMap::Parser --- + +VirtualKeyMap::Parser::Parser(VirtualKeyMap* map, Tokenizer* tokenizer) : + mMap(map), mTokenizer(tokenizer) { +} + +VirtualKeyMap::Parser::~Parser() { +} + +status_t VirtualKeyMap::Parser::parse() { + while (!mTokenizer->isEof()) { +#if DEBUG_PARSER + LOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(), + mTokenizer->peekRemainderOfLine().string()); +#endif + + mTokenizer->skipDelimiters(WHITESPACE); + + if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') { + // Multiple keys can appear on one line or they can be broken up across multiple lines. + do { + String8 token = mTokenizer->nextToken(WHITESPACE_OR_FIELD_DELIMITER); + if (token != "0x01") { + LOGE("%s: Unknown virtual key type, expected 0x01.", + mTokenizer->getLocation().string()); + return BAD_VALUE; + } + + VirtualKeyDefinition defn; + bool success = parseNextIntField(&defn.scanCode) + && parseNextIntField(&defn.centerX) + && parseNextIntField(&defn.centerY) + && parseNextIntField(&defn.width) + && parseNextIntField(&defn.height); + if (!success) { + LOGE("%s: Expected 5 colon-delimited integers in virtual key definition.", + mTokenizer->getLocation().string()); + return BAD_VALUE; + } + +#if DEBUG_PARSER + LOGD("Parsed virtual key: scanCode=%d, centerX=%d, centerY=%d, " + "width=%d, height=%d", + defn.scanCode, defn.centerX, defn.centerY, defn.width, defn.height); +#endif + mMap->mVirtualKeys.push(defn); + } while (consumeFieldDelimiterAndSkipWhitespace()); + + if (!mTokenizer->isEol()) { + LOGE("%s: Expected end of line, got '%s'.", + mTokenizer->getLocation().string(), + mTokenizer->peekRemainderOfLine().string()); + return BAD_VALUE; + } + } + + mTokenizer->nextLine(); + } + + return NO_ERROR; +} + +bool VirtualKeyMap::Parser::consumeFieldDelimiterAndSkipWhitespace() { + mTokenizer->skipDelimiters(WHITESPACE); + if (mTokenizer->peekChar() == ':') { + mTokenizer->nextChar(); + mTokenizer->skipDelimiters(WHITESPACE); + return true; + } + return false; +} + +bool VirtualKeyMap::Parser::parseNextIntField(int32_t* outValue) { + if (!consumeFieldDelimiterAndSkipWhitespace()) { + return false; + } + + String8 token = mTokenizer->nextToken(WHITESPACE_OR_FIELD_DELIMITER); + char* end; + *outValue = strtol(token.string(), &end, 0); + if (token.isEmpty() || *end != '\0') { + LOGE("Expected an integer, got '%s'.", token.string()); + return false; + } + return true; +} + +} // namespace android diff --git a/libs/ui/tests/Android.mk b/libs/ui/tests/Android.mk index aa017b9788..580d73cf38 100644 --- a/libs/ui/tests/Android.mk +++ b/libs/ui/tests/Android.mk @@ -7,8 +7,6 @@ ifneq ($(TARGET_SIMULATOR),true) # Build the unit tests. test_src_files := \ InputChannel_test.cpp \ - InputReader_test.cpp \ - InputDispatcher_test.cpp \ InputPublisherAndConsumer_test.cpp shared_libraries := \ diff --git a/libs/ui/tests/InputDispatcher_test.cpp b/libs/ui/tests/InputDispatcher_test.cpp deleted file mode 100644 index 8874dfe3c9..0000000000 --- a/libs/ui/tests/InputDispatcher_test.cpp +++ /dev/null @@ -1,226 +0,0 @@ -// -// Copyright 2010 The Android Open Source Project -// - -#include <ui/InputDispatcher.h> -#include <gtest/gtest.h> -#include <linux/input.h> - -namespace android { - -// An arbitrary time value. -static const nsecs_t ARBITRARY_TIME = 1234; - -// An arbitrary device id. -static const int32_t DEVICE_ID = 1; - -// An arbitrary injector pid / uid pair that has permission to inject events. -static const int32_t INJECTOR_PID = 999; -static const int32_t INJECTOR_UID = 1001; - - -// --- FakeInputDispatcherPolicy --- - -class FakeInputDispatcherPolicy : public InputDispatcherPolicyInterface { -protected: - virtual ~FakeInputDispatcherPolicy() { - } - -public: - FakeInputDispatcherPolicy() { - } - -private: - virtual void notifyConfigurationChanged(nsecs_t when) { - } - - virtual nsecs_t notifyANR(const sp<InputApplicationHandle>& inputApplicationHandle, - const sp<InputChannel>& inputChannel) { - return 0; - } - - virtual void notifyInputChannelBroken(const sp<InputChannel>& inputChannel) { - } - - virtual nsecs_t getKeyRepeatTimeout() { - return 500 * 1000000LL; - } - - virtual nsecs_t getKeyRepeatDelay() { - return 50 * 1000000LL; - } - - virtual int32_t getMaxEventsPerSecond() { - return 60; - } - - virtual void interceptKeyBeforeQueueing(nsecs_t when, int32_t deviceId, - int32_t action, int32_t& flags, int32_t keyCode, int32_t scanCode, - uint32_t& policyFlags) { - } - - virtual void interceptGenericBeforeQueueing(nsecs_t when, uint32_t& policyFlags) { - } - - virtual bool interceptKeyBeforeDispatching(const sp<InputChannel>& inputChannel, - const KeyEvent* keyEvent, uint32_t policyFlags) { - return false; - } - - virtual void notifySwitch(nsecs_t when, - int32_t switchCode, int32_t switchValue, uint32_t policyFlags) { - } - - virtual void pokeUserActivity(nsecs_t eventTime, int32_t eventType) { - } - - virtual bool checkInjectEventsPermissionNonReentrant( - int32_t injectorPid, int32_t injectorUid) { - return false; - } -}; - - -// --- InputDispatcherTest --- - -class InputDispatcherTest : public testing::Test { -protected: - sp<FakeInputDispatcherPolicy> mFakePolicy; - sp<InputDispatcher> mDispatcher; - - virtual void SetUp() { - mFakePolicy = new FakeInputDispatcherPolicy(); - mDispatcher = new InputDispatcher(mFakePolicy); - } - - virtual void TearDown() { - mFakePolicy.clear(); - mDispatcher.clear(); - } -}; - - -TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesKeyEvents) { - KeyEvent event; - - // Rejects undefined key actions. - event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, - /*action*/ -1, 0, - AKEYCODE_A, KEY_A, AMETA_NONE, 0, ARBITRARY_TIME, ARBITRARY_TIME); - ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event, - INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0)) - << "Should reject key events with undefined action."; - - // Rejects ACTION_MULTIPLE since it is not supported despite being defined in the API. - event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, - AKEY_EVENT_ACTION_MULTIPLE, 0, - AKEYCODE_A, KEY_A, AMETA_NONE, 0, ARBITRARY_TIME, ARBITRARY_TIME); - ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event, - INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0)) - << "Should reject key events with ACTION_MULTIPLE."; -} - -TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { - MotionEvent event; - int32_t pointerIds[MAX_POINTERS + 1]; - PointerCoords pointerCoords[MAX_POINTERS + 1]; - for (int i = 0; i <= MAX_POINTERS; i++) { - pointerIds[i] = i; - } - - // Rejects undefined motion actions. - event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, - /*action*/ -1, 0, 0, AMETA_NONE, 0, 0, 0, 0, - ARBITRARY_TIME, ARBITRARY_TIME, - /*pointerCount*/ 1, pointerIds, pointerCoords); - ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event, - INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0)) - << "Should reject motion events with undefined action."; - - // Rejects pointer down with invalid index. - event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, - AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - 0, 0, AMETA_NONE, 0, 0, 0, 0, - ARBITRARY_TIME, ARBITRARY_TIME, - /*pointerCount*/ 1, pointerIds, pointerCoords); - ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event, - INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0)) - << "Should reject motion events with pointer down index too large."; - - event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, - AMOTION_EVENT_ACTION_POINTER_DOWN | (-1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - 0, 0, AMETA_NONE, 0, 0, 0, 0, - ARBITRARY_TIME, ARBITRARY_TIME, - /*pointerCount*/ 1, pointerIds, pointerCoords); - ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event, - INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0)) - << "Should reject motion events with pointer down index too small."; - - // Rejects pointer up with invalid index. - event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, - AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - 0, 0, AMETA_NONE, 0, 0, 0, 0, - ARBITRARY_TIME, ARBITRARY_TIME, - /*pointerCount*/ 1, pointerIds, pointerCoords); - ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event, - INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0)) - << "Should reject motion events with pointer up index too large."; - - event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, - AMOTION_EVENT_ACTION_POINTER_UP | (-1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - 0, 0, AMETA_NONE, 0, 0, 0, 0, - ARBITRARY_TIME, ARBITRARY_TIME, - /*pointerCount*/ 1, pointerIds, pointerCoords); - ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event, - INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0)) - << "Should reject motion events with pointer up index too small."; - - // Rejects motion events with invalid number of pointers. - event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, - AMOTION_EVENT_ACTION_DOWN, 0, 0, AMETA_NONE, 0, 0, 0, 0, - ARBITRARY_TIME, ARBITRARY_TIME, - /*pointerCount*/ 0, pointerIds, pointerCoords); - ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event, - INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0)) - << "Should reject motion events with 0 pointers."; - - event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, - AMOTION_EVENT_ACTION_DOWN, 0, 0, AMETA_NONE, 0, 0, 0, 0, - ARBITRARY_TIME, ARBITRARY_TIME, - /*pointerCount*/ MAX_POINTERS + 1, pointerIds, pointerCoords); - ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event, - INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0)) - << "Should reject motion events with more than MAX_POINTERS pointers."; - - // Rejects motion events with invalid pointer ids. - pointerIds[0] = -1; - event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, - AMOTION_EVENT_ACTION_DOWN, 0, 0, AMETA_NONE, 0, 0, 0, 0, - ARBITRARY_TIME, ARBITRARY_TIME, - /*pointerCount*/ 1, pointerIds, pointerCoords); - ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event, - INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0)) - << "Should reject motion events with pointer ids less than 0."; - - pointerIds[0] = MAX_POINTER_ID + 1; - event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, - AMOTION_EVENT_ACTION_DOWN, 0, 0, AMETA_NONE, 0, 0, 0, 0, - ARBITRARY_TIME, ARBITRARY_TIME, - /*pointerCount*/ 1, pointerIds, pointerCoords); - ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event, - INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0)) - << "Should reject motion events with pointer ids greater than MAX_POINTER_ID."; - - // Rejects motion events with duplicate pointer ids. - pointerIds[0] = 1; - pointerIds[1] = 1; - event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, - AMOTION_EVENT_ACTION_DOWN, 0, 0, AMETA_NONE, 0, 0, 0, 0, - ARBITRARY_TIME, ARBITRARY_TIME, - /*pointerCount*/ 2, pointerIds, pointerCoords); - ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event, - INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0)) - << "Should reject motion events with duplicate pointer ids."; -} - -} // namespace android diff --git a/libs/ui/tests/InputPublisherAndConsumer_test.cpp b/libs/ui/tests/InputPublisherAndConsumer_test.cpp index 952b974769..903fcaf20a 100644 --- a/libs/ui/tests/InputPublisherAndConsumer_test.cpp +++ b/libs/ui/tests/InputPublisherAndConsumer_test.cpp @@ -118,13 +118,16 @@ void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() { EXPECT_EQ(downTime, keyEvent->getDownTime()); EXPECT_EQ(eventTime, keyEvent->getEventTime()); - status = mConsumer->sendFinishedSignal(); + status = mConsumer->sendFinishedSignal(true); ASSERT_EQ(OK, status) << "consumer sendFinishedSignal should return OK"; - status = mPublisher->receiveFinishedSignal(); + bool handled = false; + status = mPublisher->receiveFinishedSignal(&handled); ASSERT_EQ(OK, status) << "publisher receiveFinishedSignal should return OK"; + ASSERT_TRUE(handled) + << "publisher receiveFinishedSignal should have set handled to consumer's reply"; status = mPublisher->reset(); ASSERT_EQ(OK, status) @@ -279,13 +282,16 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent( EXPECT_EQ(samplePointerCoords[offset].orientation, motionEvent->getOrientation(i)); } - status = mConsumer->sendFinishedSignal(); + status = mConsumer->sendFinishedSignal(false); ASSERT_EQ(OK, status) << "consumer sendFinishedSignal should return OK"; - status = mPublisher->receiveFinishedSignal(); + bool handled = true; + status = mPublisher->receiveFinishedSignal(&handled); ASSERT_EQ(OK, status) << "publisher receiveFinishedSignal should return OK"; + ASSERT_FALSE(handled) + << "publisher receiveFinishedSignal should have set handled to consumer's reply"; status = mPublisher->reset(); ASSERT_EQ(OK, status) diff --git a/libs/ui/tests/InputReader_test.cpp b/libs/ui/tests/InputReader_test.cpp deleted file mode 100644 index f31a6be047..0000000000 --- a/libs/ui/tests/InputReader_test.cpp +++ /dev/null @@ -1,3380 +0,0 @@ -// -// Copyright 2010 The Android Open Source Project -// - -#include <ui/InputReader.h> -#include <utils/List.h> -#include <gtest/gtest.h> -#include <math.h> - -namespace android { - -// An arbitrary time value. -static const nsecs_t ARBITRARY_TIME = 1234; - -// Arbitrary display properties. -static const int32_t DISPLAY_ID = 0; -static const int32_t DISPLAY_WIDTH = 480; -static const int32_t DISPLAY_HEIGHT = 800; - -// Error tolerance for floating point assertions. -static const float EPSILON = 0.001f; - -template<typename T> -static inline T min(T a, T b) { - return a < b ? a : b; -} - -static inline float avg(float x, float y) { - return (x + y) / 2; -} - - -// --- FakeInputReaderPolicy --- - -class FakeInputReaderPolicy : public InputReaderPolicyInterface { - struct DisplayInfo { - int32_t width; - int32_t height; - int32_t orientation; - }; - - KeyedVector<int32_t, DisplayInfo> mDisplayInfos; - bool mFilterTouchEvents; - bool mFilterJumpyTouchEvents; - KeyedVector<String8, Vector<VirtualKeyDefinition> > mVirtualKeyDefinitions; - KeyedVector<String8, InputDeviceCalibration> mInputDeviceCalibrations; - Vector<String8> mExcludedDeviceNames; - -protected: - virtual ~FakeInputReaderPolicy() { } - -public: - FakeInputReaderPolicy() : - mFilterTouchEvents(false), mFilterJumpyTouchEvents(false) { - } - - void removeDisplayInfo(int32_t displayId) { - mDisplayInfos.removeItem(displayId); - } - - void setDisplayInfo(int32_t displayId, int32_t width, int32_t height, int32_t orientation) { - removeDisplayInfo(displayId); - - DisplayInfo info; - info.width = width; - info.height = height; - info.orientation = orientation; - mDisplayInfos.add(displayId, info); - } - - void setFilterTouchEvents(bool enabled) { - mFilterTouchEvents = enabled; - } - - void setFilterJumpyTouchEvents(bool enabled) { - mFilterJumpyTouchEvents = enabled; - } - - void addInputDeviceCalibration(const String8& deviceName, - const InputDeviceCalibration& calibration) { - mInputDeviceCalibrations.add(deviceName, calibration); - } - - void addInputDeviceCalibrationProperty(const String8& deviceName, - const String8& key, const String8& value) { - ssize_t index = mInputDeviceCalibrations.indexOfKey(deviceName); - if (index < 0) { - index = mInputDeviceCalibrations.add(deviceName, InputDeviceCalibration()); - } - mInputDeviceCalibrations.editValueAt(index).addProperty(key, value); - } - - void addVirtualKeyDefinition(const String8& deviceName, - const VirtualKeyDefinition& definition) { - if (mVirtualKeyDefinitions.indexOfKey(deviceName) < 0) { - mVirtualKeyDefinitions.add(deviceName, Vector<VirtualKeyDefinition>()); - } - - mVirtualKeyDefinitions.editValueFor(deviceName).push(definition); - } - - void addExcludedDeviceName(const String8& deviceName) { - mExcludedDeviceNames.push(deviceName); - } - -private: - virtual bool getDisplayInfo(int32_t displayId, - int32_t* width, int32_t* height, int32_t* orientation) { - ssize_t index = mDisplayInfos.indexOfKey(displayId); - if (index >= 0) { - const DisplayInfo& info = mDisplayInfos.valueAt(index); - if (width) { - *width = info.width; - } - if (height) { - *height = info.height; - } - if (orientation) { - *orientation = info.orientation; - } - return true; - } - return false; - } - - virtual bool filterTouchEvents() { - return mFilterTouchEvents; - } - - virtual bool filterJumpyTouchEvents() { - return mFilterJumpyTouchEvents; - } - - virtual nsecs_t getVirtualKeyQuietTime() { - return 0; - } - - virtual void getVirtualKeyDefinitions(const String8& deviceName, - Vector<VirtualKeyDefinition>& outVirtualKeyDefinitions) { - ssize_t index = mVirtualKeyDefinitions.indexOfKey(deviceName); - if (index >= 0) { - outVirtualKeyDefinitions.appendVector(mVirtualKeyDefinitions.valueAt(index)); - } - } - - virtual void getInputDeviceCalibration(const String8& deviceName, - InputDeviceCalibration& outCalibration) { - ssize_t index = mInputDeviceCalibrations.indexOfKey(deviceName); - if (index >= 0) { - outCalibration = mInputDeviceCalibrations.valueAt(index); - } - } - - virtual void getExcludedDeviceNames(Vector<String8>& outExcludedDeviceNames) { - outExcludedDeviceNames.appendVector(mExcludedDeviceNames); - } -}; - - -// --- FakeInputDispatcher --- - -class FakeInputDispatcher : public InputDispatcherInterface { -public: - struct NotifyConfigurationChangedArgs { - nsecs_t eventTime; - }; - - struct NotifyKeyArgs { - 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; - }; - - struct NotifyMotionArgs { - 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; - Vector<int32_t> pointerIds; - Vector<PointerCoords> pointerCoords; - float xPrecision; - float yPrecision; - nsecs_t downTime; - }; - - struct NotifySwitchArgs { - nsecs_t when; - int32_t switchCode; - int32_t switchValue; - uint32_t policyFlags; - }; - -private: - List<NotifyConfigurationChangedArgs> mNotifyConfigurationChangedArgs; - List<NotifyKeyArgs> mNotifyKeyArgs; - List<NotifyMotionArgs> mNotifyMotionArgs; - List<NotifySwitchArgs> mNotifySwitchArgs; - -protected: - virtual ~FakeInputDispatcher() { } - -public: - FakeInputDispatcher() { - } - - void assertNotifyConfigurationChangedWasCalled(NotifyConfigurationChangedArgs* outArgs = NULL) { - ASSERT_FALSE(mNotifyConfigurationChangedArgs.empty()) - << "Expected notifyConfigurationChanged() to have been called."; - if (outArgs) { - *outArgs = *mNotifyConfigurationChangedArgs.begin(); - } - mNotifyConfigurationChangedArgs.erase(mNotifyConfigurationChangedArgs.begin()); - } - - void assertNotifyKeyWasCalled(NotifyKeyArgs* outArgs = NULL) { - ASSERT_FALSE(mNotifyKeyArgs.empty()) - << "Expected notifyKey() to have been called."; - if (outArgs) { - *outArgs = *mNotifyKeyArgs.begin(); - } - mNotifyKeyArgs.erase(mNotifyKeyArgs.begin()); - } - - void assertNotifyKeyWasNotCalled() { - ASSERT_TRUE(mNotifyKeyArgs.empty()) - << "Expected notifyKey() to not have been called."; - } - - void assertNotifyMotionWasCalled(NotifyMotionArgs* outArgs = NULL) { - ASSERT_FALSE(mNotifyMotionArgs.empty()) - << "Expected notifyMotion() to have been called."; - if (outArgs) { - *outArgs = *mNotifyMotionArgs.begin(); - } - mNotifyMotionArgs.erase(mNotifyMotionArgs.begin()); - } - - void assertNotifyMotionWasNotCalled() { - ASSERT_TRUE(mNotifyMotionArgs.empty()) - << "Expected notifyMotion() to not have been called."; - } - - void assertNotifySwitchWasCalled(NotifySwitchArgs* outArgs = NULL) { - ASSERT_FALSE(mNotifySwitchArgs.empty()) - << "Expected notifySwitch() to have been called."; - if (outArgs) { - *outArgs = *mNotifySwitchArgs.begin(); - } - mNotifySwitchArgs.erase(mNotifySwitchArgs.begin()); - } - -private: - virtual void notifyConfigurationChanged(nsecs_t eventTime) { - NotifyConfigurationChangedArgs args; - args.eventTime = eventTime; - mNotifyConfigurationChangedArgs.push_back(args); - } - - virtual void 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) { - NotifyKeyArgs args; - args.eventTime = eventTime; - args.deviceId = deviceId; - args.source = source; - args.policyFlags = policyFlags; - args.action = action; - args.flags = flags; - args.keyCode = keyCode; - args.scanCode = scanCode; - args.metaState = metaState; - args.downTime = downTime; - mNotifyKeyArgs.push_back(args); - } - - virtual void 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) { - NotifyMotionArgs args; - args.eventTime = eventTime; - args.deviceId = deviceId; - args.source = source; - args.policyFlags = policyFlags; - args.action = action; - args.flags = flags; - args.metaState = metaState; - args.edgeFlags = edgeFlags; - args.pointerCount = pointerCount; - args.pointerIds.clear(); - args.pointerIds.appendArray(pointerIds, pointerCount); - args.pointerCoords.clear(); - args.pointerCoords.appendArray(pointerCoords, pointerCount); - args.xPrecision = xPrecision; - args.yPrecision = yPrecision; - args.downTime = downTime; - mNotifyMotionArgs.push_back(args); - } - - virtual void notifySwitch(nsecs_t when, - int32_t switchCode, int32_t switchValue, uint32_t policyFlags) { - NotifySwitchArgs args; - args.when = when; - args.switchCode = switchCode; - args.switchValue = switchValue; - args.policyFlags = policyFlags; - mNotifySwitchArgs.push_back(args); - } - - virtual void dump(String8& dump) { - ADD_FAILURE() << "Should never be called by input reader."; - } - - virtual void dispatchOnce() { - ADD_FAILURE() << "Should never be called by input reader."; - } - - virtual int32_t injectInputEvent(const InputEvent* event, - int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis) { - ADD_FAILURE() << "Should never be called by input reader."; - return INPUT_EVENT_INJECTION_FAILED; - } - - virtual void setInputWindows(const Vector<InputWindow>& inputWindows) { - ADD_FAILURE() << "Should never be called by input reader."; - } - - virtual void setFocusedApplication(const InputApplication* inputApplication) { - ADD_FAILURE() << "Should never be called by input reader."; - } - - virtual void setInputDispatchMode(bool enabled, bool frozen) { - ADD_FAILURE() << "Should never be called by input reader."; - } - - virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel, bool monitor) { - ADD_FAILURE() << "Should never be called by input reader."; - return 0; - } - - virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel) { - ADD_FAILURE() << "Should never be called by input reader."; - return 0; - } -}; - - -// --- FakeEventHub --- - -class FakeEventHub : public EventHubInterface { - struct KeyInfo { - int32_t keyCode; - uint32_t flags; - }; - - struct Device { - String8 name; - uint32_t classes; - KeyedVector<int, RawAbsoluteAxisInfo> axes; - KeyedVector<int32_t, int32_t> keyCodeStates; - KeyedVector<int32_t, int32_t> scanCodeStates; - KeyedVector<int32_t, int32_t> switchStates; - KeyedVector<int32_t, KeyInfo> keys; - - Device(const String8& name, uint32_t classes) : - name(name), classes(classes) { - } - }; - - KeyedVector<int32_t, Device*> mDevices; - Vector<String8> mExcludedDevices; - List<RawEvent> mEvents; - -protected: - virtual ~FakeEventHub() { - for (size_t i = 0; i < mDevices.size(); i++) { - delete mDevices.valueAt(i); - } - } - -public: - FakeEventHub() { } - - void addDevice(int32_t deviceId, const String8& name, uint32_t classes) { - Device* device = new Device(name, classes); - mDevices.add(deviceId, device); - - enqueueEvent(ARBITRARY_TIME, deviceId, EventHubInterface::DEVICE_ADDED, 0, 0, 0, 0); - } - - void removeDevice(int32_t deviceId) { - delete mDevices.valueFor(deviceId); - mDevices.removeItem(deviceId); - - enqueueEvent(ARBITRARY_TIME, deviceId, EventHubInterface::DEVICE_REMOVED, 0, 0, 0, 0); - } - - void finishDeviceScan() { - enqueueEvent(ARBITRARY_TIME, 0, EventHubInterface::FINISHED_DEVICE_SCAN, 0, 0, 0, 0); - } - - void addAxis(int32_t deviceId, int axis, - int32_t minValue, int32_t maxValue, int flat, int fuzz) { - Device* device = getDevice(deviceId); - - RawAbsoluteAxisInfo info; - info.valid = true; - info.minValue = minValue; - info.maxValue = maxValue; - info.flat = flat; - info.fuzz = fuzz; - device->axes.add(axis, info); - } - - void setKeyCodeState(int32_t deviceId, int32_t keyCode, int32_t state) { - Device* device = getDevice(deviceId); - device->keyCodeStates.replaceValueFor(keyCode, state); - } - - void setScanCodeState(int32_t deviceId, int32_t scanCode, int32_t state) { - Device* device = getDevice(deviceId); - device->scanCodeStates.replaceValueFor(scanCode, state); - } - - void setSwitchState(int32_t deviceId, int32_t switchCode, int32_t state) { - Device* device = getDevice(deviceId); - device->switchStates.replaceValueFor(switchCode, state); - } - - void addKey(int32_t deviceId, int32_t scanCode, int32_t keyCode, uint32_t flags) { - Device* device = getDevice(deviceId); - KeyInfo info; - info.keyCode = keyCode; - info.flags = flags; - device->keys.add(scanCode, info); - } - - Vector<String8>& getExcludedDevices() { - return mExcludedDevices; - } - - void enqueueEvent(nsecs_t when, int32_t deviceId, int32_t type, - int32_t scanCode, int32_t keyCode, int32_t value, uint32_t flags) { - RawEvent event; - event.when = when; - event.deviceId = deviceId; - event.type = type; - event.scanCode = scanCode; - event.keyCode = keyCode; - event.value = value; - event.flags = flags; - mEvents.push_back(event); - } - - void assertQueueIsEmpty() { - ASSERT_EQ(size_t(0), mEvents.size()) - << "Expected the event queue to be empty (fully consumed)."; - } - -private: - Device* getDevice(int32_t deviceId) const { - ssize_t index = mDevices.indexOfKey(deviceId); - return index >= 0 ? mDevices.valueAt(index) : NULL; - } - - virtual uint32_t getDeviceClasses(int32_t deviceId) const { - Device* device = getDevice(deviceId); - return device ? device->classes : 0; - } - - virtual String8 getDeviceName(int32_t deviceId) const { - Device* device = getDevice(deviceId); - return device ? device->name : String8("unknown"); - } - - virtual status_t getAbsoluteAxisInfo(int32_t deviceId, int axis, - RawAbsoluteAxisInfo* outAxisInfo) const { - Device* device = getDevice(deviceId); - if (device) { - ssize_t index = device->axes.indexOfKey(axis); - if (index >= 0) { - *outAxisInfo = device->axes.valueAt(index); - return OK; - } - } - return -1; - } - - virtual status_t scancodeToKeycode(int32_t deviceId, int scancode, - int32_t* outKeycode, uint32_t* outFlags) const { - Device* device = getDevice(deviceId); - if (device) { - ssize_t index = device->keys.indexOfKey(scancode); - if (index >= 0) { - if (outKeycode) { - *outKeycode = device->keys.valueAt(index).keyCode; - } - if (outFlags) { - *outFlags = device->keys.valueAt(index).flags; - } - return OK; - } - } - return NAME_NOT_FOUND; - } - - virtual void addExcludedDevice(const char* deviceName) { - mExcludedDevices.add(String8(deviceName)); - } - - virtual bool getEvent(RawEvent* outEvent) { - if (mEvents.empty()) { - return false; - } - - *outEvent = *mEvents.begin(); - mEvents.erase(mEvents.begin()); - return true; - } - - virtual int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const { - Device* device = getDevice(deviceId); - if (device) { - ssize_t index = device->scanCodeStates.indexOfKey(scanCode); - if (index >= 0) { - return device->scanCodeStates.valueAt(index); - } - } - return AKEY_STATE_UNKNOWN; - } - - virtual int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const { - Device* device = getDevice(deviceId); - if (device) { - ssize_t index = device->keyCodeStates.indexOfKey(keyCode); - if (index >= 0) { - return device->keyCodeStates.valueAt(index); - } - } - return AKEY_STATE_UNKNOWN; - } - - virtual int32_t getSwitchState(int32_t deviceId, int32_t sw) const { - Device* device = getDevice(deviceId); - if (device) { - ssize_t index = device->switchStates.indexOfKey(sw); - if (index >= 0) { - return device->switchStates.valueAt(index); - } - } - return AKEY_STATE_UNKNOWN; - } - - virtual bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const int32_t* keyCodes, - uint8_t* outFlags) const { - bool result = false; - Device* device = getDevice(deviceId); - if (device) { - for (size_t i = 0; i < numCodes; i++) { - for (size_t j = 0; j < device->keys.size(); j++) { - if (keyCodes[i] == device->keys.valueAt(j).keyCode) { - outFlags[i] = 1; - result = true; - } - } - } - } - return result; - } - - virtual void dump(String8& dump) { - } -}; - - -// --- FakeInputReaderContext --- - -class FakeInputReaderContext : public InputReaderContext { - sp<EventHubInterface> mEventHub; - sp<InputReaderPolicyInterface> mPolicy; - sp<InputDispatcherInterface> mDispatcher; - int32_t mGlobalMetaState; - bool mUpdateGlobalMetaStateWasCalled; - -public: - FakeInputReaderContext(const sp<EventHubInterface>& eventHub, - const sp<InputReaderPolicyInterface>& policy, - const sp<InputDispatcherInterface>& dispatcher) : - mEventHub(eventHub), mPolicy(policy), mDispatcher(dispatcher), - mGlobalMetaState(0) { - } - - virtual ~FakeInputReaderContext() { } - - void assertUpdateGlobalMetaStateWasCalled() { - ASSERT_TRUE(mUpdateGlobalMetaStateWasCalled) - << "Expected updateGlobalMetaState() to have been called."; - mUpdateGlobalMetaStateWasCalled = false; - } - - void setGlobalMetaState(int32_t state) { - mGlobalMetaState = state; - } - -private: - virtual void updateGlobalMetaState() { - mUpdateGlobalMetaStateWasCalled = true; - } - - virtual int32_t getGlobalMetaState() { - return mGlobalMetaState; - } - - virtual EventHubInterface* getEventHub() { - return mEventHub.get(); - } - - virtual InputReaderPolicyInterface* getPolicy() { - return mPolicy.get(); - } - - virtual InputDispatcherInterface* getDispatcher() { - return mDispatcher.get(); - } - - virtual void disableVirtualKeysUntil(nsecs_t time) { - } - - virtual bool shouldDropVirtualKey(nsecs_t now, - InputDevice* device, int32_t keyCode, int32_t scanCode) { - return false; - } -}; - - -// --- FakeInputMapper --- - -class FakeInputMapper : public InputMapper { - uint32_t mSources; - int32_t mKeyboardType; - int32_t mMetaState; - KeyedVector<int32_t, int32_t> mKeyCodeStates; - KeyedVector<int32_t, int32_t> mScanCodeStates; - KeyedVector<int32_t, int32_t> mSwitchStates; - Vector<int32_t> mSupportedKeyCodes; - RawEvent mLastEvent; - - bool mConfigureWasCalled; - bool mResetWasCalled; - bool mProcessWasCalled; - -public: - FakeInputMapper(InputDevice* device, uint32_t sources) : - InputMapper(device), - mSources(sources), mKeyboardType(AINPUT_KEYBOARD_TYPE_NONE), - mMetaState(0), - mConfigureWasCalled(false), mResetWasCalled(false), mProcessWasCalled(false) { - } - - virtual ~FakeInputMapper() { } - - void setKeyboardType(int32_t keyboardType) { - mKeyboardType = keyboardType; - } - - void setMetaState(int32_t metaState) { - mMetaState = metaState; - } - - void assertConfigureWasCalled() { - ASSERT_TRUE(mConfigureWasCalled) - << "Expected configure() to have been called."; - mConfigureWasCalled = false; - } - - void assertResetWasCalled() { - ASSERT_TRUE(mResetWasCalled) - << "Expected reset() to have been called."; - mResetWasCalled = false; - } - - void assertProcessWasCalled(RawEvent* outLastEvent = NULL) { - ASSERT_TRUE(mProcessWasCalled) - << "Expected process() to have been called."; - if (outLastEvent) { - *outLastEvent = mLastEvent; - } - mProcessWasCalled = false; - } - - void setKeyCodeState(int32_t keyCode, int32_t state) { - mKeyCodeStates.replaceValueFor(keyCode, state); - } - - void setScanCodeState(int32_t scanCode, int32_t state) { - mScanCodeStates.replaceValueFor(scanCode, state); - } - - void setSwitchState(int32_t switchCode, int32_t state) { - mSwitchStates.replaceValueFor(switchCode, state); - } - - void addSupportedKeyCode(int32_t keyCode) { - mSupportedKeyCodes.add(keyCode); - } - -private: - virtual uint32_t getSources() { - return mSources; - } - - virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) { - InputMapper::populateDeviceInfo(deviceInfo); - - if (mKeyboardType != AINPUT_KEYBOARD_TYPE_NONE) { - deviceInfo->setKeyboardType(mKeyboardType); - } - } - - virtual void configure() { - mConfigureWasCalled = true; - } - - virtual void reset() { - mResetWasCalled = true; - } - - virtual void process(const RawEvent* rawEvent) { - mLastEvent = *rawEvent; - mProcessWasCalled = true; - } - - virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode) { - ssize_t index = mKeyCodeStates.indexOfKey(keyCode); - return index >= 0 ? mKeyCodeStates.valueAt(index) : AKEY_STATE_UNKNOWN; - } - - virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode) { - ssize_t index = mScanCodeStates.indexOfKey(scanCode); - return index >= 0 ? mScanCodeStates.valueAt(index) : AKEY_STATE_UNKNOWN; - } - - virtual int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode) { - ssize_t index = mSwitchStates.indexOfKey(switchCode); - return index >= 0 ? mSwitchStates.valueAt(index) : AKEY_STATE_UNKNOWN; - } - - virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, - const int32_t* keyCodes, uint8_t* outFlags) { - bool result = false; - for (size_t i = 0; i < numCodes; i++) { - for (size_t j = 0; j < mSupportedKeyCodes.size(); j++) { - if (keyCodes[i] == mSupportedKeyCodes[j]) { - outFlags[i] = 1; - result = true; - } - } - } - return result; - } - - virtual int32_t getMetaState() { - return mMetaState; - } -}; - - -// --- InstrumentedInputReader --- - -class InstrumentedInputReader : public InputReader { - InputDevice* mNextDevice; - -public: - InstrumentedInputReader(const sp<EventHubInterface>& eventHub, - const sp<InputReaderPolicyInterface>& policy, - const sp<InputDispatcherInterface>& dispatcher) : - InputReader(eventHub, policy, dispatcher) { - } - - virtual ~InstrumentedInputReader() { - if (mNextDevice) { - delete mNextDevice; - } - } - - void setNextDevice(InputDevice* device) { - mNextDevice = device; - } - -protected: - virtual InputDevice* createDevice(int32_t deviceId, const String8& name, uint32_t classes) { - if (mNextDevice) { - InputDevice* device = mNextDevice; - mNextDevice = NULL; - return device; - } - return InputReader::createDevice(deviceId, name, classes); - } - - friend class InputReaderTest; -}; - - -// --- InputReaderTest --- - -class InputReaderTest : public testing::Test { -protected: - sp<FakeInputDispatcher> mFakeDispatcher; - sp<FakeInputReaderPolicy> mFakePolicy; - sp<FakeEventHub> mFakeEventHub; - sp<InstrumentedInputReader> mReader; - - virtual void SetUp() { - mFakeEventHub = new FakeEventHub(); - mFakePolicy = new FakeInputReaderPolicy(); - mFakeDispatcher = new FakeInputDispatcher(); - - mReader = new InstrumentedInputReader(mFakeEventHub, mFakePolicy, mFakeDispatcher); - } - - virtual void TearDown() { - mReader.clear(); - - mFakeDispatcher.clear(); - mFakePolicy.clear(); - mFakeEventHub.clear(); - } - - void addDevice(int32_t deviceId, const String8& name, uint32_t classes) { - mFakeEventHub->addDevice(deviceId, name, classes); - mFakeEventHub->finishDeviceScan(); - mReader->loopOnce(); - mReader->loopOnce(); - mFakeEventHub->assertQueueIsEmpty(); - } - - FakeInputMapper* addDeviceWithFakeInputMapper(int32_t deviceId, - const String8& name, uint32_t classes, uint32_t sources) { - InputDevice* device = new InputDevice(mReader.get(), deviceId, name); - FakeInputMapper* mapper = new FakeInputMapper(device, sources); - device->addMapper(mapper); - mReader->setNextDevice(device); - addDevice(deviceId, name, classes); - return mapper; - } -}; - -TEST_F(InputReaderTest, GetInputConfiguration_WhenNoDevices_ReturnsDefaults) { - InputConfiguration config; - mReader->getInputConfiguration(&config); - - ASSERT_EQ(InputConfiguration::KEYBOARD_NOKEYS, config.keyboard); - ASSERT_EQ(InputConfiguration::NAVIGATION_NONAV, config.navigation); - ASSERT_EQ(InputConfiguration::TOUCHSCREEN_NOTOUCH, config.touchScreen); -} - -TEST_F(InputReaderTest, GetInputConfiguration_WhenAlphabeticKeyboardPresent_ReturnsQwertyKeyboard) { - ASSERT_NO_FATAL_FAILURE(addDevice(0, String8("keyboard"), - INPUT_DEVICE_CLASS_KEYBOARD | INPUT_DEVICE_CLASS_ALPHAKEY)); - - InputConfiguration config; - mReader->getInputConfiguration(&config); - - ASSERT_EQ(InputConfiguration::KEYBOARD_QWERTY, config.keyboard); - ASSERT_EQ(InputConfiguration::NAVIGATION_NONAV, config.navigation); - ASSERT_EQ(InputConfiguration::TOUCHSCREEN_NOTOUCH, config.touchScreen); -} - -TEST_F(InputReaderTest, GetInputConfiguration_WhenTouchScreenPresent_ReturnsFingerTouchScreen) { - ASSERT_NO_FATAL_FAILURE(addDevice(0, String8("touchscreen"), - INPUT_DEVICE_CLASS_TOUCHSCREEN)); - - InputConfiguration config; - mReader->getInputConfiguration(&config); - - ASSERT_EQ(InputConfiguration::KEYBOARD_NOKEYS, config.keyboard); - ASSERT_EQ(InputConfiguration::NAVIGATION_NONAV, config.navigation); - ASSERT_EQ(InputConfiguration::TOUCHSCREEN_FINGER, config.touchScreen); -} - -TEST_F(InputReaderTest, GetInputConfiguration_WhenTrackballPresent_ReturnsTrackballNavigation) { - ASSERT_NO_FATAL_FAILURE(addDevice(0, String8("trackball"), - INPUT_DEVICE_CLASS_TRACKBALL)); - - InputConfiguration config; - mReader->getInputConfiguration(&config); - - ASSERT_EQ(InputConfiguration::KEYBOARD_NOKEYS, config.keyboard); - ASSERT_EQ(InputConfiguration::NAVIGATION_TRACKBALL, config.navigation); - ASSERT_EQ(InputConfiguration::TOUCHSCREEN_NOTOUCH, config.touchScreen); -} - -TEST_F(InputReaderTest, GetInputConfiguration_WhenDPadPresent_ReturnsDPadNavigation) { - ASSERT_NO_FATAL_FAILURE(addDevice(0, String8("dpad"), - INPUT_DEVICE_CLASS_DPAD)); - - InputConfiguration config; - mReader->getInputConfiguration(&config); - - ASSERT_EQ(InputConfiguration::KEYBOARD_NOKEYS, config.keyboard); - ASSERT_EQ(InputConfiguration::NAVIGATION_DPAD, config.navigation); - ASSERT_EQ(InputConfiguration::TOUCHSCREEN_NOTOUCH, config.touchScreen); -} - -TEST_F(InputReaderTest, GetInputDeviceInfo_WhenDeviceIdIsValid) { - ASSERT_NO_FATAL_FAILURE(addDevice(1, String8("keyboard"), - INPUT_DEVICE_CLASS_KEYBOARD)); - - InputDeviceInfo info; - status_t result = mReader->getInputDeviceInfo(1, &info); - - ASSERT_EQ(OK, result); - ASSERT_EQ(1, info.getId()); - ASSERT_STREQ("keyboard", info.getName().string()); - ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC, info.getKeyboardType()); - ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, info.getSources()); - ASSERT_EQ(size_t(0), info.getMotionRanges().size()); -} - -TEST_F(InputReaderTest, GetInputDeviceInfo_WhenDeviceIdIsInvalid) { - InputDeviceInfo info; - status_t result = mReader->getInputDeviceInfo(-1, &info); - - ASSERT_EQ(NAME_NOT_FOUND, result); -} - -TEST_F(InputReaderTest, GetInputDeviceInfo_WhenDeviceIdIsIgnored) { - addDevice(1, String8("ignored"), 0); // no classes so device will be ignored - - InputDeviceInfo info; - status_t result = mReader->getInputDeviceInfo(1, &info); - - ASSERT_EQ(NAME_NOT_FOUND, result); -} - -TEST_F(InputReaderTest, GetInputDeviceIds) { - ASSERT_NO_FATAL_FAILURE(addDevice(1, String8("keyboard"), - INPUT_DEVICE_CLASS_KEYBOARD | INPUT_DEVICE_CLASS_ALPHAKEY)); - ASSERT_NO_FATAL_FAILURE(addDevice(2, String8("trackball"), - INPUT_DEVICE_CLASS_TRACKBALL)); - - Vector<int32_t> ids; - mReader->getInputDeviceIds(ids); - - ASSERT_EQ(size_t(2), ids.size()); - ASSERT_EQ(1, ids[0]); - ASSERT_EQ(2, ids[1]); -} - -TEST_F(InputReaderTest, GetKeyCodeState_ForwardsRequestsToMappers) { - FakeInputMapper* mapper = NULL; - ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, String8("fake"), - INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD)); - mapper->setKeyCodeState(AKEYCODE_A, AKEY_STATE_DOWN); - - ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getKeyCodeState(0, - AINPUT_SOURCE_ANY, AKEYCODE_A)) - << "Should return unknown when the device id is >= 0 but unknown."; - - ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getKeyCodeState(1, - AINPUT_SOURCE_TRACKBALL, AKEYCODE_A)) - << "Should return unknown when the device id is valid but the sources are not supported by the device."; - - ASSERT_EQ(AKEY_STATE_DOWN, mReader->getKeyCodeState(1, - AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TRACKBALL, AKEYCODE_A)) - << "Should return value provided by mapper when device id is valid and the device supports some of the sources."; - - ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getKeyCodeState(-1, - AINPUT_SOURCE_TRACKBALL, AKEYCODE_A)) - << "Should return unknown when the device id is < 0 but the sources are not supported by any device."; - - ASSERT_EQ(AKEY_STATE_DOWN, mReader->getKeyCodeState(-1, - AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TRACKBALL, AKEYCODE_A)) - << "Should return value provided by mapper when device id is < 0 and one of the devices supports some of the sources."; -} - -TEST_F(InputReaderTest, GetScanCodeState_ForwardsRequestsToMappers) { - FakeInputMapper* mapper = NULL; - ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, String8("fake"), - INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD)); - mapper->setScanCodeState(KEY_A, AKEY_STATE_DOWN); - - ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getScanCodeState(0, - AINPUT_SOURCE_ANY, KEY_A)) - << "Should return unknown when the device id is >= 0 but unknown."; - - ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getScanCodeState(1, - AINPUT_SOURCE_TRACKBALL, KEY_A)) - << "Should return unknown when the device id is valid but the sources are not supported by the device."; - - ASSERT_EQ(AKEY_STATE_DOWN, mReader->getScanCodeState(1, - AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TRACKBALL, KEY_A)) - << "Should return value provided by mapper when device id is valid and the device supports some of the sources."; - - ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getScanCodeState(-1, - AINPUT_SOURCE_TRACKBALL, KEY_A)) - << "Should return unknown when the device id is < 0 but the sources are not supported by any device."; - - ASSERT_EQ(AKEY_STATE_DOWN, mReader->getScanCodeState(-1, - AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TRACKBALL, KEY_A)) - << "Should return value provided by mapper when device id is < 0 and one of the devices supports some of the sources."; -} - -TEST_F(InputReaderTest, GetSwitchState_ForwardsRequestsToMappers) { - FakeInputMapper* mapper = NULL; - ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, String8("fake"), - INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD)); - mapper->setSwitchState(SW_LID, AKEY_STATE_DOWN); - - ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getSwitchState(0, - AINPUT_SOURCE_ANY, SW_LID)) - << "Should return unknown when the device id is >= 0 but unknown."; - - ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getSwitchState(1, - AINPUT_SOURCE_TRACKBALL, SW_LID)) - << "Should return unknown when the device id is valid but the sources are not supported by the device."; - - ASSERT_EQ(AKEY_STATE_DOWN, mReader->getSwitchState(1, - AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TRACKBALL, SW_LID)) - << "Should return value provided by mapper when device id is valid and the device supports some of the sources."; - - ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getSwitchState(-1, - AINPUT_SOURCE_TRACKBALL, SW_LID)) - << "Should return unknown when the device id is < 0 but the sources are not supported by any device."; - - ASSERT_EQ(AKEY_STATE_DOWN, mReader->getSwitchState(-1, - AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TRACKBALL, SW_LID)) - << "Should return value provided by mapper when device id is < 0 and one of the devices supports some of the sources."; -} - -TEST_F(InputReaderTest, MarkSupportedKeyCodes_ForwardsRequestsToMappers) { - FakeInputMapper* mapper = NULL; - ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, String8("fake"), - INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD)); - mapper->addSupportedKeyCode(AKEYCODE_A); - mapper->addSupportedKeyCode(AKEYCODE_B); - - const int32_t keyCodes[4] = { AKEYCODE_A, AKEYCODE_B, AKEYCODE_1, AKEYCODE_2 }; - uint8_t flags[4] = { 0, 0, 0, 1 }; - - ASSERT_FALSE(mReader->hasKeys(0, AINPUT_SOURCE_ANY, 4, keyCodes, flags)) - << "Should return false when device id is >= 0 but unknown."; - ASSERT_TRUE(!flags[0] && !flags[1] && !flags[2] && !flags[3]); - - flags[3] = 1; - ASSERT_FALSE(mReader->hasKeys(1, AINPUT_SOURCE_TRACKBALL, 4, keyCodes, flags)) - << "Should return false when device id is valid but the sources are not supported by the device."; - ASSERT_TRUE(!flags[0] && !flags[1] && !flags[2] && !flags[3]); - - flags[3] = 1; - ASSERT_TRUE(mReader->hasKeys(1, AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TRACKBALL, 4, keyCodes, flags)) - << "Should return value provided by mapper when device id is valid and the device supports some of the sources."; - ASSERT_TRUE(flags[0] && flags[1] && !flags[2] && !flags[3]); - - flags[3] = 1; - ASSERT_FALSE(mReader->hasKeys(-1, AINPUT_SOURCE_TRACKBALL, 4, keyCodes, flags)) - << "Should return false when the device id is < 0 but the sources are not supported by any device."; - ASSERT_TRUE(!flags[0] && !flags[1] && !flags[2] && !flags[3]); - - flags[3] = 1; - ASSERT_TRUE(mReader->hasKeys(-1, AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TRACKBALL, 4, keyCodes, flags)) - << "Should return value provided by mapper when device id is < 0 and one of the devices supports some of the sources."; - ASSERT_TRUE(flags[0] && flags[1] && !flags[2] && !flags[3]); -} - -TEST_F(InputReaderTest, LoopOnce_WhenDeviceScanFinished_SendsConfigurationChanged) { - addDevice(1, String8("ignored"), INPUT_DEVICE_CLASS_KEYBOARD); - - FakeInputDispatcher::NotifyConfigurationChangedArgs args; - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyConfigurationChangedWasCalled(&args)); - ASSERT_EQ(ARBITRARY_TIME, args.eventTime); -} - -TEST_F(InputReaderTest, LoopOnce_ForwardsRawEventsToMappers) { - FakeInputMapper* mapper = NULL; - ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, String8("fake"), - INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD)); - - mFakeEventHub->enqueueEvent(0, 1, EV_KEY, KEY_A, AKEYCODE_A, 1, POLICY_FLAG_WAKE); - mReader->loopOnce(); - ASSERT_NO_FATAL_FAILURE(mFakeEventHub->assertQueueIsEmpty()); - - RawEvent event; - ASSERT_NO_FATAL_FAILURE(mapper->assertProcessWasCalled(&event)); - ASSERT_EQ(0, event.when); - ASSERT_EQ(1, event.deviceId); - ASSERT_EQ(EV_KEY, event.type); - ASSERT_EQ(KEY_A, event.scanCode); - ASSERT_EQ(AKEYCODE_A, event.keyCode); - ASSERT_EQ(1, event.value); - ASSERT_EQ(POLICY_FLAG_WAKE, event.flags); -} - - -// --- InputDeviceTest --- - -class InputDeviceTest : public testing::Test { -protected: - static const char* DEVICE_NAME; - static const int32_t DEVICE_ID; - - sp<FakeEventHub> mFakeEventHub; - sp<FakeInputReaderPolicy> mFakePolicy; - sp<FakeInputDispatcher> mFakeDispatcher; - FakeInputReaderContext* mFakeContext; - - InputDevice* mDevice; - - virtual void SetUp() { - mFakeEventHub = new FakeEventHub(); - mFakePolicy = new FakeInputReaderPolicy(); - mFakeDispatcher = new FakeInputDispatcher(); - mFakeContext = new FakeInputReaderContext(mFakeEventHub, mFakePolicy, mFakeDispatcher); - - mDevice = new InputDevice(mFakeContext, DEVICE_ID, String8(DEVICE_NAME)); - } - - virtual void TearDown() { - delete mDevice; - - delete mFakeContext; - mFakeDispatcher.clear(); - mFakePolicy.clear(); - mFakeEventHub.clear(); - } -}; - -const char* InputDeviceTest::DEVICE_NAME = "device"; -const int32_t InputDeviceTest::DEVICE_ID = 1; - -TEST_F(InputDeviceTest, ImmutableProperties) { - ASSERT_EQ(DEVICE_ID, mDevice->getId()); - ASSERT_STREQ(DEVICE_NAME, mDevice->getName()); -} - -TEST_F(InputDeviceTest, WhenNoMappersAreRegistered_DeviceIsIgnored) { - // Configuration. - mDevice->configure(); - - // Metadata. - ASSERT_TRUE(mDevice->isIgnored()); - ASSERT_EQ(AINPUT_SOURCE_UNKNOWN, mDevice->getSources()); - - InputDeviceInfo info; - mDevice->getDeviceInfo(&info); - ASSERT_EQ(DEVICE_ID, info.getId()); - ASSERT_STREQ(DEVICE_NAME, info.getName().string()); - ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NONE, info.getKeyboardType()); - ASSERT_EQ(AINPUT_SOURCE_UNKNOWN, info.getSources()); - - // State queries. - ASSERT_EQ(0, mDevice->getMetaState()); - - ASSERT_EQ(AKEY_STATE_UNKNOWN, mDevice->getKeyCodeState(AINPUT_SOURCE_KEYBOARD, 0)) - << "Ignored device should return unknown key code state."; - ASSERT_EQ(AKEY_STATE_UNKNOWN, mDevice->getScanCodeState(AINPUT_SOURCE_KEYBOARD, 0)) - << "Ignored device should return unknown scan code state."; - ASSERT_EQ(AKEY_STATE_UNKNOWN, mDevice->getSwitchState(AINPUT_SOURCE_KEYBOARD, 0)) - << "Ignored device should return unknown switch state."; - - const int32_t keyCodes[2] = { AKEYCODE_A, AKEYCODE_B }; - uint8_t flags[2] = { 0, 1 }; - ASSERT_FALSE(mDevice->markSupportedKeyCodes(AINPUT_SOURCE_KEYBOARD, 2, keyCodes, flags)) - << "Ignored device should never mark any key codes."; - ASSERT_EQ(0, flags[0]) << "Flag for unsupported key should be unchanged."; - ASSERT_EQ(1, flags[1]) << "Flag for unsupported key should be unchanged."; - - // Reset. - mDevice->reset(); -} - -TEST_F(InputDeviceTest, WhenMappersAreRegistered_DeviceIsNotIgnoredAndForwardsRequestsToMappers) { - // Configuration. - InputDeviceCalibration calibration; - calibration.addProperty(String8("key"), String8("value")); - mFakePolicy->addInputDeviceCalibration(String8(DEVICE_NAME), calibration); - - FakeInputMapper* mapper1 = new FakeInputMapper(mDevice, AINPUT_SOURCE_KEYBOARD); - mapper1->setKeyboardType(AINPUT_KEYBOARD_TYPE_ALPHABETIC); - mapper1->setMetaState(AMETA_ALT_ON); - mapper1->addSupportedKeyCode(AKEYCODE_A); - mapper1->addSupportedKeyCode(AKEYCODE_B); - mapper1->setKeyCodeState(AKEYCODE_A, AKEY_STATE_DOWN); - mapper1->setKeyCodeState(AKEYCODE_B, AKEY_STATE_UP); - mapper1->setScanCodeState(2, AKEY_STATE_DOWN); - mapper1->setScanCodeState(3, AKEY_STATE_UP); - mapper1->setSwitchState(4, AKEY_STATE_DOWN); - mDevice->addMapper(mapper1); - - FakeInputMapper* mapper2 = new FakeInputMapper(mDevice, AINPUT_SOURCE_TOUCHSCREEN); - mapper2->setMetaState(AMETA_SHIFT_ON); - mDevice->addMapper(mapper2); - - mDevice->configure(); - - String8 propertyValue; - ASSERT_TRUE(mDevice->getCalibration().tryGetProperty(String8("key"), propertyValue)) - << "Device should have read calibration during configuration phase."; - ASSERT_STREQ("value", propertyValue.string()); - - ASSERT_NO_FATAL_FAILURE(mapper1->assertConfigureWasCalled()); - ASSERT_NO_FATAL_FAILURE(mapper2->assertConfigureWasCalled()); - - // Metadata. - ASSERT_FALSE(mDevice->isIgnored()); - ASSERT_EQ(uint32_t(AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TOUCHSCREEN), mDevice->getSources()); - - InputDeviceInfo info; - mDevice->getDeviceInfo(&info); - ASSERT_EQ(DEVICE_ID, info.getId()); - ASSERT_STREQ(DEVICE_NAME, info.getName().string()); - ASSERT_EQ(AINPUT_KEYBOARD_TYPE_ALPHABETIC, info.getKeyboardType()); - ASSERT_EQ(uint32_t(AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TOUCHSCREEN), info.getSources()); - - // State queries. - ASSERT_EQ(AMETA_ALT_ON | AMETA_SHIFT_ON, mDevice->getMetaState()) - << "Should query mappers and combine meta states."; - - ASSERT_EQ(AKEY_STATE_UNKNOWN, mDevice->getKeyCodeState(AINPUT_SOURCE_TRACKBALL, AKEYCODE_A)) - << "Should return unknown key code state when source not supported."; - ASSERT_EQ(AKEY_STATE_UNKNOWN, mDevice->getScanCodeState(AINPUT_SOURCE_TRACKBALL, AKEYCODE_A)) - << "Should return unknown scan code state when source not supported."; - ASSERT_EQ(AKEY_STATE_UNKNOWN, mDevice->getSwitchState(AINPUT_SOURCE_TRACKBALL, AKEYCODE_A)) - << "Should return unknown switch state when source not supported."; - - ASSERT_EQ(AKEY_STATE_DOWN, mDevice->getKeyCodeState(AINPUT_SOURCE_KEYBOARD, AKEYCODE_A)) - << "Should query mapper when source is supported."; - ASSERT_EQ(AKEY_STATE_UP, mDevice->getScanCodeState(AINPUT_SOURCE_KEYBOARD, 3)) - << "Should query mapper when source is supported."; - ASSERT_EQ(AKEY_STATE_DOWN, mDevice->getSwitchState(AINPUT_SOURCE_KEYBOARD, 4)) - << "Should query mapper when source is supported."; - - const int32_t keyCodes[4] = { AKEYCODE_A, AKEYCODE_B, AKEYCODE_1, AKEYCODE_2 }; - uint8_t flags[4] = { 0, 0, 0, 1 }; - ASSERT_FALSE(mDevice->markSupportedKeyCodes(AINPUT_SOURCE_TRACKBALL, 4, keyCodes, flags)) - << "Should do nothing when source is unsupported."; - ASSERT_EQ(0, flags[0]) << "Flag should be unchanged when source is unsupported."; - ASSERT_EQ(0, flags[1]) << "Flag should be unchanged when source is unsupported."; - ASSERT_EQ(0, flags[2]) << "Flag should be unchanged when source is unsupported."; - ASSERT_EQ(1, flags[3]) << "Flag should be unchanged when source is unsupported."; - - ASSERT_TRUE(mDevice->markSupportedKeyCodes(AINPUT_SOURCE_KEYBOARD, 4, keyCodes, flags)) - << "Should query mapper when source is supported."; - ASSERT_EQ(1, flags[0]) << "Flag for supported key should be set."; - ASSERT_EQ(1, flags[1]) << "Flag for supported key should be set."; - ASSERT_EQ(0, flags[2]) << "Flag for unsupported key should be unchanged."; - ASSERT_EQ(1, flags[3]) << "Flag for unsupported key should be unchanged."; - - // Event handling. - RawEvent event; - mDevice->process(&event); - - ASSERT_NO_FATAL_FAILURE(mapper1->assertProcessWasCalled()); - ASSERT_NO_FATAL_FAILURE(mapper2->assertProcessWasCalled()); - - // Reset. - mDevice->reset(); - - ASSERT_NO_FATAL_FAILURE(mapper1->assertResetWasCalled()); - ASSERT_NO_FATAL_FAILURE(mapper2->assertResetWasCalled()); -} - - -// --- InputMapperTest --- - -class InputMapperTest : public testing::Test { -protected: - static const char* DEVICE_NAME; - static const int32_t DEVICE_ID; - - sp<FakeEventHub> mFakeEventHub; - sp<FakeInputReaderPolicy> mFakePolicy; - sp<FakeInputDispatcher> mFakeDispatcher; - FakeInputReaderContext* mFakeContext; - InputDevice* mDevice; - - virtual void SetUp() { - mFakeEventHub = new FakeEventHub(); - mFakePolicy = new FakeInputReaderPolicy(); - mFakeDispatcher = new FakeInputDispatcher(); - mFakeContext = new FakeInputReaderContext(mFakeEventHub, mFakePolicy, mFakeDispatcher); - mDevice = new InputDevice(mFakeContext, DEVICE_ID, String8(DEVICE_NAME)); - - mFakeEventHub->addDevice(DEVICE_ID, String8(DEVICE_NAME), 0); - } - - virtual void TearDown() { - delete mDevice; - delete mFakeContext; - mFakeDispatcher.clear(); - mFakePolicy.clear(); - mFakeEventHub.clear(); - } - - void prepareCalibration(const char* key, const char* value) { - mFakePolicy->addInputDeviceCalibrationProperty(String8(DEVICE_NAME), - String8(key), String8(value)); - } - - void addMapperAndConfigure(InputMapper* mapper) { - mDevice->addMapper(mapper); - mDevice->configure(); - } - - static void process(InputMapper* mapper, nsecs_t when, int32_t deviceId, int32_t type, - int32_t scanCode, int32_t keyCode, int32_t value, uint32_t flags) { - RawEvent event; - event.when = when; - event.deviceId = deviceId; - event.type = type; - event.scanCode = scanCode; - event.keyCode = keyCode; - event.value = value; - event.flags = flags; - mapper->process(&event); - } - - static void assertMotionRange(const InputDeviceInfo& info, - int32_t rangeType, float min, float max, float flat, float fuzz) { - const InputDeviceInfo::MotionRange* range = info.getMotionRange(rangeType); - ASSERT_TRUE(range != NULL) << "Range: " << rangeType; - ASSERT_NEAR(min, range->min, EPSILON) << "Range: " << rangeType; - ASSERT_NEAR(max, range->max, EPSILON) << "Range: " << rangeType; - ASSERT_NEAR(flat, range->flat, EPSILON) << "Range: " << rangeType; - ASSERT_NEAR(fuzz, range->fuzz, EPSILON) << "Range: " << rangeType; - } - - static void assertPointerCoords(const PointerCoords& coords, - float x, float y, float pressure, float size, - float touchMajor, float touchMinor, float toolMajor, float toolMinor, - float orientation) { - ASSERT_NEAR(x, coords.x, 1); - ASSERT_NEAR(y, coords.y, 1); - ASSERT_NEAR(pressure, coords.pressure, EPSILON); - ASSERT_NEAR(size, coords.size, EPSILON); - ASSERT_NEAR(touchMajor, coords.touchMajor, 1); - ASSERT_NEAR(touchMinor, coords.touchMinor, 1); - ASSERT_NEAR(toolMajor, coords.toolMajor, 1); - ASSERT_NEAR(toolMinor, coords.toolMinor, 1); - ASSERT_NEAR(orientation, coords.orientation, EPSILON); - } -}; - -const char* InputMapperTest::DEVICE_NAME = "device"; -const int32_t InputMapperTest::DEVICE_ID = 1; - - -// --- SwitchInputMapperTest --- - -class SwitchInputMapperTest : public InputMapperTest { -protected: -}; - -TEST_F(SwitchInputMapperTest, GetSources) { - SwitchInputMapper* mapper = new SwitchInputMapper(mDevice); - addMapperAndConfigure(mapper); - - ASSERT_EQ(uint32_t(0), mapper->getSources()); -} - -TEST_F(SwitchInputMapperTest, GetSwitchState) { - SwitchInputMapper* mapper = new SwitchInputMapper(mDevice); - addMapperAndConfigure(mapper); - - mFakeEventHub->setSwitchState(DEVICE_ID, SW_LID, 1); - ASSERT_EQ(1, mapper->getSwitchState(AINPUT_SOURCE_ANY, SW_LID)); - - mFakeEventHub->setSwitchState(DEVICE_ID, SW_LID, 0); - ASSERT_EQ(0, mapper->getSwitchState(AINPUT_SOURCE_ANY, SW_LID)); -} - -TEST_F(SwitchInputMapperTest, Process) { - SwitchInputMapper* mapper = new SwitchInputMapper(mDevice); - addMapperAndConfigure(mapper); - - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SW, SW_LID, 0, 1, 0); - - FakeInputDispatcher::NotifySwitchArgs args; - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifySwitchWasCalled(&args)); - ASSERT_EQ(ARBITRARY_TIME, args.when); - ASSERT_EQ(SW_LID, args.switchCode); - ASSERT_EQ(1, args.switchValue); - ASSERT_EQ(uint32_t(0), args.policyFlags); -} - - -// --- KeyboardInputMapperTest --- - -class KeyboardInputMapperTest : public InputMapperTest { -protected: - void testDPadKeyRotation(KeyboardInputMapper* mapper, - int32_t originalScanCode, int32_t originalKeyCode, int32_t rotatedKeyCode); -}; - -void KeyboardInputMapperTest::testDPadKeyRotation(KeyboardInputMapper* mapper, - int32_t originalScanCode, int32_t originalKeyCode, int32_t rotatedKeyCode) { - FakeInputDispatcher::NotifyKeyArgs args; - - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, originalScanCode, originalKeyCode, 1, 0); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action); - ASSERT_EQ(originalScanCode, args.scanCode); - ASSERT_EQ(rotatedKeyCode, args.keyCode); - - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, originalScanCode, originalKeyCode, 0, 0); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action); - ASSERT_EQ(originalScanCode, args.scanCode); - ASSERT_EQ(rotatedKeyCode, args.keyCode); -} - - -TEST_F(KeyboardInputMapperTest, GetSources) { - KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, -1, - AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC); - addMapperAndConfigure(mapper); - - ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, mapper->getSources()); -} - -TEST_F(KeyboardInputMapperTest, Process_SimpleKeyPress) { - KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, -1, - AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC); - addMapperAndConfigure(mapper); - - // Key down. - process(mapper, ARBITRARY_TIME, DEVICE_ID, - EV_KEY, KEY_HOME, AKEYCODE_HOME, 1, POLICY_FLAG_WAKE); - FakeInputDispatcher::NotifyKeyArgs args; - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(DEVICE_ID, args.deviceId); - ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source); - ASSERT_EQ(ARBITRARY_TIME, args.eventTime); - ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action); - ASSERT_EQ(AKEYCODE_HOME, args.keyCode); - ASSERT_EQ(KEY_HOME, args.scanCode); - ASSERT_EQ(AMETA_NONE, args.metaState); - ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, args.flags); - ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags); - ASSERT_EQ(ARBITRARY_TIME, args.downTime); - - // Key up. - process(mapper, ARBITRARY_TIME + 1, DEVICE_ID, - EV_KEY, KEY_HOME, AKEYCODE_HOME, 0, POLICY_FLAG_WAKE); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(DEVICE_ID, args.deviceId); - ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source); - ASSERT_EQ(ARBITRARY_TIME + 1, args.eventTime); - ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action); - ASSERT_EQ(AKEYCODE_HOME, args.keyCode); - ASSERT_EQ(KEY_HOME, args.scanCode); - ASSERT_EQ(AMETA_NONE, args.metaState); - ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, args.flags); - ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags); - ASSERT_EQ(ARBITRARY_TIME, args.downTime); -} - -TEST_F(KeyboardInputMapperTest, Reset_WhenKeysAreNotDown_DoesNotSynthesizeKeyUp) { - KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, -1, - AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC); - addMapperAndConfigure(mapper); - - // Key down. - process(mapper, ARBITRARY_TIME, DEVICE_ID, - EV_KEY, KEY_HOME, AKEYCODE_HOME, 1, POLICY_FLAG_WAKE); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled()); - - // Key up. - process(mapper, ARBITRARY_TIME, DEVICE_ID, - EV_KEY, KEY_HOME, AKEYCODE_HOME, 0, POLICY_FLAG_WAKE); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled()); - - // Reset. Since no keys still down, should not synthesize any key ups. - mapper->reset(); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasNotCalled()); -} - -TEST_F(KeyboardInputMapperTest, Reset_WhenKeysAreDown_SynthesizesKeyUps) { - KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, -1, - AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC); - addMapperAndConfigure(mapper); - - // Metakey down. - process(mapper, ARBITRARY_TIME, DEVICE_ID, - EV_KEY, KEY_LEFTSHIFT, AKEYCODE_SHIFT_LEFT, 1, 0); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled()); - - // Key down. - process(mapper, ARBITRARY_TIME + 1, DEVICE_ID, - EV_KEY, KEY_A, AKEYCODE_A, 1, 0); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled()); - - // Reset. Since two keys are still down, should synthesize two key ups in reverse order. - mapper->reset(); - - FakeInputDispatcher::NotifyKeyArgs args; - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(DEVICE_ID, args.deviceId); - ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source); - ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action); - ASSERT_EQ(AKEYCODE_A, args.keyCode); - ASSERT_EQ(KEY_A, args.scanCode); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState); - ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, args.flags); - ASSERT_EQ(uint32_t(0), args.policyFlags); - ASSERT_EQ(ARBITRARY_TIME + 1, args.downTime); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(DEVICE_ID, args.deviceId); - ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source); - ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action); - ASSERT_EQ(AKEYCODE_SHIFT_LEFT, args.keyCode); - ASSERT_EQ(KEY_LEFTSHIFT, args.scanCode); - ASSERT_EQ(AMETA_NONE, args.metaState); - ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, args.flags); - ASSERT_EQ(uint32_t(0), args.policyFlags); - ASSERT_EQ(ARBITRARY_TIME + 1, args.downTime); - - // And that's it. - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasNotCalled()); -} - -TEST_F(KeyboardInputMapperTest, Process_ShouldUpdateMetaState) { - KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, -1, - AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC); - addMapperAndConfigure(mapper); - - // Initial metastate. - ASSERT_EQ(AMETA_NONE, mapper->getMetaState()); - - // Metakey down. - process(mapper, ARBITRARY_TIME, DEVICE_ID, - EV_KEY, KEY_LEFTSHIFT, AKEYCODE_SHIFT_LEFT, 1, 0); - FakeInputDispatcher::NotifyKeyArgs args; - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mapper->getMetaState()); - ASSERT_NO_FATAL_FAILURE(mFakeContext->assertUpdateGlobalMetaStateWasCalled()); - - // Key down. - process(mapper, ARBITRARY_TIME + 1, DEVICE_ID, - EV_KEY, KEY_A, AKEYCODE_A, 1, 0); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mapper->getMetaState()); - - // Key up. - process(mapper, ARBITRARY_TIME + 2, DEVICE_ID, - EV_KEY, KEY_A, AKEYCODE_A, 0, 0); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mapper->getMetaState()); - - // Metakey up. - process(mapper, ARBITRARY_TIME + 3, DEVICE_ID, - EV_KEY, KEY_LEFTSHIFT, AKEYCODE_SHIFT_LEFT, 0, 0); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(AMETA_NONE, args.metaState); - ASSERT_EQ(AMETA_NONE, mapper->getMetaState()); - ASSERT_NO_FATAL_FAILURE(mFakeContext->assertUpdateGlobalMetaStateWasCalled()); -} - -TEST_F(KeyboardInputMapperTest, Process_WhenNotAttachedToDisplay_ShouldNotRotateDPad) { - KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, -1, - AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC); - addMapperAndConfigure(mapper); - - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_RIGHT)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_DOWN)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_LEFT)); -} - -TEST_F(KeyboardInputMapperTest, Process_WhenAttachedToDisplay_ShouldRotateDPad) { - KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, DISPLAY_ID, - AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC); - addMapperAndConfigure(mapper); - - mFakePolicy->setDisplayInfo(DISPLAY_ID, - DISPLAY_WIDTH, DISPLAY_HEIGHT, - InputReaderPolicyInterface::ROTATION_0); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_RIGHT)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_DOWN)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_LEFT)); - - mFakePolicy->setDisplayInfo(DISPLAY_ID, - DISPLAY_WIDTH, DISPLAY_HEIGHT, - InputReaderPolicyInterface::ROTATION_90); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN)); - - mFakePolicy->setDisplayInfo(DISPLAY_ID, - DISPLAY_WIDTH, DISPLAY_HEIGHT, - InputReaderPolicyInterface::ROTATION_180); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_DOWN)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_LEFT)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_UP)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_RIGHT)); - - mFakePolicy->setDisplayInfo(DISPLAY_ID, - DISPLAY_WIDTH, DISPLAY_HEIGHT, - InputReaderPolicyInterface::ROTATION_270); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_RIGHT)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_DOWN)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_LEFT)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_UP)); - - // Special case: if orientation changes while key is down, we still emit the same keycode - // in the key up as we did in the key down. - FakeInputDispatcher::NotifyKeyArgs args; - - mFakePolicy->setDisplayInfo(DISPLAY_ID, - DISPLAY_WIDTH, DISPLAY_HEIGHT, - InputReaderPolicyInterface::ROTATION_270); - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, KEY_UP, AKEYCODE_DPAD_UP, 1, 0); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action); - ASSERT_EQ(KEY_UP, args.scanCode); - ASSERT_EQ(AKEYCODE_DPAD_RIGHT, args.keyCode); - - mFakePolicy->setDisplayInfo(DISPLAY_ID, - DISPLAY_WIDTH, DISPLAY_HEIGHT, - InputReaderPolicyInterface::ROTATION_180); - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, KEY_UP, AKEYCODE_DPAD_UP, 0, 0); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action); - ASSERT_EQ(KEY_UP, args.scanCode); - ASSERT_EQ(AKEYCODE_DPAD_RIGHT, args.keyCode); -} - -TEST_F(KeyboardInputMapperTest, GetKeyCodeState) { - KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, -1, - AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC); - addMapperAndConfigure(mapper); - - mFakeEventHub->setKeyCodeState(DEVICE_ID, AKEYCODE_A, 1); - ASSERT_EQ(1, mapper->getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_A)); - - mFakeEventHub->setKeyCodeState(DEVICE_ID, AKEYCODE_A, 0); - ASSERT_EQ(0, mapper->getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_A)); -} - -TEST_F(KeyboardInputMapperTest, GetScanCodeState) { - KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, -1, - AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC); - addMapperAndConfigure(mapper); - - mFakeEventHub->setScanCodeState(DEVICE_ID, KEY_A, 1); - ASSERT_EQ(1, mapper->getScanCodeState(AINPUT_SOURCE_ANY, KEY_A)); - - mFakeEventHub->setScanCodeState(DEVICE_ID, KEY_A, 0); - ASSERT_EQ(0, mapper->getScanCodeState(AINPUT_SOURCE_ANY, KEY_A)); -} - -TEST_F(KeyboardInputMapperTest, MarkSupportedKeyCodes) { - KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, -1, - AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC); - addMapperAndConfigure(mapper); - - mFakeEventHub->addKey(DEVICE_ID, KEY_A, AKEYCODE_A, 0); - - const int32_t keyCodes[2] = { AKEYCODE_A, AKEYCODE_B }; - uint8_t flags[2] = { 0, 0 }; - ASSERT_TRUE(mapper->markSupportedKeyCodes(AINPUT_SOURCE_ANY, 1, keyCodes, flags)); - ASSERT_TRUE(flags[0]); - ASSERT_FALSE(flags[1]); -} - - -// --- TrackballInputMapperTest --- - -class TrackballInputMapperTest : public InputMapperTest { -protected: - static const int32_t TRACKBALL_MOVEMENT_THRESHOLD; - - void testMotionRotation(TrackballInputMapper* mapper, - int32_t originalX, int32_t originalY, int32_t rotatedX, int32_t rotatedY); -}; - -const int32_t TrackballInputMapperTest::TRACKBALL_MOVEMENT_THRESHOLD = 6; - -void TrackballInputMapperTest::testMotionRotation(TrackballInputMapper* mapper, - int32_t originalX, int32_t originalY, int32_t rotatedX, int32_t rotatedY) { - FakeInputDispatcher::NotifyMotionArgs args; - - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_X, 0, originalX, 0); - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_Y, 0, originalY, 0); - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0, 0, 0); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], - float(rotatedX) / TRACKBALL_MOVEMENT_THRESHOLD, - float(rotatedY) / TRACKBALL_MOVEMENT_THRESHOLD, - 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); -} - -TEST_F(TrackballInputMapperTest, GetSources) { - TrackballInputMapper* mapper = new TrackballInputMapper(mDevice, -1); - addMapperAndConfigure(mapper); - - ASSERT_EQ(AINPUT_SOURCE_TRACKBALL, mapper->getSources()); -} - -TEST_F(TrackballInputMapperTest, PopulateDeviceInfo) { - TrackballInputMapper* mapper = new TrackballInputMapper(mDevice, -1); - addMapperAndConfigure(mapper); - - InputDeviceInfo info; - mapper->populateDeviceInfo(&info); - - ASSERT_NO_FATAL_FAILURE(assertMotionRange(info, AINPUT_MOTION_RANGE_X, - -1.0f, 1.0f, 0.0f, 1.0f / TRACKBALL_MOVEMENT_THRESHOLD)); - ASSERT_NO_FATAL_FAILURE(assertMotionRange(info, AINPUT_MOTION_RANGE_Y, - -1.0f, 1.0f, 0.0f, 1.0f / TRACKBALL_MOVEMENT_THRESHOLD)); -} - -TEST_F(TrackballInputMapperTest, Process_ShouldSetAllFieldsAndIncludeGlobalMetaState) { - TrackballInputMapper* mapper = new TrackballInputMapper(mDevice, -1); - addMapperAndConfigure(mapper); - - mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON); - - FakeInputDispatcher::NotifyMotionArgs args; - - // Button press. - // Mostly testing non x/y behavior here so we don't need to check again elsewhere. - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MOUSE, 0, 1, 0); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(ARBITRARY_TIME, args.eventTime); - ASSERT_EQ(DEVICE_ID, args.deviceId); - ASSERT_EQ(AINPUT_SOURCE_TRACKBALL, args.source); - ASSERT_EQ(uint32_t(0), args.policyFlags); - ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action); - ASSERT_EQ(0, args.flags); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState); - ASSERT_EQ(0, args.edgeFlags); - ASSERT_EQ(uint32_t(1), args.pointerCount); - ASSERT_EQ(0, args.pointerIds[0]); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], - 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); - ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.xPrecision); - ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.yPrecision); - ASSERT_EQ(ARBITRARY_TIME, args.downTime); - - // Button release. Should have same down time. - process(mapper, ARBITRARY_TIME + 1, DEVICE_ID, EV_KEY, BTN_MOUSE, 0, 0, 0); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(ARBITRARY_TIME + 1, args.eventTime); - ASSERT_EQ(DEVICE_ID, args.deviceId); - ASSERT_EQ(AINPUT_SOURCE_TRACKBALL, args.source); - ASSERT_EQ(uint32_t(0), args.policyFlags); - ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action); - ASSERT_EQ(0, args.flags); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState); - ASSERT_EQ(0, args.edgeFlags); - ASSERT_EQ(uint32_t(1), args.pointerCount); - ASSERT_EQ(0, args.pointerIds[0]); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], - 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); - ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.xPrecision); - ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.yPrecision); - ASSERT_EQ(ARBITRARY_TIME, args.downTime); -} - -TEST_F(TrackballInputMapperTest, Process_ShouldHandleIndependentXYUpdates) { - TrackballInputMapper* mapper = new TrackballInputMapper(mDevice, -1); - addMapperAndConfigure(mapper); - - FakeInputDispatcher::NotifyMotionArgs args; - - // Motion in X but not Y. - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_X, 0, 1, 0); - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0, 0, 0); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], - 1.0f / TRACKBALL_MOVEMENT_THRESHOLD, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); - - // Motion in Y but not X. - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_Y, 0, -2, 0); - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0, 0, 0); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action); - ASSERT_NEAR(0.0f, args.pointerCoords[0].x, EPSILON); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], - 0.0f, -2.0f / TRACKBALL_MOVEMENT_THRESHOLD, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); -} - -TEST_F(TrackballInputMapperTest, Process_ShouldHandleIndependentButtonUpdates) { - TrackballInputMapper* mapper = new TrackballInputMapper(mDevice, -1); - addMapperAndConfigure(mapper); - - FakeInputDispatcher::NotifyMotionArgs args; - - // Button press without following sync. - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MOUSE, 0, 1, 0); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], - 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); - - // Button release without following sync. - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MOUSE, 0, 0, 0); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], - 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); -} - -TEST_F(TrackballInputMapperTest, Process_ShouldHandleCombinedXYAndButtonUpdates) { - TrackballInputMapper* mapper = new TrackballInputMapper(mDevice, -1); - addMapperAndConfigure(mapper); - - FakeInputDispatcher::NotifyMotionArgs args; - - // Combined X, Y and Button. - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_X, 0, 1, 0); - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_Y, 0, -2, 0); - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MOUSE, 0, 1, 0); - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0, 0, 0); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], - 1.0f / TRACKBALL_MOVEMENT_THRESHOLD, -2.0f / TRACKBALL_MOVEMENT_THRESHOLD, - 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); - - // Move X, Y a bit while pressed. - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_X, 0, 2, 0); - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_Y, 0, 1, 0); - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0, 0, 0); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], - 2.0f / TRACKBALL_MOVEMENT_THRESHOLD, 1.0f / TRACKBALL_MOVEMENT_THRESHOLD, - 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); - - // Release Button. - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MOUSE, 0, 0, 0); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], - 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); -} - -TEST_F(TrackballInputMapperTest, Reset_WhenButtonIsNotDown_ShouldNotSynthesizeButtonUp) { - TrackballInputMapper* mapper = new TrackballInputMapper(mDevice, -1); - addMapperAndConfigure(mapper); - - FakeInputDispatcher::NotifyMotionArgs args; - - // Button press. - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MOUSE, 0, 1, 0); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); - - // Button release. - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MOUSE, 0, 0, 0); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); - - // Reset. Should not synthesize button up since button is not pressed. - mapper->reset(); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasNotCalled()); -} - -TEST_F(TrackballInputMapperTest, Reset_WhenButtonIsDown_ShouldSynthesizeButtonUp) { - TrackballInputMapper* mapper = new TrackballInputMapper(mDevice, -1); - addMapperAndConfigure(mapper); - - FakeInputDispatcher::NotifyMotionArgs args; - - // Button press. - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MOUSE, 0, 1, 0); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); - - // Reset. Should synthesize button up. - mapper->reset(); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], - 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); -} - -TEST_F(TrackballInputMapperTest, Process_WhenNotAttachedToDisplay_ShouldNotRotateMotions) { - TrackballInputMapper* mapper = new TrackballInputMapper(mDevice, -1); - addMapperAndConfigure(mapper); - - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, 0, 1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, 1, 1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, 1, 0)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, -1, 1, -1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, -1, 0, -1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, -1, -1, -1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 0, -1, 0)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 1, -1, 1)); -} - -TEST_F(TrackballInputMapperTest, Process_WhenAttachedToDisplay_ShouldRotateMotions) { - TrackballInputMapper* mapper = new TrackballInputMapper(mDevice, DISPLAY_ID); - addMapperAndConfigure(mapper); - - mFakePolicy->setDisplayInfo(DISPLAY_ID, - DISPLAY_WIDTH, DISPLAY_HEIGHT, - InputReaderPolicyInterface::ROTATION_0); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, 0, 1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, 1, 1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, 1, 0)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, -1, 1, -1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, -1, 0, -1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, -1, -1, -1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 0, -1, 0)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 1, -1, 1)); - - mFakePolicy->setDisplayInfo(DISPLAY_ID, - DISPLAY_WIDTH, DISPLAY_HEIGHT, - InputReaderPolicyInterface::ROTATION_90); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, 1, 0)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, 1, -1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, 0, -1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, -1, -1, -1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, -1, -1, 0)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, -1, -1, 1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 0, 0, 1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 1, 1, 1)); - - mFakePolicy->setDisplayInfo(DISPLAY_ID, - DISPLAY_WIDTH, DISPLAY_HEIGHT, - InputReaderPolicyInterface::ROTATION_180); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, 0, -1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, -1, -1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, -1, 0)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, -1, -1, 1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, -1, 0, 1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, -1, 1, 1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 0, 1, 0)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 1, 1, -1)); - - mFakePolicy->setDisplayInfo(DISPLAY_ID, - DISPLAY_WIDTH, DISPLAY_HEIGHT, - InputReaderPolicyInterface::ROTATION_270); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, -1, 0)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, -1, 1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, 0, 1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, -1, 1, 1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, -1, 1, 0)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, -1, 1, -1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 0, 0, -1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 1, -1, -1)); -} - - -// --- TouchInputMapperTest --- - -class TouchInputMapperTest : public InputMapperTest { -protected: - static const int32_t RAW_X_MIN; - static const int32_t RAW_X_MAX; - static const int32_t RAW_Y_MIN; - static const int32_t RAW_Y_MAX; - static const int32_t RAW_TOUCH_MIN; - static const int32_t RAW_TOUCH_MAX; - static const int32_t RAW_TOOL_MIN; - static const int32_t RAW_TOOL_MAX; - static const int32_t RAW_PRESSURE_MIN; - static const int32_t RAW_PRESSURE_MAX; - static const int32_t RAW_ORIENTATION_MIN; - static const int32_t RAW_ORIENTATION_MAX; - static const int32_t RAW_ID_MIN; - static const int32_t RAW_ID_MAX; - static const float X_PRECISION; - static const float Y_PRECISION; - - static const VirtualKeyDefinition VIRTUAL_KEYS[2]; - - enum Axes { - POSITION = 1 << 0, - TOUCH = 1 << 1, - TOOL = 1 << 2, - PRESSURE = 1 << 3, - ORIENTATION = 1 << 4, - MINOR = 1 << 5, - ID = 1 << 6, - }; - - void prepareDisplay(int32_t orientation); - void prepareVirtualKeys(); - int32_t toRawX(float displayX); - int32_t toRawY(float displayY); - float toDisplayX(int32_t rawX); - float toDisplayY(int32_t rawY); -}; - -const int32_t TouchInputMapperTest::RAW_X_MIN = 25; -const int32_t TouchInputMapperTest::RAW_X_MAX = 1020; -const int32_t TouchInputMapperTest::RAW_Y_MIN = 30; -const int32_t TouchInputMapperTest::RAW_Y_MAX = 1010; -const int32_t TouchInputMapperTest::RAW_TOUCH_MIN = 0; -const int32_t TouchInputMapperTest::RAW_TOUCH_MAX = 31; -const int32_t TouchInputMapperTest::RAW_TOOL_MIN = 0; -const int32_t TouchInputMapperTest::RAW_TOOL_MAX = 15; -const int32_t TouchInputMapperTest::RAW_PRESSURE_MIN = RAW_TOUCH_MIN; -const int32_t TouchInputMapperTest::RAW_PRESSURE_MAX = RAW_TOUCH_MAX; -const int32_t TouchInputMapperTest::RAW_ORIENTATION_MIN = -7; -const int32_t TouchInputMapperTest::RAW_ORIENTATION_MAX = 7; -const int32_t TouchInputMapperTest::RAW_ID_MIN = 0; -const int32_t TouchInputMapperTest::RAW_ID_MAX = 9; -const float TouchInputMapperTest::X_PRECISION = float(RAW_X_MAX - RAW_X_MIN) / DISPLAY_WIDTH; -const float TouchInputMapperTest::Y_PRECISION = float(RAW_Y_MAX - RAW_Y_MIN) / DISPLAY_HEIGHT; - -const VirtualKeyDefinition TouchInputMapperTest::VIRTUAL_KEYS[2] = { - { KEY_HOME, 60, DISPLAY_HEIGHT + 15, 20, 20 }, - { KEY_MENU, DISPLAY_HEIGHT - 60, DISPLAY_WIDTH + 15, 20, 20 }, -}; - -void TouchInputMapperTest::prepareDisplay(int32_t orientation) { - mFakePolicy->setDisplayInfo(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, orientation); -} - -void TouchInputMapperTest::prepareVirtualKeys() { - mFakePolicy->addVirtualKeyDefinition(String8(DEVICE_NAME), VIRTUAL_KEYS[0]); - mFakePolicy->addVirtualKeyDefinition(String8(DEVICE_NAME), VIRTUAL_KEYS[1]); - mFakeEventHub->addKey(DEVICE_ID, KEY_HOME, AKEYCODE_HOME, POLICY_FLAG_WAKE); - mFakeEventHub->addKey(DEVICE_ID, KEY_MENU, AKEYCODE_MENU, POLICY_FLAG_WAKE); -} - -int32_t TouchInputMapperTest::toRawX(float displayX) { - return int32_t(displayX * (RAW_X_MAX - RAW_X_MIN) / DISPLAY_WIDTH + RAW_X_MIN); -} - -int32_t TouchInputMapperTest::toRawY(float displayY) { - return int32_t(displayY * (RAW_Y_MAX - RAW_Y_MIN) / DISPLAY_HEIGHT + RAW_Y_MIN); -} - -float TouchInputMapperTest::toDisplayX(int32_t rawX) { - return float(rawX - RAW_X_MIN) * DISPLAY_WIDTH / (RAW_X_MAX - RAW_X_MIN); -} - -float TouchInputMapperTest::toDisplayY(int32_t rawY) { - return float(rawY - RAW_Y_MIN) * DISPLAY_HEIGHT / (RAW_Y_MAX - RAW_Y_MIN); -} - - -// --- SingleTouchInputMapperTest --- - -class SingleTouchInputMapperTest : public TouchInputMapperTest { -protected: - void prepareAxes(int axes); - - void processDown(SingleTouchInputMapper* mapper, int32_t x, int32_t y); - void processMove(SingleTouchInputMapper* mapper, int32_t x, int32_t y); - void processUp(SingleTouchInputMapper* mappery); - void processPressure(SingleTouchInputMapper* mapper, int32_t pressure); - void processToolMajor(SingleTouchInputMapper* mapper, int32_t toolMajor); - void processSync(SingleTouchInputMapper* mapper); -}; - -void SingleTouchInputMapperTest::prepareAxes(int axes) { - if (axes & POSITION) { - mFakeEventHub->addAxis(DEVICE_ID, ABS_X, RAW_X_MIN, RAW_X_MAX, 0, 0); - mFakeEventHub->addAxis(DEVICE_ID, ABS_Y, RAW_Y_MIN, RAW_Y_MAX, 0, 0); - } - if (axes & PRESSURE) { - mFakeEventHub->addAxis(DEVICE_ID, ABS_PRESSURE, RAW_PRESSURE_MIN, RAW_PRESSURE_MAX, 0, 0); - } - if (axes & TOOL) { - mFakeEventHub->addAxis(DEVICE_ID, ABS_TOOL_WIDTH, RAW_TOOL_MIN, RAW_TOOL_MAX, 0, 0); - } -} - -void SingleTouchInputMapperTest::processDown(SingleTouchInputMapper* mapper, int32_t x, int32_t y) { - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_TOUCH, 0, 1, 0); - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_X, 0, x, 0); - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_Y, 0, y, 0); -} - -void SingleTouchInputMapperTest::processMove(SingleTouchInputMapper* mapper, int32_t x, int32_t y) { - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_X, 0, x, 0); - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_Y, 0, y, 0); -} - -void SingleTouchInputMapperTest::processUp(SingleTouchInputMapper* mapper) { - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_TOUCH, 0, 0, 0); -} - -void SingleTouchInputMapperTest::processPressure( - SingleTouchInputMapper* mapper, int32_t pressure) { - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_PRESSURE, 0, pressure, 0); -} - -void SingleTouchInputMapperTest::processToolMajor( - SingleTouchInputMapper* mapper, int32_t toolMajor) { - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_TOOL_WIDTH, 0, toolMajor, 0); -} - -void SingleTouchInputMapperTest::processSync(SingleTouchInputMapper* mapper) { - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0, 0, 0); -} - - -TEST_F(SingleTouchInputMapperTest, GetSources_WhenNotAttachedToADisplay_ReturnsTouchPad) { - SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, -1); - prepareAxes(POSITION); - addMapperAndConfigure(mapper); - - ASSERT_EQ(AINPUT_SOURCE_TOUCHPAD, mapper->getSources()); -} - -TEST_F(SingleTouchInputMapperTest, GetSources_WhenAttachedToADisplay_ReturnsTouchScreen) { - SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, DISPLAY_ID); - prepareAxes(POSITION); - addMapperAndConfigure(mapper); - - ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, mapper->getSources()); -} - -TEST_F(SingleTouchInputMapperTest, GetKeyCodeState) { - SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, DISPLAY_ID); - prepareDisplay(InputReaderPolicyInterface::ROTATION_0); - prepareAxes(POSITION); - prepareVirtualKeys(); - addMapperAndConfigure(mapper); - - // Unknown key. - ASSERT_EQ(AKEY_STATE_UNKNOWN, mapper->getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_A)); - - // Virtual key is down. - int32_t x = toRawX(VIRTUAL_KEYS[0].centerX); - int32_t y = toRawY(VIRTUAL_KEYS[0].centerY); - processDown(mapper, x, y); - processSync(mapper); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled()); - - ASSERT_EQ(AKEY_STATE_VIRTUAL, mapper->getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_HOME)); - - // Virtual key is up. - processUp(mapper); - processSync(mapper); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled()); - - ASSERT_EQ(AKEY_STATE_UP, mapper->getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_HOME)); -} - -TEST_F(SingleTouchInputMapperTest, GetScanCodeState) { - SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, DISPLAY_ID); - prepareDisplay(InputReaderPolicyInterface::ROTATION_0); - prepareAxes(POSITION); - prepareVirtualKeys(); - addMapperAndConfigure(mapper); - - // Unknown key. - ASSERT_EQ(AKEY_STATE_UNKNOWN, mapper->getScanCodeState(AINPUT_SOURCE_ANY, KEY_A)); - - // Virtual key is down. - int32_t x = toRawX(VIRTUAL_KEYS[0].centerX); - int32_t y = toRawY(VIRTUAL_KEYS[0].centerY); - processDown(mapper, x, y); - processSync(mapper); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled()); - - ASSERT_EQ(AKEY_STATE_VIRTUAL, mapper->getScanCodeState(AINPUT_SOURCE_ANY, KEY_HOME)); - - // Virtual key is up. - processUp(mapper); - processSync(mapper); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled()); - - ASSERT_EQ(AKEY_STATE_UP, mapper->getScanCodeState(AINPUT_SOURCE_ANY, KEY_HOME)); -} - -TEST_F(SingleTouchInputMapperTest, MarkSupportedKeyCodes) { - SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, DISPLAY_ID); - prepareDisplay(InputReaderPolicyInterface::ROTATION_0); - prepareAxes(POSITION); - prepareVirtualKeys(); - addMapperAndConfigure(mapper); - - const int32_t keys[2] = { AKEYCODE_HOME, AKEYCODE_A }; - uint8_t flags[2] = { 0, 0 }; - ASSERT_TRUE(mapper->markSupportedKeyCodes(AINPUT_SOURCE_ANY, 2, keys, flags)); - ASSERT_TRUE(flags[0]); - ASSERT_FALSE(flags[1]); -} - -TEST_F(SingleTouchInputMapperTest, Reset_WhenVirtualKeysAreDown_SendsUp) { - // Note: Ideally we should send cancels but the implementation is more straightforward - // with up and this will only happen if a device is forcibly removed. - SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, DISPLAY_ID); - prepareDisplay(InputReaderPolicyInterface::ROTATION_0); - prepareAxes(POSITION); - prepareVirtualKeys(); - addMapperAndConfigure(mapper); - - mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON); - - // Press virtual key. - int32_t x = toRawX(VIRTUAL_KEYS[0].centerX); - int32_t y = toRawY(VIRTUAL_KEYS[0].centerY); - processDown(mapper, x, y); - processSync(mapper); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled()); - - // Reset. Since key is down, synthesize key up. - mapper->reset(); - - FakeInputDispatcher::NotifyKeyArgs args; - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled(&args)); - //ASSERT_EQ(ARBITRARY_TIME, args.eventTime); - ASSERT_EQ(DEVICE_ID, args.deviceId); - ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source); - ASSERT_EQ(POLICY_FLAG_VIRTUAL, args.policyFlags); - ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action); - ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY, args.flags); - ASSERT_EQ(AKEYCODE_HOME, args.keyCode); - ASSERT_EQ(KEY_HOME, args.scanCode); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState); - ASSERT_EQ(ARBITRARY_TIME, args.downTime); -} - -TEST_F(SingleTouchInputMapperTest, Reset_WhenNothingIsPressed_NothingMuchHappens) { - SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, DISPLAY_ID); - prepareDisplay(InputReaderPolicyInterface::ROTATION_0); - prepareAxes(POSITION); - prepareVirtualKeys(); - addMapperAndConfigure(mapper); - - // Press virtual key. - int32_t x = toRawX(VIRTUAL_KEYS[0].centerX); - int32_t y = toRawY(VIRTUAL_KEYS[0].centerY); - processDown(mapper, x, y); - processSync(mapper); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled()); - - // Release virtual key. - processUp(mapper); - processSync(mapper); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled()); - - // Reset. Since no key is down, nothing happens. - mapper->reset(); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasNotCalled()); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasNotCalled()); -} - -TEST_F(SingleTouchInputMapperTest, Process_WhenVirtualKeyIsPressedAndReleasedNormally_SendsKeyDownAndKeyUp) { - SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, DISPLAY_ID); - prepareDisplay(InputReaderPolicyInterface::ROTATION_0); - prepareAxes(POSITION); - prepareVirtualKeys(); - addMapperAndConfigure(mapper); - - mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON); - - FakeInputDispatcher::NotifyKeyArgs args; - - // Press virtual key. - int32_t x = toRawX(VIRTUAL_KEYS[0].centerX); - int32_t y = toRawY(VIRTUAL_KEYS[0].centerY); - processDown(mapper, x, y); - processSync(mapper); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(ARBITRARY_TIME, args.eventTime); - ASSERT_EQ(DEVICE_ID, args.deviceId); - ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source); - ASSERT_EQ(POLICY_FLAG_VIRTUAL, args.policyFlags); - ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action); - ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY, args.flags); - ASSERT_EQ(AKEYCODE_HOME, args.keyCode); - ASSERT_EQ(KEY_HOME, args.scanCode); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState); - ASSERT_EQ(ARBITRARY_TIME, args.downTime); - - // Release virtual key. - processUp(mapper); - processSync(mapper); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(ARBITRARY_TIME, args.eventTime); - ASSERT_EQ(DEVICE_ID, args.deviceId); - ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source); - ASSERT_EQ(POLICY_FLAG_VIRTUAL, args.policyFlags); - ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action); - ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY, args.flags); - ASSERT_EQ(AKEYCODE_HOME, args.keyCode); - ASSERT_EQ(KEY_HOME, args.scanCode); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState); - ASSERT_EQ(ARBITRARY_TIME, args.downTime); - - // Should not have sent any motions. - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasNotCalled()); -} - -TEST_F(SingleTouchInputMapperTest, Process_WhenVirtualKeyIsPressedAndMovedOutOfBounds_SendsKeyDownAndKeyCancel) { - SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, DISPLAY_ID); - prepareDisplay(InputReaderPolicyInterface::ROTATION_0); - prepareAxes(POSITION); - prepareVirtualKeys(); - addMapperAndConfigure(mapper); - - mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON); - - FakeInputDispatcher::NotifyKeyArgs keyArgs; - - // Press virtual key. - int32_t x = toRawX(VIRTUAL_KEYS[0].centerX); - int32_t y = toRawY(VIRTUAL_KEYS[0].centerY); - processDown(mapper, x, y); - processSync(mapper); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled(&keyArgs)); - ASSERT_EQ(ARBITRARY_TIME, keyArgs.eventTime); - ASSERT_EQ(DEVICE_ID, keyArgs.deviceId); - ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, keyArgs.source); - ASSERT_EQ(POLICY_FLAG_VIRTUAL, keyArgs.policyFlags); - ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action); - ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY, keyArgs.flags); - ASSERT_EQ(AKEYCODE_HOME, keyArgs.keyCode); - ASSERT_EQ(KEY_HOME, keyArgs.scanCode); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, keyArgs.metaState); - ASSERT_EQ(ARBITRARY_TIME, keyArgs.downTime); - - // Move out of bounds. This should generate a cancel and a pointer down since we moved - // into the display area. - y -= 100; - processMove(mapper, x, y); - processSync(mapper); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled(&keyArgs)); - ASSERT_EQ(ARBITRARY_TIME, keyArgs.eventTime); - ASSERT_EQ(DEVICE_ID, keyArgs.deviceId); - ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, keyArgs.source); - ASSERT_EQ(POLICY_FLAG_VIRTUAL, keyArgs.policyFlags); - ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action); - ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY - | AKEY_EVENT_FLAG_CANCELED, keyArgs.flags); - ASSERT_EQ(AKEYCODE_HOME, keyArgs.keyCode); - ASSERT_EQ(KEY_HOME, keyArgs.scanCode); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, keyArgs.metaState); - ASSERT_EQ(ARBITRARY_TIME, keyArgs.downTime); - - FakeInputDispatcher::NotifyMotionArgs motionArgs; - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime); - ASSERT_EQ(DEVICE_ID, motionArgs.deviceId); - ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source); - ASSERT_EQ(uint32_t(0), motionArgs.policyFlags); - ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action); - ASSERT_EQ(0, motionArgs.flags); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState); - ASSERT_EQ(0, motionArgs.edgeFlags); - ASSERT_EQ(size_t(1), motionArgs.pointerCount); - ASSERT_EQ(0, motionArgs.pointerIds[0]); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - toDisplayX(x), toDisplayY(y), 1, 0, 0, 0, 0, 0, 0)); - ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON); - ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON); - ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime); - - // Keep moving out of bounds. Should generate a pointer move. - y -= 50; - processMove(mapper, x, y); - processSync(mapper); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime); - ASSERT_EQ(DEVICE_ID, motionArgs.deviceId); - ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source); - ASSERT_EQ(uint32_t(0), motionArgs.policyFlags); - ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action); - ASSERT_EQ(0, motionArgs.flags); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState); - ASSERT_EQ(0, motionArgs.edgeFlags); - ASSERT_EQ(size_t(1), motionArgs.pointerCount); - ASSERT_EQ(0, motionArgs.pointerIds[0]); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - toDisplayX(x), toDisplayY(y), 1, 0, 0, 0, 0, 0, 0)); - ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON); - ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON); - ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime); - - // Release out of bounds. Should generate a pointer up. - processUp(mapper); - processSync(mapper); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime); - ASSERT_EQ(DEVICE_ID, motionArgs.deviceId); - ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source); - ASSERT_EQ(uint32_t(0), motionArgs.policyFlags); - ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action); - ASSERT_EQ(0, motionArgs.flags); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState); - ASSERT_EQ(0, motionArgs.edgeFlags); - ASSERT_EQ(size_t(1), motionArgs.pointerCount); - ASSERT_EQ(0, motionArgs.pointerIds[0]); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - toDisplayX(x), toDisplayY(y), 1, 0, 0, 0, 0, 0, 0)); - ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON); - ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON); - ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime); - - // Should not have sent any more keys or motions. - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasNotCalled()); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasNotCalled()); -} - -TEST_F(SingleTouchInputMapperTest, Process_WhenTouchStartsOutsideDisplayAndMovesIn_SendsDownAsTouchEntersDisplay) { - SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, DISPLAY_ID); - prepareDisplay(InputReaderPolicyInterface::ROTATION_0); - prepareAxes(POSITION); - prepareVirtualKeys(); - addMapperAndConfigure(mapper); - - mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON); - - FakeInputDispatcher::NotifyMotionArgs motionArgs; - - // Initially go down out of bounds. - int32_t x = -10; - int32_t y = -10; - processDown(mapper, x, y); - processSync(mapper); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasNotCalled()); - - // Move into the display area. Should generate a pointer down. - x = 50; - y = 75; - processMove(mapper, x, y); - processSync(mapper); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime); - ASSERT_EQ(DEVICE_ID, motionArgs.deviceId); - ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source); - ASSERT_EQ(uint32_t(0), motionArgs.policyFlags); - ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action); - ASSERT_EQ(0, motionArgs.flags); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState); - ASSERT_EQ(0, motionArgs.edgeFlags); - ASSERT_EQ(size_t(1), motionArgs.pointerCount); - ASSERT_EQ(0, motionArgs.pointerIds[0]); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - toDisplayX(x), toDisplayY(y), 1, 0, 0, 0, 0, 0, 0)); - ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON); - ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON); - ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime); - - // Release. Should generate a pointer up. - processUp(mapper); - processSync(mapper); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime); - ASSERT_EQ(DEVICE_ID, motionArgs.deviceId); - ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source); - ASSERT_EQ(uint32_t(0), motionArgs.policyFlags); - ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action); - ASSERT_EQ(0, motionArgs.flags); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState); - ASSERT_EQ(0, motionArgs.edgeFlags); - ASSERT_EQ(size_t(1), motionArgs.pointerCount); - ASSERT_EQ(0, motionArgs.pointerIds[0]); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - toDisplayX(x), toDisplayY(y), 1, 0, 0, 0, 0, 0, 0)); - ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON); - ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON); - ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime); - - // Should not have sent any more keys or motions. - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasNotCalled()); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasNotCalled()); -} - -TEST_F(SingleTouchInputMapperTest, Process_NormalSingleTouchGesture) { - SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, DISPLAY_ID); - prepareDisplay(InputReaderPolicyInterface::ROTATION_0); - prepareAxes(POSITION); - prepareVirtualKeys(); - addMapperAndConfigure(mapper); - - mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON); - - FakeInputDispatcher::NotifyMotionArgs motionArgs; - - // Down. - int32_t x = 100; - int32_t y = 125; - processDown(mapper, x, y); - processSync(mapper); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime); - ASSERT_EQ(DEVICE_ID, motionArgs.deviceId); - ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source); - ASSERT_EQ(uint32_t(0), motionArgs.policyFlags); - ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action); - ASSERT_EQ(0, motionArgs.flags); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState); - ASSERT_EQ(0, motionArgs.edgeFlags); - ASSERT_EQ(size_t(1), motionArgs.pointerCount); - ASSERT_EQ(0, motionArgs.pointerIds[0]); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - toDisplayX(x), toDisplayY(y), 1, 0, 0, 0, 0, 0, 0)); - ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON); - ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON); - ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime); - - // Move. - x += 50; - y += 75; - processMove(mapper, x, y); - processSync(mapper); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime); - ASSERT_EQ(DEVICE_ID, motionArgs.deviceId); - ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source); - ASSERT_EQ(uint32_t(0), motionArgs.policyFlags); - ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action); - ASSERT_EQ(0, motionArgs.flags); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState); - ASSERT_EQ(0, motionArgs.edgeFlags); - ASSERT_EQ(size_t(1), motionArgs.pointerCount); - ASSERT_EQ(0, motionArgs.pointerIds[0]); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - toDisplayX(x), toDisplayY(y), 1, 0, 0, 0, 0, 0, 0)); - ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON); - ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON); - ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime); - - // Up. - processUp(mapper); - processSync(mapper); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime); - ASSERT_EQ(DEVICE_ID, motionArgs.deviceId); - ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source); - ASSERT_EQ(uint32_t(0), motionArgs.policyFlags); - ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action); - ASSERT_EQ(0, motionArgs.flags); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState); - ASSERT_EQ(0, motionArgs.edgeFlags); - ASSERT_EQ(size_t(1), motionArgs.pointerCount); - ASSERT_EQ(0, motionArgs.pointerIds[0]); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - toDisplayX(x), toDisplayY(y), 1, 0, 0, 0, 0, 0, 0)); - ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON); - ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON); - ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime); - - // Should not have sent any more keys or motions. - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasNotCalled()); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasNotCalled()); -} - -TEST_F(SingleTouchInputMapperTest, Process_Rotation) { - SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, DISPLAY_ID); - prepareAxes(POSITION); - addMapperAndConfigure(mapper); - - FakeInputDispatcher::NotifyMotionArgs args; - - // Rotation 0. - prepareDisplay(InputReaderPolicyInterface::ROTATION_0); - processDown(mapper, toRawX(50), toRawY(75)); - processSync(mapper); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); - ASSERT_NEAR(50, args.pointerCoords[0].x, 1); - ASSERT_NEAR(75, args.pointerCoords[0].y, 1); - - processUp(mapper); - processSync(mapper); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled()); - - // Rotation 90. - prepareDisplay(InputReaderPolicyInterface::ROTATION_90); - processDown(mapper, toRawX(50), toRawY(75)); - processSync(mapper); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); - ASSERT_NEAR(75, args.pointerCoords[0].x, 1); - ASSERT_NEAR(DISPLAY_WIDTH - 50, args.pointerCoords[0].y, 1); - - processUp(mapper); - processSync(mapper); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled()); - - // Rotation 180. - prepareDisplay(InputReaderPolicyInterface::ROTATION_180); - processDown(mapper, toRawX(50), toRawY(75)); - processSync(mapper); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); - ASSERT_NEAR(DISPLAY_WIDTH - 50, args.pointerCoords[0].x, 1); - ASSERT_NEAR(DISPLAY_HEIGHT - 75, args.pointerCoords[0].y, 1); - - processUp(mapper); - processSync(mapper); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled()); - - // Rotation 270. - prepareDisplay(InputReaderPolicyInterface::ROTATION_270); - processDown(mapper, toRawX(50), toRawY(75)); - processSync(mapper); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); - ASSERT_NEAR(DISPLAY_HEIGHT - 75, args.pointerCoords[0].x, 1); - ASSERT_NEAR(50, args.pointerCoords[0].y, 1); - - processUp(mapper); - processSync(mapper); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled()); -} - -TEST_F(SingleTouchInputMapperTest, Process_AllAxes_DefaultCalibration) { - SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, DISPLAY_ID); - prepareDisplay(InputReaderPolicyInterface::ROTATION_0); - prepareAxes(POSITION | PRESSURE | TOOL); - addMapperAndConfigure(mapper); - - // These calculations are based on the input device calibration documentation. - int32_t rawX = 100; - int32_t rawY = 200; - int32_t rawPressure = 10; - int32_t rawToolMajor = 12; - - float x = toDisplayX(rawX); - float y = toDisplayY(rawY); - float pressure = float(rawPressure) / RAW_PRESSURE_MAX; - float size = float(rawToolMajor) / RAW_TOOL_MAX; - float tool = min(DISPLAY_WIDTH, DISPLAY_HEIGHT) * size; - float touch = min(tool * pressure, tool); - - processDown(mapper, rawX, rawY); - processPressure(mapper, rawPressure); - processToolMajor(mapper, rawToolMajor); - processSync(mapper); - - FakeInputDispatcher::NotifyMotionArgs args; - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], - x, y, pressure, size, touch, touch, tool, tool, 0)); -} - - -// --- MultiTouchInputMapperTest --- - -class MultiTouchInputMapperTest : public TouchInputMapperTest { -protected: - void prepareAxes(int axes); - - void processPosition(MultiTouchInputMapper* mapper, int32_t x, int32_t y); - void processTouchMajor(MultiTouchInputMapper* mapper, int32_t touchMajor); - void processTouchMinor(MultiTouchInputMapper* mapper, int32_t touchMinor); - void processToolMajor(MultiTouchInputMapper* mapper, int32_t toolMajor); - void processToolMinor(MultiTouchInputMapper* mapper, int32_t toolMinor); - void processOrientation(MultiTouchInputMapper* mapper, int32_t orientation); - void processPressure(MultiTouchInputMapper* mapper, int32_t pressure); - void processId(MultiTouchInputMapper* mapper, int32_t id); - void processMTSync(MultiTouchInputMapper* mapper); - void processSync(MultiTouchInputMapper* mapper); -}; - -void MultiTouchInputMapperTest::prepareAxes(int axes) { - if (axes & POSITION) { - mFakeEventHub->addAxis(DEVICE_ID, ABS_MT_POSITION_X, RAW_X_MIN, RAW_X_MAX, 0, 0); - mFakeEventHub->addAxis(DEVICE_ID, ABS_MT_POSITION_Y, RAW_Y_MIN, RAW_Y_MAX, 0, 0); - } - if (axes & TOUCH) { - mFakeEventHub->addAxis(DEVICE_ID, ABS_MT_TOUCH_MAJOR, RAW_TOUCH_MIN, RAW_TOUCH_MAX, 0, 0); - if (axes & MINOR) { - mFakeEventHub->addAxis(DEVICE_ID, ABS_MT_TOUCH_MINOR, - RAW_TOUCH_MIN, RAW_TOUCH_MAX, 0, 0); - } - } - if (axes & TOOL) { - mFakeEventHub->addAxis(DEVICE_ID, ABS_MT_WIDTH_MAJOR, RAW_TOOL_MIN, RAW_TOOL_MAX, 0, 0); - if (axes & MINOR) { - mFakeEventHub->addAxis(DEVICE_ID, ABS_MT_WIDTH_MINOR, - RAW_TOOL_MAX, RAW_TOOL_MAX, 0, 0); - } - } - if (axes & ORIENTATION) { - mFakeEventHub->addAxis(DEVICE_ID, ABS_MT_ORIENTATION, - RAW_ORIENTATION_MIN, RAW_ORIENTATION_MAX, 0, 0); - } - if (axes & PRESSURE) { - mFakeEventHub->addAxis(DEVICE_ID, ABS_MT_PRESSURE, - RAW_PRESSURE_MIN, RAW_PRESSURE_MAX, 0, 0); - } - if (axes & ID) { - mFakeEventHub->addAxis(DEVICE_ID, ABS_MT_TRACKING_ID, - RAW_ID_MIN, RAW_ID_MAX, 0, 0); - } -} - -void MultiTouchInputMapperTest::processPosition( - MultiTouchInputMapper* mapper, int32_t x, int32_t y) { - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_MT_POSITION_X, 0, x, 0); - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_MT_POSITION_Y, 0, y, 0); -} - -void MultiTouchInputMapperTest::processTouchMajor( - MultiTouchInputMapper* mapper, int32_t touchMajor) { - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_MT_TOUCH_MAJOR, 0, touchMajor, 0); -} - -void MultiTouchInputMapperTest::processTouchMinor( - MultiTouchInputMapper* mapper, int32_t touchMinor) { - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_MT_TOUCH_MINOR, 0, touchMinor, 0); -} - -void MultiTouchInputMapperTest::processToolMajor( - MultiTouchInputMapper* mapper, int32_t toolMajor) { - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_MT_WIDTH_MAJOR, 0, toolMajor, 0); -} - -void MultiTouchInputMapperTest::processToolMinor( - MultiTouchInputMapper* mapper, int32_t toolMinor) { - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_MT_WIDTH_MINOR, 0, toolMinor, 0); -} - -void MultiTouchInputMapperTest::processOrientation( - MultiTouchInputMapper* mapper, int32_t orientation) { - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_MT_ORIENTATION, 0, orientation, 0); -} - -void MultiTouchInputMapperTest::processPressure( - MultiTouchInputMapper* mapper, int32_t pressure) { - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_MT_PRESSURE, 0, pressure, 0); -} - -void MultiTouchInputMapperTest::processId( - MultiTouchInputMapper* mapper, int32_t id) { - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_MT_TRACKING_ID, 0, id, 0); -} - -void MultiTouchInputMapperTest::processMTSync(MultiTouchInputMapper* mapper) { - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_MT_REPORT, 0, 0, 0); -} - -void MultiTouchInputMapperTest::processSync(MultiTouchInputMapper* mapper) { - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0, 0, 0); -} - - -TEST_F(MultiTouchInputMapperTest, Process_NormalMultiTouchGesture_WithoutTrackingIds) { - MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice, DISPLAY_ID); - prepareDisplay(InputReaderPolicyInterface::ROTATION_0); - prepareAxes(POSITION); - prepareVirtualKeys(); - addMapperAndConfigure(mapper); - - mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON); - - FakeInputDispatcher::NotifyMotionArgs motionArgs; - - // Two fingers down at once. - int32_t x1 = 100, y1 = 125, x2 = 300, y2 = 500; - processPosition(mapper, x1, y1); - processMTSync(mapper); - processPosition(mapper, x2, y2); - processMTSync(mapper); - processSync(mapper); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime); - ASSERT_EQ(DEVICE_ID, motionArgs.deviceId); - ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source); - ASSERT_EQ(uint32_t(0), motionArgs.policyFlags); - ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action); - ASSERT_EQ(0, motionArgs.flags); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState); - ASSERT_EQ(0, motionArgs.edgeFlags); - ASSERT_EQ(size_t(1), motionArgs.pointerCount); - ASSERT_EQ(0, motionArgs.pointerIds[0]); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - toDisplayX(x1), toDisplayY(y1), 1, 0, 0, 0, 0, 0, 0)); - ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON); - ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON); - ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime); - ASSERT_EQ(DEVICE_ID, motionArgs.deviceId); - ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source); - ASSERT_EQ(uint32_t(0), motionArgs.policyFlags); - ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - motionArgs.action); - ASSERT_EQ(0, motionArgs.flags); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState); - ASSERT_EQ(0, motionArgs.edgeFlags); - ASSERT_EQ(size_t(2), motionArgs.pointerCount); - ASSERT_EQ(0, motionArgs.pointerIds[0]); - ASSERT_EQ(1, motionArgs.pointerIds[1]); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - toDisplayX(x1), toDisplayY(y1), 1, 0, 0, 0, 0, 0, 0)); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[1], - toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0)); - ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON); - ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON); - ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime); - - // Move. - x1 += 10; y1 += 15; x2 += 5; y2 -= 10; - processPosition(mapper, x1, y1); - processMTSync(mapper); - processPosition(mapper, x2, y2); - processMTSync(mapper); - processSync(mapper); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime); - ASSERT_EQ(DEVICE_ID, motionArgs.deviceId); - ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source); - ASSERT_EQ(uint32_t(0), motionArgs.policyFlags); - ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action); - ASSERT_EQ(0, motionArgs.flags); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState); - ASSERT_EQ(0, motionArgs.edgeFlags); - ASSERT_EQ(size_t(2), motionArgs.pointerCount); - ASSERT_EQ(0, motionArgs.pointerIds[0]); - ASSERT_EQ(1, motionArgs.pointerIds[1]); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - toDisplayX(x1), toDisplayY(y1), 1, 0, 0, 0, 0, 0, 0)); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[1], - toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0)); - ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON); - ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON); - ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime); - - // First finger up. - x2 += 15; y2 -= 20; - processPosition(mapper, x2, y2); - processMTSync(mapper); - processSync(mapper); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime); - ASSERT_EQ(DEVICE_ID, motionArgs.deviceId); - ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source); - ASSERT_EQ(uint32_t(0), motionArgs.policyFlags); - ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - motionArgs.action); - ASSERT_EQ(0, motionArgs.flags); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState); - ASSERT_EQ(0, motionArgs.edgeFlags); - ASSERT_EQ(size_t(2), motionArgs.pointerCount); - ASSERT_EQ(0, motionArgs.pointerIds[0]); - ASSERT_EQ(1, motionArgs.pointerIds[1]); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - toDisplayX(x1), toDisplayY(y1), 1, 0, 0, 0, 0, 0, 0)); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[1], - toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0)); - ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON); - ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON); - ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime); - ASSERT_EQ(DEVICE_ID, motionArgs.deviceId); - ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source); - ASSERT_EQ(uint32_t(0), motionArgs.policyFlags); - ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action); - ASSERT_EQ(0, motionArgs.flags); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState); - ASSERT_EQ(0, motionArgs.edgeFlags); - ASSERT_EQ(size_t(1), motionArgs.pointerCount); - ASSERT_EQ(1, motionArgs.pointerIds[0]); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0)); - ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON); - ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON); - ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime); - - // Move. - x2 += 20; y2 -= 25; - processPosition(mapper, x2, y2); - processMTSync(mapper); - processSync(mapper); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime); - ASSERT_EQ(DEVICE_ID, motionArgs.deviceId); - ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source); - ASSERT_EQ(uint32_t(0), motionArgs.policyFlags); - ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action); - ASSERT_EQ(0, motionArgs.flags); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState); - ASSERT_EQ(0, motionArgs.edgeFlags); - ASSERT_EQ(size_t(1), motionArgs.pointerCount); - ASSERT_EQ(1, motionArgs.pointerIds[0]); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0)); - ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON); - ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON); - ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime); - - // New finger down. - int32_t x3 = 700, y3 = 300; - processPosition(mapper, x2, y2); - processMTSync(mapper); - processPosition(mapper, x3, y3); - processMTSync(mapper); - processSync(mapper); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime); - ASSERT_EQ(DEVICE_ID, motionArgs.deviceId); - ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source); - ASSERT_EQ(uint32_t(0), motionArgs.policyFlags); - ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - motionArgs.action); - ASSERT_EQ(0, motionArgs.flags); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState); - ASSERT_EQ(0, motionArgs.edgeFlags); - ASSERT_EQ(size_t(2), motionArgs.pointerCount); - ASSERT_EQ(0, motionArgs.pointerIds[0]); - ASSERT_EQ(1, motionArgs.pointerIds[1]); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - toDisplayX(x3), toDisplayY(y3), 1, 0, 0, 0, 0, 0, 0)); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[1], - toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0)); - ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON); - ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON); - ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime); - - // Second finger up. - x3 += 30; y3 -= 20; - processPosition(mapper, x3, y3); - processMTSync(mapper); - processSync(mapper); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime); - ASSERT_EQ(DEVICE_ID, motionArgs.deviceId); - ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source); - ASSERT_EQ(uint32_t(0), motionArgs.policyFlags); - ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - motionArgs.action); - ASSERT_EQ(0, motionArgs.flags); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState); - ASSERT_EQ(0, motionArgs.edgeFlags); - ASSERT_EQ(size_t(2), motionArgs.pointerCount); - ASSERT_EQ(0, motionArgs.pointerIds[0]); - ASSERT_EQ(1, motionArgs.pointerIds[1]); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - toDisplayX(x3), toDisplayY(y3), 1, 0, 0, 0, 0, 0, 0)); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[1], - toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0)); - ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON); - ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON); - ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime); - ASSERT_EQ(DEVICE_ID, motionArgs.deviceId); - ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source); - ASSERT_EQ(uint32_t(0), motionArgs.policyFlags); - ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action); - ASSERT_EQ(0, motionArgs.flags); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState); - ASSERT_EQ(0, motionArgs.edgeFlags); - ASSERT_EQ(size_t(1), motionArgs.pointerCount); - ASSERT_EQ(0, motionArgs.pointerIds[0]); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - toDisplayX(x3), toDisplayY(y3), 1, 0, 0, 0, 0, 0, 0)); - ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON); - ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON); - ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime); - - // Last finger up. - processMTSync(mapper); - processSync(mapper); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime); - ASSERT_EQ(DEVICE_ID, motionArgs.deviceId); - ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source); - ASSERT_EQ(uint32_t(0), motionArgs.policyFlags); - ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action); - ASSERT_EQ(0, motionArgs.flags); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState); - ASSERT_EQ(0, motionArgs.edgeFlags); - ASSERT_EQ(size_t(1), motionArgs.pointerCount); - ASSERT_EQ(0, motionArgs.pointerIds[0]); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - toDisplayX(x3), toDisplayY(y3), 1, 0, 0, 0, 0, 0, 0)); - ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON); - ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON); - ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime); - - // Should not have sent any more keys or motions. - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasNotCalled()); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasNotCalled()); -} - -TEST_F(MultiTouchInputMapperTest, Process_NormalMultiTouchGesture_WithTrackingIds) { - MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice, DISPLAY_ID); - prepareDisplay(InputReaderPolicyInterface::ROTATION_0); - prepareAxes(POSITION | ID); - prepareVirtualKeys(); - addMapperAndConfigure(mapper); - - mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON); - - FakeInputDispatcher::NotifyMotionArgs motionArgs; - - // Two fingers down at once. - int32_t x1 = 100, y1 = 125, x2 = 300, y2 = 500; - processPosition(mapper, x1, y1); - processId(mapper, 1); - processMTSync(mapper); - processPosition(mapper, x2, y2); - processId(mapper, 2); - processMTSync(mapper); - processSync(mapper); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action); - ASSERT_EQ(size_t(1), motionArgs.pointerCount); - ASSERT_EQ(1, motionArgs.pointerIds[0]); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - toDisplayX(x1), toDisplayY(y1), 1, 0, 0, 0, 0, 0, 0)); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - motionArgs.action); - ASSERT_EQ(size_t(2), motionArgs.pointerCount); - ASSERT_EQ(1, motionArgs.pointerIds[0]); - ASSERT_EQ(2, motionArgs.pointerIds[1]); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - toDisplayX(x1), toDisplayY(y1), 1, 0, 0, 0, 0, 0, 0)); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[1], - toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0)); - - // Move. - x1 += 10; y1 += 15; x2 += 5; y2 -= 10; - processPosition(mapper, x1, y1); - processId(mapper, 1); - processMTSync(mapper); - processPosition(mapper, x2, y2); - processId(mapper, 2); - processMTSync(mapper); - processSync(mapper); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action); - ASSERT_EQ(size_t(2), motionArgs.pointerCount); - ASSERT_EQ(1, motionArgs.pointerIds[0]); - ASSERT_EQ(2, motionArgs.pointerIds[1]); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - toDisplayX(x1), toDisplayY(y1), 1, 0, 0, 0, 0, 0, 0)); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[1], - toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0)); - - // First finger up. - x2 += 15; y2 -= 20; - processPosition(mapper, x2, y2); - processId(mapper, 2); - processMTSync(mapper); - processSync(mapper); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - motionArgs.action); - ASSERT_EQ(size_t(2), motionArgs.pointerCount); - ASSERT_EQ(1, motionArgs.pointerIds[0]); - ASSERT_EQ(2, motionArgs.pointerIds[1]); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - toDisplayX(x1), toDisplayY(y1), 1, 0, 0, 0, 0, 0, 0)); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[1], - toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0)); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action); - ASSERT_EQ(size_t(1), motionArgs.pointerCount); - ASSERT_EQ(2, motionArgs.pointerIds[0]); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0)); - - // Move. - x2 += 20; y2 -= 25; - processPosition(mapper, x2, y2); - processId(mapper, 2); - processMTSync(mapper); - processSync(mapper); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action); - ASSERT_EQ(size_t(1), motionArgs.pointerCount); - ASSERT_EQ(2, motionArgs.pointerIds[0]); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0)); - - // New finger down. - int32_t x3 = 700, y3 = 300; - processPosition(mapper, x2, y2); - processId(mapper, 2); - processMTSync(mapper); - processPosition(mapper, x3, y3); - processId(mapper, 3); - processMTSync(mapper); - processSync(mapper); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - motionArgs.action); - ASSERT_EQ(size_t(2), motionArgs.pointerCount); - ASSERT_EQ(2, motionArgs.pointerIds[0]); - ASSERT_EQ(3, motionArgs.pointerIds[1]); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0)); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[1], - toDisplayX(x3), toDisplayY(y3), 1, 0, 0, 0, 0, 0, 0)); - - // Second finger up. - x3 += 30; y3 -= 20; - processPosition(mapper, x3, y3); - processId(mapper, 3); - processMTSync(mapper); - processSync(mapper); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - motionArgs.action); - ASSERT_EQ(size_t(2), motionArgs.pointerCount); - ASSERT_EQ(2, motionArgs.pointerIds[0]); - ASSERT_EQ(3, motionArgs.pointerIds[1]); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0)); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[1], - toDisplayX(x3), toDisplayY(y3), 1, 0, 0, 0, 0, 0, 0)); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action); - ASSERT_EQ(size_t(1), motionArgs.pointerCount); - ASSERT_EQ(3, motionArgs.pointerIds[0]); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - toDisplayX(x3), toDisplayY(y3), 1, 0, 0, 0, 0, 0, 0)); - - // Last finger up. - processMTSync(mapper); - processSync(mapper); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action); - ASSERT_EQ(size_t(1), motionArgs.pointerCount); - ASSERT_EQ(3, motionArgs.pointerIds[0]); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - toDisplayX(x3), toDisplayY(y3), 1, 0, 0, 0, 0, 0, 0)); - - // Should not have sent any more keys or motions. - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasNotCalled()); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasNotCalled()); -} - -TEST_F(MultiTouchInputMapperTest, Process_AllAxes_WithDefaultCalibration) { - MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice, DISPLAY_ID); - prepareDisplay(InputReaderPolicyInterface::ROTATION_0); - prepareAxes(POSITION | TOUCH | TOOL | PRESSURE | ORIENTATION | ID | MINOR); - addMapperAndConfigure(mapper); - - // These calculations are based on the input device calibration documentation. - int32_t rawX = 100; - int32_t rawY = 200; - int32_t rawTouchMajor = 7; - int32_t rawTouchMinor = 6; - int32_t rawToolMajor = 9; - int32_t rawToolMinor = 8; - int32_t rawPressure = 11; - int32_t rawOrientation = 3; - int32_t id = 5; - - float x = toDisplayX(rawX); - float y = toDisplayY(rawY); - float pressure = float(rawPressure) / RAW_PRESSURE_MAX; - float size = avg(rawToolMajor, rawToolMinor) / RAW_TOOL_MAX; - float toolMajor = float(min(DISPLAY_WIDTH, DISPLAY_HEIGHT)) * rawToolMajor / RAW_TOOL_MAX; - float toolMinor = float(min(DISPLAY_WIDTH, DISPLAY_HEIGHT)) * rawToolMinor / RAW_TOOL_MAX; - float touchMajor = min(toolMajor * pressure, toolMajor); - float touchMinor = min(toolMinor * pressure, toolMinor); - float orientation = float(rawOrientation) / RAW_ORIENTATION_MAX * M_PI_2; - - processPosition(mapper, rawX, rawY); - processTouchMajor(mapper, rawTouchMajor); - processTouchMinor(mapper, rawTouchMinor); - processToolMajor(mapper, rawToolMajor); - processToolMinor(mapper, rawToolMinor); - processPressure(mapper, rawPressure); - processOrientation(mapper, rawOrientation); - processId(mapper, id); - processMTSync(mapper); - processSync(mapper); - - FakeInputDispatcher::NotifyMotionArgs args; - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(id, args.pointerIds[0]); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], - x, y, pressure, size, touchMajor, touchMinor, toolMajor, toolMinor, orientation)); -} - -TEST_F(MultiTouchInputMapperTest, Process_TouchAndToolAxes_GeometricCalibration) { - MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice, DISPLAY_ID); - prepareDisplay(InputReaderPolicyInterface::ROTATION_0); - prepareAxes(POSITION | TOUCH | TOOL | MINOR); - prepareCalibration("touch.touchSize.calibration", "geometric"); - prepareCalibration("touch.toolSize.calibration", "geometric"); - addMapperAndConfigure(mapper); - - // These calculations are based on the input device calibration documentation. - int32_t rawX = 100; - int32_t rawY = 200; - int32_t rawTouchMajor = 140; - int32_t rawTouchMinor = 120; - int32_t rawToolMajor = 180; - int32_t rawToolMinor = 160; - - float x = toDisplayX(rawX); - float y = toDisplayY(rawY); - float pressure = float(rawTouchMajor) / RAW_TOUCH_MAX; - float size = avg(rawToolMajor, rawToolMinor) / RAW_TOOL_MAX; - float scale = avg(float(DISPLAY_WIDTH) / (RAW_X_MAX - RAW_X_MIN), - float(DISPLAY_HEIGHT) / (RAW_Y_MAX - RAW_Y_MIN)); - float toolMajor = float(rawToolMajor) * scale; - float toolMinor = float(rawToolMinor) * scale; - float touchMajor = min(float(rawTouchMajor) * scale, toolMajor); - float touchMinor = min(float(rawTouchMinor) * scale, toolMinor); - - processPosition(mapper, rawX, rawY); - processTouchMajor(mapper, rawTouchMajor); - processTouchMinor(mapper, rawTouchMinor); - processToolMajor(mapper, rawToolMajor); - processToolMinor(mapper, rawToolMinor); - processMTSync(mapper); - processSync(mapper); - - FakeInputDispatcher::NotifyMotionArgs args; - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], - x, y, pressure, size, touchMajor, touchMinor, toolMajor, toolMinor, 0)); -} - -TEST_F(MultiTouchInputMapperTest, Process_TouchToolPressureSizeAxes_SummedLinearCalibration) { - MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice, DISPLAY_ID); - prepareDisplay(InputReaderPolicyInterface::ROTATION_0); - prepareAxes(POSITION | TOUCH | TOOL); - prepareCalibration("touch.touchSize.calibration", "pressure"); - prepareCalibration("touch.toolSize.calibration", "linear"); - prepareCalibration("touch.toolSize.linearScale", "10"); - prepareCalibration("touch.toolSize.linearBias", "160"); - prepareCalibration("touch.toolSize.isSummed", "1"); - prepareCalibration("touch.pressure.calibration", "amplitude"); - prepareCalibration("touch.pressure.source", "touch"); - prepareCalibration("touch.pressure.scale", "0.01"); - addMapperAndConfigure(mapper); - - // These calculations are based on the input device calibration documentation. - // Note: We only provide a single common touch/tool value because the device is assumed - // not to emit separate values for each pointer (isSummed = 1). - int32_t rawX = 100; - int32_t rawY = 200; - int32_t rawX2 = 150; - int32_t rawY2 = 250; - int32_t rawTouchMajor = 60; - int32_t rawToolMajor = 5; - - float x = toDisplayX(rawX); - float y = toDisplayY(rawY); - float x2 = toDisplayX(rawX2); - float y2 = toDisplayY(rawY2); - float pressure = float(rawTouchMajor) * 0.01f; - float size = float(rawToolMajor) / RAW_TOOL_MAX; - float tool = (float(rawToolMajor) * 10.0f + 160.0f) / 2; - float touch = min(tool * pressure, tool); - - processPosition(mapper, rawX, rawY); - processTouchMajor(mapper, rawTouchMajor); - processToolMajor(mapper, rawToolMajor); - processMTSync(mapper); - processPosition(mapper, rawX2, rawY2); - processTouchMajor(mapper, rawTouchMajor); - processToolMajor(mapper, rawToolMajor); - processMTSync(mapper); - processSync(mapper); - - FakeInputDispatcher::NotifyMotionArgs args; - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - args.action); - ASSERT_EQ(size_t(2), args.pointerCount); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], - x, y, pressure, size, touch, touch, tool, tool, 0)); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[1], - x2, y2, pressure, size, touch, touch, tool, tool, 0)); -} - -TEST_F(MultiTouchInputMapperTest, Process_TouchToolPressureSizeAxes_AreaCalibration) { - MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice, DISPLAY_ID); - prepareDisplay(InputReaderPolicyInterface::ROTATION_0); - prepareAxes(POSITION | TOUCH | TOOL); - prepareCalibration("touch.touchSize.calibration", "pressure"); - prepareCalibration("touch.toolSize.calibration", "area"); - prepareCalibration("touch.toolSize.areaScale", "22"); - prepareCalibration("touch.toolSize.areaBias", "1"); - prepareCalibration("touch.toolSize.linearScale", "9.2"); - prepareCalibration("touch.toolSize.linearBias", "3"); - prepareCalibration("touch.pressure.calibration", "amplitude"); - prepareCalibration("touch.pressure.source", "touch"); - prepareCalibration("touch.pressure.scale", "0.01"); - addMapperAndConfigure(mapper); - - // These calculations are based on the input device calibration documentation. - int32_t rawX = 100; - int32_t rawY = 200; - int32_t rawTouchMajor = 60; - int32_t rawToolMajor = 5; - - float x = toDisplayX(rawX); - float y = toDisplayY(rawY); - float pressure = float(rawTouchMajor) * 0.01f; - float size = float(rawToolMajor) / RAW_TOOL_MAX; - float tool = sqrtf(float(rawToolMajor) * 22.0f + 1.0f) * 9.2f + 3.0f; - float touch = min(tool * pressure, tool); - - processPosition(mapper, rawX, rawY); - processTouchMajor(mapper, rawTouchMajor); - processToolMajor(mapper, rawToolMajor); - processMTSync(mapper); - processSync(mapper); - - FakeInputDispatcher::NotifyMotionArgs args; - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], - x, y, pressure, size, touch, touch, tool, tool, 0)); -} - -} // namespace android diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index eb75ed8940..e8d40ba083 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -28,6 +28,7 @@ commonSources:= \ Flattenable.cpp \ ObbFile.cpp \ Pool.cpp \ + PropertyMap.cpp \ RefBase.cpp \ ResourceTypes.cpp \ SharedBuffer.cpp \ @@ -41,6 +42,8 @@ commonSources:= \ TextOutput.cpp \ Threads.cpp \ Timers.cpp \ + Tokenizer.cpp \ + Unicode.cpp \ VectorImpl.cpp \ ZipFileCRO.cpp \ ZipFileRO.cpp \ @@ -67,11 +70,6 @@ LOCAL_CFLAGS += -DMB_CUR_MAX=1 endif endif -ifeq ($(HOST_OS),darwin) -# MacOS doesn't have lseek64. However, off_t is 64-bit anyway. -LOCAL_CFLAGS += -DOFF_T_IS_64_BIT -endif - include $(BUILD_HOST_STATIC_LIBRARY) diff --git a/libs/utils/Asset.cpp b/libs/utils/Asset.cpp index cef7db492e..a18294b18c 100644 --- a/libs/utils/Asset.cpp +++ b/libs/utils/Asset.cpp @@ -35,6 +35,9 @@ #include <fcntl.h> #include <errno.h> #include <assert.h> +#include <unistd.h> +#include <sys/stat.h> +#include <sys/types.h> using namespace android; @@ -62,7 +65,7 @@ String8 Asset::getAssetAllocations() if (cur->isAllocated()) { res.append(" "); res.append(cur->getAssetSource()); - off_t size = (cur->getLength()+512)/1024; + off64_t size = (cur->getLength()+512)/1024; char buf[64]; sprintf(buf, ": %dK\n", (int)size); res.append(buf); @@ -119,7 +122,7 @@ Asset::~Asset(void) { _FileAsset* pAsset; status_t result; - off_t length; + off64_t length; int fd; fd = open(fileName, O_RDONLY | O_BINARY); @@ -132,12 +135,26 @@ Asset::~Asset(void) * always open things read-only it doesn't really matter, so there's * no value in incurring the extra overhead of an fstat() call. */ - length = lseek(fd, 0, SEEK_END); + // TODO(kroot): replace this with fstat despite the plea above. +#if 1 + length = lseek64(fd, 0, SEEK_END); if (length < 0) { ::close(fd); return NULL; } - (void) lseek(fd, 0, SEEK_SET); + (void) lseek64(fd, 0, SEEK_SET); +#else + struct stat st; + if (fstat(fd, &st) < 0) { + ::close(fd); + return NULL; + } + + if (!S_ISREG(st.st_mode)) { + ::close(fd); + return NULL; + } +#endif pAsset = new _FileAsset; result = pAsset->openChunk(fileName, fd, 0, length); @@ -162,7 +179,7 @@ Asset::~Asset(void) { _CompressedAsset* pAsset; status_t result; - off_t fileLen; + off64_t fileLen; bool scanResult; long offset; int method; @@ -215,7 +232,7 @@ Asset::~Asset(void) /* * Create a new Asset from part of an open file. */ -/*static*/ Asset* Asset::createFromFileSegment(int fd, off_t offset, +/*static*/ Asset* Asset::createFromFileSegment(int fd, off64_t offset, size_t length, AccessMode mode) { _FileAsset* pAsset; @@ -233,7 +250,7 @@ Asset::~Asset(void) /* * Create a new Asset from compressed data in an open file. */ -/*static*/ Asset* Asset::createFromCompressedData(int fd, off_t offset, +/*static*/ Asset* Asset::createFromCompressedData(int fd, off64_t offset, int compressionMethod, size_t uncompressedLen, size_t compressedLen, AccessMode mode) { @@ -295,9 +312,9 @@ Asset::~Asset(void) * * Returns the new chunk offset, or -1 if the seek is illegal. */ -off_t Asset::handleSeek(off_t offset, int whence, off_t curPosn, off_t maxPosn) +off64_t Asset::handleSeek(off64_t offset, int whence, off64_t curPosn, off64_t maxPosn) { - off_t newOffset; + off64_t newOffset; switch (whence) { case SEEK_SET: @@ -311,15 +328,15 @@ off_t Asset::handleSeek(off_t offset, int whence, off_t curPosn, off_t maxPosn) break; default: LOGW("unexpected whence %d\n", whence); - // this was happening due to an off_t size mismatch + // this was happening due to an off64_t size mismatch assert(false); - return (off_t) -1; + return (off64_t) -1; } if (newOffset < 0 || newOffset > maxPosn) { LOGW("seek out of range: want %ld, end=%ld\n", (long) newOffset, (long) maxPosn); - return (off_t) -1; + return (off64_t) -1; } return newOffset; @@ -353,7 +370,7 @@ _FileAsset::~_FileAsset(void) * * Zero-length chunks are allowed. */ -status_t _FileAsset::openChunk(const char* fileName, int fd, off_t offset, size_t length) +status_t _FileAsset::openChunk(const char* fileName, int fd, off64_t offset, size_t length) { assert(mFp == NULL); // no reopen assert(mMap == NULL); @@ -363,15 +380,15 @@ status_t _FileAsset::openChunk(const char* fileName, int fd, off_t offset, size_ /* * Seek to end to get file length. */ - off_t fileLength; - fileLength = lseek(fd, 0, SEEK_END); - if (fileLength == (off_t) -1) { + off64_t fileLength; + fileLength = lseek64(fd, 0, SEEK_END); + if (fileLength == (off64_t) -1) { // probably a bad file descriptor LOGD("failed lseek (errno=%d)\n", errno); return UNKNOWN_ERROR; } - if ((off_t) (offset + length) > fileLength) { + if ((off64_t) (offset + length) > fileLength) { LOGD("start (%ld) + len (%ld) > end (%ld)\n", (long) offset, (long) length, (long) fileLength); return BAD_INDEX; @@ -482,21 +499,21 @@ ssize_t _FileAsset::read(void* buf, size_t count) /* * Seek to a new position. */ -off_t _FileAsset::seek(off_t offset, int whence) +off64_t _FileAsset::seek(off64_t offset, int whence) { - off_t newPosn; - long actualOffset; + off64_t newPosn; + off64_t actualOffset; // compute new position within chunk newPosn = handleSeek(offset, whence, mOffset, mLength); - if (newPosn == (off_t) -1) + if (newPosn == (off64_t) -1) return newPosn; - actualOffset = (long) (mStart + newPosn); + actualOffset = mStart + newPosn; if (mFp != NULL) { if (fseek(mFp, (long) actualOffset, SEEK_SET) != 0) - return (off_t) -1; + return (off64_t) -1; } mOffset = actualOffset - mStart; @@ -603,7 +620,7 @@ const void* _FileAsset::getBuffer(bool wordAligned) } } -int _FileAsset::openFileDescriptor(off_t* outStart, off_t* outLength) const +int _FileAsset::openFileDescriptor(off64_t* outStart, off64_t* outLength) const { if (mMap != NULL) { const char* fname = mMap->getFileName(); @@ -678,7 +695,7 @@ _CompressedAsset::~_CompressedAsset(void) * This currently just sets up some values and returns. On the first * read, we expand the entire file into a buffer and return data from it. */ -status_t _CompressedAsset::openChunk(int fd, off_t offset, +status_t _CompressedAsset::openChunk(int fd, off64_t offset, int compressionMethod, size_t uncompressedLen, size_t compressedLen) { assert(mFd < 0); // no re-open @@ -782,13 +799,13 @@ ssize_t _CompressedAsset::read(void* buf, size_t count) * expensive, because it requires plowing through a bunch of compressed * data. */ -off_t _CompressedAsset::seek(off_t offset, int whence) +off64_t _CompressedAsset::seek(off64_t offset, int whence) { - off_t newPosn; + off64_t newPosn; // compute new position within chunk newPosn = handleSeek(offset, whence, mOffset, mUncompressedLen); - if (newPosn == (off_t) -1) + if (newPosn == (off64_t) -1) return newPosn; if (mZipInflater) { diff --git a/libs/utils/FileMap.cpp b/libs/utils/FileMap.cpp index e1ba9b2389..c220a9016f 100644 --- a/libs/utils/FileMap.cpp +++ b/libs/utils/FileMap.cpp @@ -63,16 +63,18 @@ FileMap::~FileMap(void) free(mFileName); } #ifdef HAVE_POSIX_FILEMAP - if (munmap(mBasePtr, mBaseLength) != 0) { + if (mBasePtr && munmap(mBasePtr, mBaseLength) != 0) { LOGD("munmap(%p, %d) failed\n", mBasePtr, (int) mBaseLength); } #endif #ifdef HAVE_WIN32_FILEMAP - if ( UnmapViewOfFile(mBasePtr) == 0) { + if (mBasePtr && UnmapViewOfFile(mBasePtr) == 0) { LOGD("UnmapViewOfFile(%p) failed, error = %ld\n", mBasePtr, GetLastError() ); } - CloseHandle(mFileMapping); + if (mFileMapping != INVALID_HANDLE_VALUE) { + CloseHandle(mFileMapping); + } CloseHandle(mFileHandle); #endif } @@ -86,11 +88,12 @@ FileMap::~FileMap(void) * * Returns "false" on failure. */ -bool FileMap::create(const char* origFileName, int fd, off_t offset, size_t length, bool readOnly) +bool FileMap::create(const char* origFileName, int fd, off64_t offset, size_t length, + bool readOnly) { #ifdef HAVE_WIN32_FILEMAP int adjust; - off_t adjOffset; + off64_t adjOffset; size_t adjLength; if (mPageSize == -1) { @@ -129,7 +132,7 @@ bool FileMap::create(const char* origFileName, int fd, off_t offset, size_t leng #endif #ifdef HAVE_POSIX_FILEMAP int prot, flags, adjust; - off_t adjOffset; + off64_t adjOffset; size_t adjLength; void* ptr; diff --git a/libs/utils/ObbFile.cpp b/libs/utils/ObbFile.cpp index 2c3724c0af..2907b5666b 100644 --- a/libs/utils/ObbFile.cpp +++ b/libs/utils/ObbFile.cpp @@ -22,6 +22,8 @@ #include <unistd.h> #define LOG_TAG "ObbFile" + +#include <utils/Compat.h> #include <utils/Log.h> #include <utils/ObbFile.h> @@ -67,17 +69,6 @@ _rc; }) #endif -/* - * Work around situations where off_t is 64-bit and use off64_t in - * situations where it's 32-bit. - */ -#ifdef OFF_T_IS_64_BIT -#define my_lseek64 lseek -typedef off_t my_off64_t; -#else -#define my_lseek64 lseek64 -typedef off64_t my_off64_t; -#endif namespace android { @@ -125,7 +116,7 @@ bool ObbFile::readFrom(int fd) bool ObbFile::parseObbFile(int fd) { - my_off64_t fileLength = my_lseek64(fd, 0, SEEK_END); + off64_t fileLength = lseek64(fd, 0, SEEK_END); if (fileLength < kFooterMinSize) { if (fileLength < 0) { @@ -140,7 +131,7 @@ bool ObbFile::parseObbFile(int fd) size_t footerSize; { - my_lseek64(fd, fileLength - kFooterTagSize, SEEK_SET); + lseek64(fd, fileLength - kFooterTagSize, SEEK_SET); char *footer = new char[kFooterTagSize]; actual = TEMP_FAILURE_RETRY(read(fd, footer, kFooterTagSize)); @@ -171,8 +162,8 @@ bool ObbFile::parseObbFile(int fd) } } - my_off64_t fileOffset = fileLength - footerSize - kFooterTagSize; - if (my_lseek64(fd, fileOffset, SEEK_SET) != fileOffset) { + off64_t fileOffset = fileLength - footerSize - kFooterTagSize; + if (lseek64(fd, fileOffset, SEEK_SET) != fileOffset) { LOGW("seek %lld failed: %s\n", fileOffset, strerror(errno)); return false; } @@ -211,10 +202,10 @@ bool ObbFile::parseObbFile(int fd) memcpy(&mSalt, (unsigned char*)scanBuf + kSaltOffset, sizeof(mSalt)); - uint32_t packageNameLen = get4LE((unsigned char*)scanBuf + kPackageNameLenOffset); - if (packageNameLen <= 0 + size_t packageNameLen = get4LE((unsigned char*)scanBuf + kPackageNameLenOffset); + if (packageNameLen == 0 || packageNameLen > (footerSize - kPackageNameOffset)) { - LOGW("bad ObbFile package name length (0x%04x; 0x%04x possible)\n", + LOGW("bad ObbFile package name length (0x%04zx; 0x%04zx possible)\n", packageNameLen, footerSize - kPackageNameOffset); free(scanBuf); return false; @@ -257,7 +248,7 @@ bool ObbFile::writeTo(int fd) return false; } - my_lseek64(fd, 0, SEEK_END); + lseek64(fd, 0, SEEK_END); if (mPackageName.size() == 0 || mVersion == -1) { LOGW("tried to write uninitialized ObbFile data\n"); diff --git a/libs/utils/PropertyMap.cpp b/libs/utils/PropertyMap.cpp new file mode 100644 index 0000000000..d472d45c4d --- /dev/null +++ b/libs/utils/PropertyMap.cpp @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "PropertyMap" + +#include <stdlib.h> +#include <string.h> + +#include <utils/PropertyMap.h> +#include <utils/Log.h> + +// Enables debug output for the parser. +#define DEBUG_PARSER 0 + +// Enables debug output for parser performance. +#define DEBUG_PARSER_PERFORMANCE 0 + + +namespace android { + +static const char* WHITESPACE = " \t\r"; +static const char* WHITESPACE_OR_PROPERTY_DELIMITER = " \t\r="; + + +// --- PropertyMap --- + +PropertyMap::PropertyMap() { +} + +PropertyMap::~PropertyMap() { +} + +void PropertyMap::clear() { + mProperties.clear(); +} + +void PropertyMap::addProperty(const String8& key, const String8& value) { + mProperties.add(key, value); +} + +bool PropertyMap::hasProperty(const String8& key) const { + return mProperties.indexOfKey(key) >= 0; +} + +bool PropertyMap::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 PropertyMap::tryGetProperty(const String8& key, bool& outValue) const { + int32_t intValue; + if (!tryGetProperty(key, intValue)) { + return false; + } + + outValue = intValue; + return true; +} + +bool PropertyMap::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("Property key '%s' has invalid value '%s'. Expected an integer.", + key.string(), stringValue.string()); + return false; + } + outValue = value; + return true; +} + +bool PropertyMap::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("Property key '%s' has invalid value '%s'. Expected a float.", + key.string(), stringValue.string()); + return false; + } + outValue = value; + return true; +} + +void PropertyMap::addAll(const PropertyMap* map) { + for (size_t i = 0; i < map->mProperties.size(); i++) { + mProperties.add(map->mProperties.keyAt(i), map->mProperties.valueAt(i)); + } +} + +status_t PropertyMap::load(const String8& filename, PropertyMap** outMap) { + *outMap = NULL; + + Tokenizer* tokenizer; + status_t status = Tokenizer::open(filename, &tokenizer); + if (status) { + LOGE("Error %d opening property file %s.", status, filename.string()); + } else { + PropertyMap* map = new PropertyMap(); + if (!map) { + LOGE("Error allocating property map."); + status = NO_MEMORY; + } else { +#if DEBUG_PARSER_PERFORMANCE + nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC); +#endif + Parser parser(map, tokenizer); + status = parser.parse(); +#if DEBUG_PARSER_PERFORMANCE + nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime; + LOGD("Parsed property file '%s' %d lines in %0.3fms.", + tokenizer->getFilename().string(), tokenizer->getLineNumber(), + elapsedTime / 1000000.0); +#endif + if (status) { + delete map; + } else { + *outMap = map; + } + } + delete tokenizer; + } + return status; +} + + +// --- PropertyMap::Parser --- + +PropertyMap::Parser::Parser(PropertyMap* map, Tokenizer* tokenizer) : + mMap(map), mTokenizer(tokenizer) { +} + +PropertyMap::Parser::~Parser() { +} + +status_t PropertyMap::Parser::parse() { + while (!mTokenizer->isEof()) { +#if DEBUG_PARSER + LOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(), + mTokenizer->peekRemainderOfLine().string()); +#endif + + mTokenizer->skipDelimiters(WHITESPACE); + + if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') { + String8 keyToken = mTokenizer->nextToken(WHITESPACE_OR_PROPERTY_DELIMITER); + if (keyToken.isEmpty()) { + LOGE("%s: Expected non-empty property key.", mTokenizer->getLocation().string()); + return BAD_VALUE; + } + + mTokenizer->skipDelimiters(WHITESPACE); + + if (mTokenizer->nextChar() != '=') { + LOGE("%s: Expected '=' between property key and value.", + mTokenizer->getLocation().string()); + return BAD_VALUE; + } + + mTokenizer->skipDelimiters(WHITESPACE); + + String8 valueToken = mTokenizer->nextToken(WHITESPACE); + if (valueToken.find("\\", 0) >= 0 || valueToken.find("\"", 0) >= 0) { + LOGE("%s: Found reserved character '\\' or '\"' in property value.", + mTokenizer->getLocation().string()); + return BAD_VALUE; + } + + mTokenizer->skipDelimiters(WHITESPACE); + if (!mTokenizer->isEol()) { + LOGE("%s: Expected end of line, got '%s'.", + mTokenizer->getLocation().string(), + mTokenizer->peekRemainderOfLine().string()); + return BAD_VALUE; + } + + if (mMap->hasProperty(keyToken)) { + LOGE("%s: Duplicate property value for key '%s'.", + mTokenizer->getLocation().string(), keyToken.string()); + return BAD_VALUE; + } + + mMap->addProperty(keyToken, valueToken); + } + + mTokenizer->nextLine(); + } + return NO_ERROR; +} + +} // namespace android diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp index 8345cc363d..bbf50934df 100644 --- a/libs/utils/ResourceTypes.cpp +++ b/libs/utils/ResourceTypes.cpp @@ -317,6 +317,12 @@ status_t ResStringPool::setTo(const void* data, size_t size, bool copyData) mStringPoolSize = (mHeader->header.size-mHeader->stringsStart)/charSize; } else { + // check invariant: styles starts before end of data + if (mHeader->stylesStart >= (mHeader->header.size-sizeof(uint16_t))) { + LOGW("Bad style block: style block starts at %d past data size of %d\n", + (int)mHeader->stylesStart, (int)mHeader->header.size); + return (mError=BAD_TYPE); + } // check invariant: styles follow the strings if (mHeader->stylesStart <= mHeader->stringsStart) { LOGW("Bad style block: style block starts at %d, before strings at %d\n", @@ -438,15 +444,51 @@ void ResStringPool::uninit() } } -#define DECODE_LENGTH(str, chrsz, len) \ - len = *(str); \ - if (*(str)&(1<<(chrsz*8-1))) { \ - (str)++; \ - len = (((len)&((1<<(chrsz*8-1))-1))<<(chrsz*8)) + *(str); \ - } \ - (str)++; +/** + * Strings in UTF-16 format have length indicated by a length encoded in the + * stored data. It is either 1 or 2 characters of length data. This allows a + * maximum length of 0x7FFFFFF (2147483647 bytes), but if you're storing that + * much data in a string, you're abusing them. + * + * If the high bit is set, then there are two characters or 4 bytes of length + * data encoded. In that case, drop the high bit of the first character and + * add it together with the next character. + */ +static inline size_t +decodeLength(const char16_t** str) +{ + size_t len = **str; + if ((len & 0x8000) != 0) { + (*str)++; + len = ((len & 0x7FFF) << 16) | **str; + } + (*str)++; + return len; +} + +/** + * Strings in UTF-8 format have length indicated by a length encoded in the + * stored data. It is either 1 or 2 characters of length data. This allows a + * maximum length of 0x7FFF (32767 bytes), but you should consider storing + * text in another way if you're using that much data in a single string. + * + * If the high bit is set, then there are two characters or 2 bytes of length + * data encoded. In that case, drop the high bit of the first character and + * add it together with the next character. + */ +static inline size_t +decodeLength(const uint8_t** str) +{ + size_t len = **str; + if ((len & 0x80) != 0) { + (*str)++; + len = ((len & 0x7F) << 8) | **str; + } + (*str)++; + return len; +} -const uint16_t* ResStringPool::stringAt(size_t idx, size_t* outLen) const +const uint16_t* ResStringPool::stringAt(size_t idx, size_t* u16len) const { if (mError == NO_ERROR && idx < mHeader->stringCount) { const bool isUTF8 = (mHeader->flags&ResStringPool_header::UTF8_FLAG) != 0; @@ -455,37 +497,51 @@ const uint16_t* ResStringPool::stringAt(size_t idx, size_t* outLen) const if (!isUTF8) { const char16_t* strings = (char16_t*)mStrings; const char16_t* str = strings+off; - DECODE_LENGTH(str, sizeof(char16_t), *outLen) - if ((uint32_t)(str+*outLen-strings) < mStringPoolSize) { + + *u16len = decodeLength(&str); + if ((uint32_t)(str+*u16len-strings) < mStringPoolSize) { return str; } else { LOGW("Bad string block: string #%d extends to %d, past end at %d\n", - (int)idx, (int)(str+*outLen-strings), (int)mStringPoolSize); + (int)idx, (int)(str+*u16len-strings), (int)mStringPoolSize); } } else { const uint8_t* strings = (uint8_t*)mStrings; - const uint8_t* str = strings+off; - DECODE_LENGTH(str, sizeof(uint8_t), *outLen) - size_t encLen; - DECODE_LENGTH(str, sizeof(uint8_t), encLen) - if ((uint32_t)(str+encLen-strings) < mStringPoolSize) { + const uint8_t* u8str = strings+off; + + *u16len = decodeLength(&u8str); + size_t u8len = decodeLength(&u8str); + + // encLen must be less than 0x7FFF due to encoding. + if ((uint32_t)(u8str+u8len-strings) < mStringPoolSize) { AutoMutex lock(mDecodeLock); + if (mCache[idx] != NULL) { return mCache[idx]; } - char16_t *u16str = (char16_t *)calloc(*outLen+1, sizeof(char16_t)); + + ssize_t actualLen = utf8_to_utf16_length(u8str, u8len); + if (actualLen < 0 || (size_t)actualLen != *u16len) { + LOGW("Bad string block: string #%lld decoded length is not correct " + "%lld vs %llu\n", + (long long)idx, (long long)actualLen, (long long)*u16len); + return NULL; + } + + char16_t *u16str = (char16_t *)calloc(*u16len+1, sizeof(char16_t)); if (!u16str) { LOGW("No memory when trying to allocate decode cache for string #%d\n", (int)idx); return NULL; } - const unsigned char *u8src = reinterpret_cast<const unsigned char *>(str); - utf8_to_utf16(u8src, encLen, u16str, *outLen); + + utf8_to_utf16(u8str, u8len, u16str); mCache[idx] = u16str; return u16str; } else { - LOGW("Bad string block: string #%d extends to %d, past end at %d\n", - (int)idx, (int)(str+encLen-strings), (int)mStringPoolSize); + LOGW("Bad string block: string #%lld extends to %lld, past end at %lld\n", + (long long)idx, (long long)(u8str+u8len-strings), + (long long)mStringPoolSize); } } } else { @@ -506,9 +562,8 @@ const char* ResStringPool::string8At(size_t idx, size_t* outLen) const if (isUTF8) { const uint8_t* strings = (uint8_t*)mStrings; const uint8_t* str = strings+off; - DECODE_LENGTH(str, sizeof(uint8_t), *outLen) - size_t encLen; - DECODE_LENGTH(str, sizeof(uint8_t), encLen) + *outLen = decodeLength(&str); + size_t encLen = decodeLength(&str); if ((uint32_t)(str+encLen-strings) < mStringPoolSize) { return (const char*)str; } else { @@ -1878,13 +1933,19 @@ bool ResTable::getResourceName(uint32_t resID, resource_name* outName) const outName->type = grp->basePackage->typeStrings.stringAt(t, &outName->typeLen); outName->name = grp->basePackage->keyStrings.stringAt( dtohl(entry->key.index), &outName->nameLen); + + // If we have a bad index for some reason, we should abort. + if (outName->type == NULL || outName->name == NULL) { + return false; + } + return true; } return false; } -ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag, +ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag, uint16_t density, uint32_t* outSpecFlags, ResTable_config* outConfig) const { if (mError != NO_ERROR) { @@ -1914,7 +1975,7 @@ ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag memset(&bestItem, 0, sizeof(bestItem)); // make the compiler shut up if (outSpecFlags != NULL) *outSpecFlags = 0; - + // Look through all resource packages, starting with the most // recently added. const PackageGroup* const grp = mPackageGroups[p]; @@ -1922,6 +1983,22 @@ ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag LOGW("Bad identifier when getting value for resource number 0x%08x", resID); return BAD_INDEX; } + + // Allow overriding density + const ResTable_config* desiredConfig = &mParams; + ResTable_config* overrideConfig = NULL; + if (density > 0) { + overrideConfig = (ResTable_config*) malloc(sizeof(ResTable_config)); + if (overrideConfig == NULL) { + LOGE("Couldn't malloc ResTable_config for overrides: %s", strerror(errno)); + return BAD_INDEX; + } + memcpy(overrideConfig, &mParams, sizeof(ResTable_config)); + overrideConfig->density = density; + desiredConfig = overrideConfig; + } + + ssize_t rc = BAD_VALUE; size_t ip = grp->packages.size(); while (ip > 0) { ip--; @@ -1931,12 +2008,13 @@ ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag const ResTable_type* type; const ResTable_entry* entry; const Type* typeClass; - ssize_t offset = getEntry(package, t, e, &mParams, &type, &entry, &typeClass); + ssize_t offset = getEntry(package, t, e, desiredConfig, &type, &entry, &typeClass); if (offset <= 0) { if (offset < 0) { LOGW("Failure getting entry for 0x%08x (t=%d e=%d) in package %zd (error %d)\n", resID, t, e, ip, (int)offset); - return offset; + rc = offset; + goto out; } continue; } @@ -1951,13 +2029,14 @@ ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag TABLE_NOISY(aout << "Resource type data: " << HexDump(type, dtohl(type->header.size)) << endl); - + if ((size_t)offset > (dtohl(type->header.size)-sizeof(Res_value))) { LOGW("ResTable_item at %d is beyond type chunk data %d", (int)offset, dtohl(type->header.size)); - return BAD_TYPE; + rc = BAD_TYPE; + goto out; } - + const Res_value* item = (const Res_value*)(((const uint8_t*)type) + offset); ResTable_config thisConfig; @@ -1999,10 +2078,16 @@ ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag outValue->data, &len)).string() : "", outValue->data)); - return bestPackage->header->index; + rc = bestPackage->header->index; + goto out; + } + +out: + if (overrideConfig != NULL) { + free(overrideConfig); } - return BAD_VALUE; + return rc; } ssize_t ResTable::resolveReference(Res_value* value, ssize_t blockIndex, @@ -2015,7 +2100,7 @@ ssize_t ResTable::resolveReference(Res_value* value, ssize_t blockIndex, if (outLastRef) *outLastRef = value->data; uint32_t lastRef = value->data; uint32_t newFlags = 0; - const ssize_t newIndex = getResource(value->data, value, true, &newFlags, + const ssize_t newIndex = getResource(value->data, value, true, 0, &newFlags, outConfig); if (newIndex == BAD_INDEX) { return BAD_INDEX; @@ -2609,6 +2694,24 @@ bool ResTable::expandResourceRef(const uint16_t* refStr, size_t refLen, *outType = *defType; } *outName = String16(p, end-p); + if(**outPackage == 0) { + if(outErrorMsg) { + *outErrorMsg = "Resource package cannot be an empty string"; + } + return false; + } + if(**outType == 0) { + if(outErrorMsg) { + *outErrorMsg = "Resource type cannot be an empty string"; + } + return false; + } + if(**outName == 0) { + if(outErrorMsg) { + *outErrorMsg = "Resource id cannot be an empty string"; + } + return false; + } return true; } @@ -4127,13 +4230,16 @@ void ResTable::print(bool inclValues) const | (0x00ff0000 & ((typeIndex+1)<<16)) | (0x0000ffff & (entryIndex)); resource_name resName; - this->getResourceName(resID, &resName); - printf(" spec resource 0x%08x %s:%s/%s: flags=0x%08x\n", - resID, - CHAR16_TO_CSTR(resName.package, resName.packageLen), - CHAR16_TO_CSTR(resName.type, resName.typeLen), - CHAR16_TO_CSTR(resName.name, resName.nameLen), - dtohl(typeConfigs->typeSpecFlags[entryIndex])); + if (this->getResourceName(resID, &resName)) { + printf(" spec resource 0x%08x %s:%s/%s: flags=0x%08x\n", + resID, + CHAR16_TO_CSTR(resName.package, resName.packageLen), + CHAR16_TO_CSTR(resName.type, resName.typeLen), + CHAR16_TO_CSTR(resName.name, resName.nameLen), + dtohl(typeConfigs->typeSpecFlags[entryIndex])); + } else { + printf(" INVALID TYPE CONFIG FOR RESOURCE 0x%08x\n", resID); + } } } for (size_t configIndex=0; configIndex<NTC; configIndex++) { @@ -4340,11 +4446,14 @@ void ResTable::print(bool inclValues) const | (0x00ff0000 & ((typeIndex+1)<<16)) | (0x0000ffff & (entryIndex)); resource_name resName; - this->getResourceName(resID, &resName); - printf(" resource 0x%08x %s:%s/%s: ", resID, - CHAR16_TO_CSTR(resName.package, resName.packageLen), - CHAR16_TO_CSTR(resName.type, resName.typeLen), - CHAR16_TO_CSTR(resName.name, resName.nameLen)); + if (this->getResourceName(resID, &resName)) { + printf(" resource 0x%08x %s:%s/%s: ", resID, + CHAR16_TO_CSTR(resName.package, resName.packageLen), + CHAR16_TO_CSTR(resName.type, resName.typeLen), + CHAR16_TO_CSTR(resName.name, resName.nameLen)); + } else { + printf(" INVALID RESOURCE 0x%08x: ", resID); + } if ((thisOffset&0x3) != 0) { printf("NON-INTEGER OFFSET: %p\n", (void*)thisOffset); continue; @@ -4402,18 +4511,19 @@ void ResTable::print(bool inclValues) const print_value(pkg, value); } else if (bagPtr != NULL) { const int N = dtohl(bagPtr->count); - const ResTable_map* mapPtr = (const ResTable_map*) - (((const uint8_t*)ent) + esize); + const uint8_t* baseMapPtr = (const uint8_t*)ent; + size_t mapOffset = esize; + const ResTable_map* mapPtr = (ResTable_map*)(baseMapPtr+mapOffset); printf(" Parent=0x%08x, Count=%d\n", dtohl(bagPtr->parent.ident), N); - for (int i=0; i<N; i++) { + for (int i=0; i<N && mapOffset < (typeSize-sizeof(ResTable_map)); i++) { printf(" #%i (Key=0x%08x): ", i, dtohl(mapPtr->name.ident)); value.copyFrom_dtoh(mapPtr->value); print_value(pkg, value); const size_t size = dtohs(mapPtr->value.size); - mapPtr = (ResTable_map*)(((const uint8_t*)mapPtr) - + size + sizeof(*mapPtr)-sizeof(mapPtr->value)); + mapOffset += size + sizeof(*mapPtr)-sizeof(mapPtr->value); + mapPtr = (ResTable_map*)(baseMapPtr+mapOffset); } } } diff --git a/libs/utils/StreamingZipInflater.cpp b/libs/utils/StreamingZipInflater.cpp index 1f62ac5189..5a162cc49e 100644 --- a/libs/utils/StreamingZipInflater.cpp +++ b/libs/utils/StreamingZipInflater.cpp @@ -31,7 +31,7 @@ using namespace android; /* * Streaming access to compressed asset data in an open fd */ -StreamingZipInflater::StreamingZipInflater(int fd, off_t compDataStart, +StreamingZipInflater::StreamingZipInflater(int fd, off64_t compDataStart, size_t uncompSize, size_t compSize) { mFd = fd; mDataMap = NULL; @@ -210,7 +210,7 @@ int StreamingZipInflater::readNextChunk() { // seeking backwards requires uncompressing fom the beginning, so is very // expensive. seeking forwards only requires uncompressing from the current // position to the destination. -off_t StreamingZipInflater::seekAbsolute(off_t absoluteInputPosition) { +off64_t StreamingZipInflater::seekAbsolute(off64_t absoluteInputPosition) { if (absoluteInputPosition < mOutCurPosition) { // rewind and reprocess the data from the beginning if (!mStreamNeedsInit) { diff --git a/libs/utils/String16.cpp b/libs/utils/String16.cpp index eab7b2b926..4ce166426c 100644 --- a/libs/utils/String16.cpp +++ b/libs/utils/String16.cpp @@ -18,228 +18,17 @@ #include <utils/Debug.h> #include <utils/Log.h> +#include <utils/Unicode.h> #include <utils/String8.h> #include <utils/TextOutput.h> #include <utils/threads.h> #include <private/utils/Static.h> -#ifdef HAVE_WINSOCK -# undef nhtol -# undef htonl -# undef nhtos -# undef htons - -# ifdef HAVE_LITTLE_ENDIAN -# define ntohl(x) ( ((x) << 24) | (((x) >> 24) & 255) | (((x) << 8) & 0xff0000) | (((x) >> 8) & 0xff00) ) -# define htonl(x) ntohl(x) -# define ntohs(x) ( (((x) << 8) & 0xff00) | (((x) >> 8) & 255) ) -# define htons(x) ntohs(x) -# else -# define ntohl(x) (x) -# define htonl(x) (x) -# define ntohs(x) (x) -# define htons(x) (x) -# endif -#else -# include <netinet/in.h> -#endif - #include <memory.h> #include <stdio.h> #include <ctype.h> -// --------------------------------------------------------------------------- - -int strcmp16(const char16_t *s1, const char16_t *s2) -{ - char16_t ch; - int d = 0; - - while ( 1 ) { - d = (int)(ch = *s1++) - (int)*s2++; - if ( d || !ch ) - break; - } - - return d; -} - -int strncmp16(const char16_t *s1, const char16_t *s2, size_t n) -{ - char16_t ch; - int d = 0; - - while ( n-- ) { - d = (int)(ch = *s1++) - (int)*s2++; - if ( d || !ch ) - break; - } - - return d; -} - -char16_t *strcpy16(char16_t *dst, const char16_t *src) -{ - char16_t *q = dst; - const char16_t *p = src; - char16_t ch; - - do { - *q++ = ch = *p++; - } while ( ch ); - - return dst; -} - -size_t strlen16(const char16_t *s) -{ - const char16_t *ss = s; - while ( *ss ) - ss++; - return ss-s; -} - - -char16_t *strncpy16(char16_t *dst, const char16_t *src, size_t n) -{ - char16_t *q = dst; - const char16_t *p = src; - char ch; - - while (n) { - n--; - *q++ = ch = *p++; - if ( !ch ) - break; - } - - *q = 0; - - return dst; -} - -size_t strnlen16(const char16_t *s, size_t maxlen) -{ - const char16_t *ss = s; - - /* Important: the maxlen test must precede the reference through ss; - since the byte beyond the maximum may segfault */ - while ((maxlen > 0) && *ss) { - ss++; - maxlen--; - } - return ss-s; -} - -int strzcmp16(const char16_t *s1, size_t n1, const char16_t *s2, size_t n2) -{ - const char16_t* e1 = s1+n1; - const char16_t* e2 = s2+n2; - - while (s1 < e1 && s2 < e2) { - const int d = (int)*s1++ - (int)*s2++; - if (d) { - return d; - } - } - - return n1 < n2 - ? (0 - (int)*s2) - : (n1 > n2 - ? ((int)*s1 - 0) - : 0); -} - -int strzcmp16_h_n(const char16_t *s1H, size_t n1, const char16_t *s2N, size_t n2) -{ - const char16_t* e1 = s1H+n1; - const char16_t* e2 = s2N+n2; - - while (s1H < e1 && s2N < e2) { - const char16_t c2 = ntohs(*s2N); - const int d = (int)*s1H++ - (int)c2; - s2N++; - if (d) { - return d; - } - } - - return n1 < n2 - ? (0 - (int)ntohs(*s2N)) - : (n1 > n2 - ? ((int)*s1H - 0) - : 0); -} - -static inline size_t -utf8_char_len(uint8_t ch) -{ - return ((0xe5000000 >> ((ch >> 3) & 0x1e)) & 3) + 1; -} - -#define UTF8_SHIFT_AND_MASK(unicode, byte) (unicode)<<=6; (unicode) |= (0x3f & (byte)); - -static inline uint32_t -utf8_to_utf32(const uint8_t *src, size_t length) -{ - uint32_t unicode; - - switch (length) - { - case 1: - return src[0]; - case 2: - unicode = src[0] & 0x1f; - UTF8_SHIFT_AND_MASK(unicode, src[1]) - return unicode; - case 3: - unicode = src[0] & 0x0f; - UTF8_SHIFT_AND_MASK(unicode, src[1]) - UTF8_SHIFT_AND_MASK(unicode, src[2]) - return unicode; - case 4: - unicode = src[0] & 0x07; - UTF8_SHIFT_AND_MASK(unicode, src[1]) - UTF8_SHIFT_AND_MASK(unicode, src[2]) - UTF8_SHIFT_AND_MASK(unicode, src[3]) - return unicode; - default: - return 0xffff; - } - - //printf("Char at %p: len=%d, utf-16=%p\n", src, length, (void*)result); -} - -void -utf8_to_utf16(const uint8_t *src, size_t srcLen, - char16_t* dst, const size_t dstLen) -{ - const uint8_t* const end = src + srcLen; - const char16_t* const dstEnd = dst + dstLen; - while (src < end && dst < dstEnd) { - size_t len = utf8_char_len(*src); - uint32_t codepoint = utf8_to_utf32((const uint8_t*)src, len); - - // Convert the UTF32 codepoint to one or more UTF16 codepoints - if (codepoint <= 0xFFFF) { - // Single UTF16 character - *dst++ = (char16_t) codepoint; - } else { - // Multiple UTF16 characters with surrogates - codepoint = codepoint - 0x10000; - *dst++ = (char16_t) ((codepoint >> 10) + 0xD800); - *dst++ = (char16_t) ((codepoint & 0x3FF) + 0xDC00); - } - - src += len; - } - if (dst < dstEnd) { - *dst = 0; - } -} - -// --------------------------------------------------------------------------- namespace android { @@ -270,37 +59,33 @@ void terminate_string16() // --------------------------------------------------------------------------- -static char16_t* allocFromUTF8(const char* in, size_t len) +static char16_t* allocFromUTF8(const char* u8str, size_t u8len) { - if (len == 0) return getEmptyString(); - - size_t chars = 0; - const char* end = in+len; - const char* p = in; - - while (p < end) { - chars++; - int utf8len = utf8_char_len(*p); - uint32_t codepoint = utf8_to_utf32((const uint8_t*)p, utf8len); - if (codepoint > 0xFFFF) chars++; // this will be a surrogate pair in utf16 - p += utf8len; + if (u8len == 0) return getEmptyString(); + + const uint8_t* u8cur = (const uint8_t*) u8str; + + const ssize_t u16len = utf8_to_utf16_length(u8cur, u8len); + if (u16len < 0) { + return getEmptyString(); } - - size_t bufSize = (chars+1)*sizeof(char16_t); - SharedBuffer* buf = SharedBuffer::alloc(bufSize); + + const uint8_t* const u8end = u8cur + u8len; + + SharedBuffer* buf = SharedBuffer::alloc(sizeof(char16_t)*(u16len+1)); if (buf) { - p = in; - char16_t* str = (char16_t*)buf->data(); - - utf8_to_utf16((const uint8_t*)p, len, str, bufSize); + u8cur = (const uint8_t*) u8str; + char16_t* u16str = (char16_t*)buf->data(); + + utf8_to_utf16(u8cur, u8len, u16str); //printf("Created UTF-16 string from UTF-8 \"%s\":", in); //printHexData(1, str, buf->size(), 16, 1); //printf("\n"); - return str; + return u16str; } - + return getEmptyString(); } diff --git a/libs/utils/String8.cpp b/libs/utils/String8.cpp index 1c4f80c1f8..0bc5aff221 100644 --- a/libs/utils/String8.cpp +++ b/libs/utils/String8.cpp @@ -17,6 +17,8 @@ #include <utils/String8.h> #include <utils/Log.h> +#include <utils/Unicode.h> +#include <utils/SharedBuffer.h> #include <utils/String16.h> #include <utils/TextOutput.h> #include <utils/threads.h> @@ -34,94 +36,10 @@ namespace android { -static const char32_t kByteMask = 0x000000BF; -static const char32_t kByteMark = 0x00000080; - -// Surrogates aren't valid for UTF-32 characters, so define some -// constants that will let us screen them out. -static const char32_t kUnicodeSurrogateHighStart = 0x0000D800; -static const char32_t kUnicodeSurrogateHighEnd = 0x0000DBFF; -static const char32_t kUnicodeSurrogateLowStart = 0x0000DC00; -static const char32_t kUnicodeSurrogateLowEnd = 0x0000DFFF; -static const char32_t kUnicodeSurrogateStart = kUnicodeSurrogateHighStart; -static const char32_t kUnicodeSurrogateEnd = kUnicodeSurrogateLowEnd; -static const char32_t kUnicodeMaxCodepoint = 0x0010FFFF; - -// Mask used to set appropriate bits in first byte of UTF-8 sequence, -// indexed by number of bytes in the sequence. -// 0xxxxxxx -// -> (00-7f) 7bit. Bit mask for the first byte is 0x00000000 -// 110yyyyx 10xxxxxx -// -> (c0-df)(80-bf) 11bit. Bit mask is 0x000000C0 -// 1110yyyy 10yxxxxx 10xxxxxx -// -> (e0-ef)(80-bf)(80-bf) 16bit. Bit mask is 0x000000E0 -// 11110yyy 10yyxxxx 10xxxxxx 10xxxxxx -// -> (f0-f7)(80-bf)(80-bf)(80-bf) 21bit. Bit mask is 0x000000F0 -static const char32_t kFirstByteMark[] = { - 0x00000000, 0x00000000, 0x000000C0, 0x000000E0, 0x000000F0 -}; - // Separator used by resource paths. This is not platform dependent contrary // to OS_PATH_SEPARATOR. #define RES_PATH_SEPARATOR '/' -// Return number of utf8 bytes required for the character. -static size_t utf32_to_utf8_bytes(char32_t srcChar) -{ - size_t bytesToWrite; - - // Figure out how many bytes the result will require. - if (srcChar < 0x00000080) - { - bytesToWrite = 1; - } - else if (srcChar < 0x00000800) - { - bytesToWrite = 2; - } - else if (srcChar < 0x00010000) - { - if ((srcChar < kUnicodeSurrogateStart) - || (srcChar > kUnicodeSurrogateEnd)) - { - bytesToWrite = 3; - } - else - { - // Surrogates are invalid UTF-32 characters. - return 0; - } - } - // Max code point for Unicode is 0x0010FFFF. - else if (srcChar <= kUnicodeMaxCodepoint) - { - bytesToWrite = 4; - } - else - { - // Invalid UTF-32 character. - return 0; - } - - return bytesToWrite; -} - -// Write out the source character to <dstP>. - -static void utf32_to_utf8(uint8_t* dstP, char32_t srcChar, size_t bytes) -{ - dstP += bytes; - switch (bytes) - { /* note: everything falls through. */ - case 4: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6; - case 3: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6; - case 2: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6; - case 1: *--dstP = (uint8_t)(srcChar | kFirstByteMark[bytes]); - } -} - -// --------------------------------------------------------------------------- - static SharedBuffer* gEmptyStringBuf = NULL; static char* gEmptyString = NULL; @@ -175,62 +93,47 @@ static char* allocFromUTF8(const char* in, size_t len) return getEmptyString(); } -template<typename T, typename L> -static char* allocFromUTF16OrUTF32(const T* in, L len) +static char* allocFromUTF16(const char16_t* in, size_t len) { if (len == 0) return getEmptyString(); - size_t bytes = 0; - const T* end = in+len; - const T* p = in; - - while (p < end) { - bytes += utf32_to_utf8_bytes(*p); - p++; + const ssize_t bytes = utf16_to_utf8_length(in, len); + if (bytes < 0) { + return getEmptyString(); } SharedBuffer* buf = SharedBuffer::alloc(bytes+1); LOG_ASSERT(buf, "Unable to allocate shared buffer"); - if (buf) { - p = in; - char* str = (char*)buf->data(); - char* d = str; - while (p < end) { - const T c = *p++; - size_t len = utf32_to_utf8_bytes(c); - utf32_to_utf8((uint8_t*)d, c, len); - d += len; - } - *d = 0; - - return str; + if (!buf) { + return getEmptyString(); } - return getEmptyString(); + char* str = (char*)buf->data(); + utf16_to_utf8(in, len, str); + return str; } -static char* allocFromUTF16(const char16_t* in, size_t len) +static char* allocFromUTF32(const char32_t* in, size_t len) { - if (len == 0) return getEmptyString(); + if (len == 0) { + return getEmptyString(); + } - const size_t bytes = utf8_length_from_utf16(in, len); + const ssize_t bytes = utf32_to_utf8_length(in, len); + if (bytes < 0) { + return getEmptyString(); + } SharedBuffer* buf = SharedBuffer::alloc(bytes+1); LOG_ASSERT(buf, "Unable to allocate shared buffer"); - if (buf) { - char* str = (char*)buf->data(); - - utf16_to_utf8(in, len, str, bytes+1); - - return str; + if (!buf) { + return getEmptyString(); } - return getEmptyString(); -} + char* str = (char*) buf->data(); + utf32_to_utf8(in, len, str); -static char* allocFromUTF32(const char32_t* in, size_t len) -{ - return allocFromUTF16OrUTF32<char32_t, size_t>(in, len); + return str; } // --------------------------------------------------------------------------- @@ -292,6 +195,29 @@ String8::~String8() SharedBuffer::bufferFromData(mString)->release(); } +String8 String8::format(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + + String8 result(formatV(fmt, args)); + + va_end(args); + return result; +} + +String8 String8::formatV(const char* fmt, va_list args) +{ + String8 result; + result.appendFormatV(fmt, args); + return result; +} + +void String8::clear() { + SharedBuffer::bufferFromData(mString)->release(); + mString = getEmptyString(); +} + void String8::setTo(const String8& other) { SharedBuffer::bufferFromData(other.mString)->acquire(); @@ -374,22 +300,28 @@ status_t String8::append(const char* other, size_t otherLen) status_t String8::appendFormat(const char* fmt, ...) { - va_list ap; - va_start(ap, fmt); + va_list args; + va_start(args, fmt); + + status_t result = appendFormatV(fmt, args); + va_end(args); + return result; +} + +status_t String8::appendFormatV(const char* fmt, va_list args) +{ int result = NO_ERROR; - int n = vsnprintf(NULL, 0, fmt, ap); + int n = vsnprintf(NULL, 0, fmt, args); if (n != 0) { size_t oldLength = length(); char* buf = lockBuffer(oldLength + n); if (buf) { - vsnprintf(buf + oldLength, n + 1, fmt, ap); + vsnprintf(buf + oldLength, n + 1, fmt, args); } else { result = NO_MEMORY; } } - - va_end(ap); return result; } @@ -505,17 +437,17 @@ void String8::toUpper(size_t start, size_t length) size_t String8::getUtf32Length() const { - return utf32_length(mString, length()); + return utf8_to_utf32_length(mString, length()); } int32_t String8::getUtf32At(size_t index, size_t *next_index) const { - return utf32_at(mString, length(), index, next_index); + return utf32_from_utf8_at(mString, length(), index, next_index); } -size_t String8::getUtf32(char32_t* dst, size_t dst_len) const +void String8::getUtf32(char32_t* dst) const { - return utf8_to_utf32(mString, length(), dst, dst_len); + utf8_to_utf32(mString, length(), dst); } TextOutput& operator<<(TextOutput& to, const String8& val) @@ -700,241 +632,3 @@ String8& String8::convertToResPath() } }; // namespace android - -// --------------------------------------------------------------------------- - -size_t strlen32(const char32_t *s) -{ - const char32_t *ss = s; - while ( *ss ) - ss++; - return ss-s; -} - -size_t strnlen32(const char32_t *s, size_t maxlen) -{ - const char32_t *ss = s; - while ((maxlen > 0) && *ss) { - ss++; - maxlen--; - } - return ss-s; -} - -size_t utf8_length(const char *src) -{ - const char *cur = src; - size_t ret = 0; - while (*cur != '\0') { - const char first_char = *cur++; - if ((first_char & 0x80) == 0) { // ASCII - ret += 1; - continue; - } - // (UTF-8's character must not be like 10xxxxxx, - // but 110xxxxx, 1110xxxx, ... or 1111110x) - if ((first_char & 0x40) == 0) { - return 0; - } - - int32_t mask, to_ignore_mask; - size_t num_to_read = 0; - char32_t utf32 = 0; - for (num_to_read = 1, mask = 0x40, to_ignore_mask = 0x80; - num_to_read < 5 && (first_char & mask); - num_to_read++, to_ignore_mask |= mask, mask >>= 1) { - if ((*cur & 0xC0) != 0x80) { // must be 10xxxxxx - return 0; - } - // 0x3F == 00111111 - utf32 = (utf32 << 6) + (*cur++ & 0x3F); - } - // "first_char" must be (110xxxxx - 11110xxx) - if (num_to_read == 5) { - return 0; - } - to_ignore_mask |= mask; - utf32 |= ((~to_ignore_mask) & first_char) << (6 * (num_to_read - 1)); - if (utf32 > android::kUnicodeMaxCodepoint) { - return 0; - } - - ret += num_to_read; - } - return ret; -} - -size_t utf32_length(const char *src, size_t src_len) -{ - if (src == NULL || src_len == 0) { - return 0; - } - size_t ret = 0; - const char* cur; - const char* end; - size_t num_to_skip; - for (cur = src, end = src + src_len, num_to_skip = 1; - cur < end; - cur += num_to_skip, ret++) { - const char first_char = *cur; - num_to_skip = 1; - if ((first_char & 0x80) == 0) { // ASCII - continue; - } - int32_t mask; - - for (mask = 0x40; (first_char & mask); num_to_skip++, mask >>= 1) { - } - } - return ret; -} - -size_t utf8_length_from_utf32(const char32_t *src, size_t src_len) -{ - if (src == NULL || src_len == 0) { - return 0; - } - size_t ret = 0; - const char32_t *end = src + src_len; - while (src < end) { - ret += android::utf32_to_utf8_bytes(*src++); - } - return ret; -} - -size_t utf8_length_from_utf16(const char16_t *src, size_t src_len) -{ - if (src == NULL || src_len == 0) { - return 0; - } - size_t ret = 0; - const char16_t* const end = src + src_len; - while (src < end) { - if ((*src & 0xFC00) == 0xD800 && (src + 1) < end - && (*++src & 0xFC00) == 0xDC00) { - // surrogate pairs are always 4 bytes. - ret += 4; - src++; - } else { - ret += android::utf32_to_utf8_bytes((char32_t) *src++); - } - } - return ret; -} - -static int32_t utf32_at_internal(const char* cur, size_t *num_read) -{ - const char first_char = *cur; - if ((first_char & 0x80) == 0) { // ASCII - *num_read = 1; - return *cur; - } - cur++; - char32_t mask, to_ignore_mask; - size_t num_to_read = 0; - char32_t utf32 = first_char; - for (num_to_read = 1, mask = 0x40, to_ignore_mask = 0xFFFFFF80; - (first_char & mask); - num_to_read++, to_ignore_mask |= mask, mask >>= 1) { - // 0x3F == 00111111 - utf32 = (utf32 << 6) + (*cur++ & 0x3F); - } - to_ignore_mask |= mask; - utf32 &= ~(to_ignore_mask << (6 * (num_to_read - 1))); - - *num_read = num_to_read; - return static_cast<int32_t>(utf32); -} - -int32_t utf32_at(const char *src, size_t src_len, - size_t index, size_t *next_index) -{ - if (index >= src_len) { - return -1; - } - size_t dummy_index; - if (next_index == NULL) { - next_index = &dummy_index; - } - size_t num_read; - int32_t ret = utf32_at_internal(src + index, &num_read); - if (ret >= 0) { - *next_index = index + num_read; - } - - return ret; -} - -size_t utf8_to_utf32(const char* src, size_t src_len, - char32_t* dst, size_t dst_len) -{ - if (src == NULL || src_len == 0 || dst == NULL || dst_len == 0) { - return 0; - } - - const char* cur = src; - const char* end = src + src_len; - char32_t* cur_utf32 = dst; - const char32_t* end_utf32 = dst + dst_len; - while (cur_utf32 < end_utf32 && cur < end) { - size_t num_read; - *cur_utf32++ = - static_cast<char32_t>(utf32_at_internal(cur, &num_read)); - cur += num_read; - } - if (cur_utf32 < end_utf32) { - *cur_utf32 = 0; - } - return static_cast<size_t>(cur_utf32 - dst); -} - -size_t utf32_to_utf8(const char32_t* src, size_t src_len, - char* dst, size_t dst_len) -{ - if (src == NULL || src_len == 0 || dst == NULL || dst_len == 0) { - return 0; - } - const char32_t *cur_utf32 = src; - const char32_t *end_utf32 = src + src_len; - char *cur = dst; - const char *end = dst + dst_len; - while (cur_utf32 < end_utf32 && cur < end) { - size_t len = android::utf32_to_utf8_bytes(*cur_utf32); - android::utf32_to_utf8((uint8_t *)cur, *cur_utf32++, len); - cur += len; - } - if (cur < end) { - *cur = '\0'; - } - return cur - dst; -} - -size_t utf16_to_utf8(const char16_t* src, size_t src_len, - char* dst, size_t dst_len) -{ - if (src == NULL || src_len == 0 || dst == NULL || dst_len == 0) { - return 0; - } - const char16_t* cur_utf16 = src; - const char16_t* const end_utf16 = src + src_len; - char *cur = dst; - const char* const end = dst + dst_len; - while (cur_utf16 < end_utf16 && cur < end) { - char32_t utf32; - // surrogate pairs - if ((*cur_utf16 & 0xFC00) == 0xD800 && (cur_utf16 + 1) < end_utf16) { - utf32 = (*cur_utf16++ - 0xD800) << 10; - utf32 |= *cur_utf16++ - 0xDC00; - utf32 += 0x10000; - } else { - utf32 = (char32_t) *cur_utf16++; - } - size_t len = android::utf32_to_utf8_bytes(utf32); - android::utf32_to_utf8((uint8_t*)cur, utf32, len); - cur += len; - } - if (cur < end) { - *cur = '\0'; - } - return cur - dst; -} diff --git a/libs/utils/Tokenizer.cpp b/libs/utils/Tokenizer.cpp new file mode 100644 index 0000000000..b3445b797f --- /dev/null +++ b/libs/utils/Tokenizer.cpp @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "Tokenizer" + +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <utils/Log.h> +#include <utils/Tokenizer.h> + +// Enables debug output for the tokenizer. +#define DEBUG_TOKENIZER 0 + + +namespace android { + +static inline bool isDelimiter(char ch, const char* delimiters) { + return strchr(delimiters, ch) != NULL; +} + +Tokenizer::Tokenizer(const String8& filename, FileMap* fileMap, char* buffer, size_t length) : + mFilename(filename), mFileMap(fileMap), + mBuffer(buffer), mLength(length), mCurrent(buffer), mLineNumber(1) { +} + +Tokenizer::~Tokenizer() { + if (mFileMap) { + mFileMap->release(); + } else { + delete[] mBuffer; + } +} + +status_t Tokenizer::open(const String8& filename, Tokenizer** outTokenizer) { + *outTokenizer = NULL; + + int result = NO_ERROR; + int fd = ::open(filename.string(), O_RDONLY); + if (fd < 0) { + result = -errno; + LOGE("Error opening file '%s', %s.", filename.string(), strerror(errno)); + } else { + struct stat stat; + if (fstat(fd, &stat)) { + result = -errno; + LOGE("Error getting size of file '%s', %s.", filename.string(), strerror(errno)); + } else { + size_t length = size_t(stat.st_size); + + FileMap* fileMap = new FileMap(); + char* buffer; + if (fileMap->create(NULL, fd, 0, length, true)) { + fileMap->advise(FileMap::SEQUENTIAL); + buffer = static_cast<char*>(fileMap->getDataPtr()); + } else { + fileMap->release(); + fileMap = NULL; + + // Fall back to reading into a buffer since we can't mmap files in sysfs. + // The length we obtained from stat is wrong too (it will always be 4096) + // so we must trust that read will read the entire file. + buffer = new char[length]; + ssize_t nrd = read(fd, buffer, length); + if (nrd < 0) { + result = -errno; + LOGE("Error reading file '%s', %s.", filename.string(), strerror(errno)); + delete[] buffer; + buffer = NULL; + } else { + length = size_t(nrd); + } + } + + if (!result) { + *outTokenizer = new Tokenizer(filename, fileMap, buffer, length); + } + } + close(fd); + } + return result; +} + +String8 Tokenizer::getLocation() const { + String8 result; + result.appendFormat("%s:%d", mFilename.string(), mLineNumber); + return result; +} + +String8 Tokenizer::peekRemainderOfLine() const { + const char* end = getEnd(); + const char* eol = mCurrent; + while (eol != end) { + char ch = *eol; + if (ch == '\n') { + break; + } + eol += 1; + } + return String8(mCurrent, eol - mCurrent); +} + +String8 Tokenizer::nextToken(const char* delimiters) { +#if DEBUG_TOKENIZER + LOGD("nextToken"); +#endif + const char* end = getEnd(); + const char* tokenStart = mCurrent; + while (mCurrent != end) { + char ch = *mCurrent; + if (ch == '\n' || isDelimiter(ch, delimiters)) { + break; + } + mCurrent += 1; + } + return String8(tokenStart, mCurrent - tokenStart); +} + +void Tokenizer::nextLine() { +#if DEBUG_TOKENIZER + LOGD("nextLine"); +#endif + const char* end = getEnd(); + while (mCurrent != end) { + char ch = *(mCurrent++); + if (ch == '\n') { + mLineNumber += 1; + break; + } + } +} + +void Tokenizer::skipDelimiters(const char* delimiters) { +#if DEBUG_TOKENIZER + LOGD("skipDelimiters"); +#endif + const char* end = getEnd(); + while (mCurrent != end) { + char ch = *mCurrent; + if (ch == '\n' || !isDelimiter(ch, delimiters)) { + break; + } + mCurrent += 1; + } +} + +} // namespace android diff --git a/libs/utils/Unicode.cpp b/libs/utils/Unicode.cpp new file mode 100644 index 0000000000..78c61b4fc6 --- /dev/null +++ b/libs/utils/Unicode.cpp @@ -0,0 +1,575 @@ +/* + * Copyright (C) 2005 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <utils/Unicode.h> + +#include <stddef.h> + +#ifdef HAVE_WINSOCK +# undef nhtol +# undef htonl +# undef nhtos +# undef htons + +# ifdef HAVE_LITTLE_ENDIAN +# define ntohl(x) ( ((x) << 24) | (((x) >> 24) & 255) | (((x) << 8) & 0xff0000) | (((x) >> 8) & 0xff00) ) +# define htonl(x) ntohl(x) +# define ntohs(x) ( (((x) << 8) & 0xff00) | (((x) >> 8) & 255) ) +# define htons(x) ntohs(x) +# else +# define ntohl(x) (x) +# define htonl(x) (x) +# define ntohs(x) (x) +# define htons(x) (x) +# endif +#else +# include <netinet/in.h> +#endif + +extern "C" { + +static const char32_t kByteMask = 0x000000BF; +static const char32_t kByteMark = 0x00000080; + +// Surrogates aren't valid for UTF-32 characters, so define some +// constants that will let us screen them out. +static const char32_t kUnicodeSurrogateHighStart = 0x0000D800; +static const char32_t kUnicodeSurrogateHighEnd = 0x0000DBFF; +static const char32_t kUnicodeSurrogateLowStart = 0x0000DC00; +static const char32_t kUnicodeSurrogateLowEnd = 0x0000DFFF; +static const char32_t kUnicodeSurrogateStart = kUnicodeSurrogateHighStart; +static const char32_t kUnicodeSurrogateEnd = kUnicodeSurrogateLowEnd; +static const char32_t kUnicodeMaxCodepoint = 0x0010FFFF; + +// Mask used to set appropriate bits in first byte of UTF-8 sequence, +// indexed by number of bytes in the sequence. +// 0xxxxxxx +// -> (00-7f) 7bit. Bit mask for the first byte is 0x00000000 +// 110yyyyx 10xxxxxx +// -> (c0-df)(80-bf) 11bit. Bit mask is 0x000000C0 +// 1110yyyy 10yxxxxx 10xxxxxx +// -> (e0-ef)(80-bf)(80-bf) 16bit. Bit mask is 0x000000E0 +// 11110yyy 10yyxxxx 10xxxxxx 10xxxxxx +// -> (f0-f7)(80-bf)(80-bf)(80-bf) 21bit. Bit mask is 0x000000F0 +static const char32_t kFirstByteMark[] = { + 0x00000000, 0x00000000, 0x000000C0, 0x000000E0, 0x000000F0 +}; + +// -------------------------------------------------------------------------- +// UTF-32 +// -------------------------------------------------------------------------- + +/** + * Return number of UTF-8 bytes required for the character. If the character + * is invalid, return size of 0. + */ +static inline size_t utf32_codepoint_utf8_length(char32_t srcChar) +{ + // Figure out how many bytes the result will require. + if (srcChar < 0x00000080) { + return 1; + } else if (srcChar < 0x00000800) { + return 2; + } else if (srcChar < 0x00010000) { + if ((srcChar < kUnicodeSurrogateStart) || (srcChar > kUnicodeSurrogateEnd)) { + return 3; + } else { + // Surrogates are invalid UTF-32 characters. + return 0; + } + } + // Max code point for Unicode is 0x0010FFFF. + else if (srcChar <= kUnicodeMaxCodepoint) { + return 4; + } else { + // Invalid UTF-32 character. + return 0; + } +} + +// Write out the source character to <dstP>. + +static inline void utf32_codepoint_to_utf8(uint8_t* dstP, char32_t srcChar, size_t bytes) +{ + dstP += bytes; + switch (bytes) + { /* note: everything falls through. */ + case 4: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6; + case 3: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6; + case 2: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6; + case 1: *--dstP = (uint8_t)(srcChar | kFirstByteMark[bytes]); + } +} + +size_t strlen32(const char32_t *s) +{ + const char32_t *ss = s; + while ( *ss ) + ss++; + return ss-s; +} + +size_t strnlen32(const char32_t *s, size_t maxlen) +{ + const char32_t *ss = s; + while ((maxlen > 0) && *ss) { + ss++; + maxlen--; + } + return ss-s; +} + +static inline int32_t utf32_at_internal(const char* cur, size_t *num_read) +{ + const char first_char = *cur; + if ((first_char & 0x80) == 0) { // ASCII + *num_read = 1; + return *cur; + } + cur++; + char32_t mask, to_ignore_mask; + size_t num_to_read = 0; + char32_t utf32 = first_char; + for (num_to_read = 1, mask = 0x40, to_ignore_mask = 0xFFFFFF80; + (first_char & mask); + num_to_read++, to_ignore_mask |= mask, mask >>= 1) { + // 0x3F == 00111111 + utf32 = (utf32 << 6) + (*cur++ & 0x3F); + } + to_ignore_mask |= mask; + utf32 &= ~(to_ignore_mask << (6 * (num_to_read - 1))); + + *num_read = num_to_read; + return static_cast<int32_t>(utf32); +} + +int32_t utf32_from_utf8_at(const char *src, size_t src_len, size_t index, size_t *next_index) +{ + if (index >= src_len) { + return -1; + } + size_t dummy_index; + if (next_index == NULL) { + next_index = &dummy_index; + } + size_t num_read; + int32_t ret = utf32_at_internal(src + index, &num_read); + if (ret >= 0) { + *next_index = index + num_read; + } + + return ret; +} + +ssize_t utf32_to_utf8_length(const char32_t *src, size_t src_len) +{ + if (src == NULL || src_len == 0) { + return -1; + } + + size_t ret = 0; + const char32_t *end = src + src_len; + while (src < end) { + ret += utf32_codepoint_utf8_length(*src++); + } + return ret; +} + +void utf32_to_utf8(const char32_t* src, size_t src_len, char* dst) +{ + if (src == NULL || src_len == 0 || dst == NULL) { + return; + } + + const char32_t *cur_utf32 = src; + const char32_t *end_utf32 = src + src_len; + char *cur = dst; + while (cur_utf32 < end_utf32) { + size_t len = utf32_codepoint_utf8_length(*cur_utf32); + utf32_codepoint_to_utf8((uint8_t *)cur, *cur_utf32++, len); + cur += len; + } + *cur = '\0'; +} + +// -------------------------------------------------------------------------- +// UTF-16 +// -------------------------------------------------------------------------- + +int strcmp16(const char16_t *s1, const char16_t *s2) +{ + char16_t ch; + int d = 0; + + while ( 1 ) { + d = (int)(ch = *s1++) - (int)*s2++; + if ( d || !ch ) + break; + } + + return d; +} + +int strncmp16(const char16_t *s1, const char16_t *s2, size_t n) +{ + char16_t ch; + int d = 0; + + while ( n-- ) { + d = (int)(ch = *s1++) - (int)*s2++; + if ( d || !ch ) + break; + } + + return d; +} + +char16_t *strcpy16(char16_t *dst, const char16_t *src) +{ + char16_t *q = dst; + const char16_t *p = src; + char16_t ch; + + do { + *q++ = ch = *p++; + } while ( ch ); + + return dst; +} + +size_t strlen16(const char16_t *s) +{ + const char16_t *ss = s; + while ( *ss ) + ss++; + return ss-s; +} + + +char16_t *strncpy16(char16_t *dst, const char16_t *src, size_t n) +{ + char16_t *q = dst; + const char16_t *p = src; + char ch; + + while (n) { + n--; + *q++ = ch = *p++; + if ( !ch ) + break; + } + + *q = 0; + + return dst; +} + +size_t strnlen16(const char16_t *s, size_t maxlen) +{ + const char16_t *ss = s; + + /* Important: the maxlen test must precede the reference through ss; + since the byte beyond the maximum may segfault */ + while ((maxlen > 0) && *ss) { + ss++; + maxlen--; + } + return ss-s; +} + +int strzcmp16(const char16_t *s1, size_t n1, const char16_t *s2, size_t n2) +{ + const char16_t* e1 = s1+n1; + const char16_t* e2 = s2+n2; + + while (s1 < e1 && s2 < e2) { + const int d = (int)*s1++ - (int)*s2++; + if (d) { + return d; + } + } + + return n1 < n2 + ? (0 - (int)*s2) + : (n1 > n2 + ? ((int)*s1 - 0) + : 0); +} + +int strzcmp16_h_n(const char16_t *s1H, size_t n1, const char16_t *s2N, size_t n2) +{ + const char16_t* e1 = s1H+n1; + const char16_t* e2 = s2N+n2; + + while (s1H < e1 && s2N < e2) { + const char16_t c2 = ntohs(*s2N); + const int d = (int)*s1H++ - (int)c2; + s2N++; + if (d) { + return d; + } + } + + return n1 < n2 + ? (0 - (int)ntohs(*s2N)) + : (n1 > n2 + ? ((int)*s1H - 0) + : 0); +} + +void utf16_to_utf8(const char16_t* src, size_t src_len, char* dst) +{ + if (src == NULL || src_len == 0 || dst == NULL) { + return; + } + + const char16_t* cur_utf16 = src; + const char16_t* const end_utf16 = src + src_len; + char *cur = dst; + while (cur_utf16 < end_utf16) { + char32_t utf32; + // surrogate pairs + if ((*cur_utf16 & 0xFC00) == 0xD800) { + utf32 = (*cur_utf16++ - 0xD800) << 10; + utf32 |= *cur_utf16++ - 0xDC00; + utf32 += 0x10000; + } else { + utf32 = (char32_t) *cur_utf16++; + } + const size_t len = utf32_codepoint_utf8_length(utf32); + utf32_codepoint_to_utf8((uint8_t*)cur, utf32, len); + cur += len; + } + *cur = '\0'; +} + +// -------------------------------------------------------------------------- +// UTF-8 +// -------------------------------------------------------------------------- + +ssize_t utf8_length(const char *src) +{ + const char *cur = src; + size_t ret = 0; + while (*cur != '\0') { + const char first_char = *cur++; + if ((first_char & 0x80) == 0) { // ASCII + ret += 1; + continue; + } + // (UTF-8's character must not be like 10xxxxxx, + // but 110xxxxx, 1110xxxx, ... or 1111110x) + if ((first_char & 0x40) == 0) { + return -1; + } + + int32_t mask, to_ignore_mask; + size_t num_to_read = 0; + char32_t utf32 = 0; + for (num_to_read = 1, mask = 0x40, to_ignore_mask = 0x80; + num_to_read < 5 && (first_char & mask); + num_to_read++, to_ignore_mask |= mask, mask >>= 1) { + if ((*cur & 0xC0) != 0x80) { // must be 10xxxxxx + return -1; + } + // 0x3F == 00111111 + utf32 = (utf32 << 6) + (*cur++ & 0x3F); + } + // "first_char" must be (110xxxxx - 11110xxx) + if (num_to_read == 5) { + return -1; + } + to_ignore_mask |= mask; + utf32 |= ((~to_ignore_mask) & first_char) << (6 * (num_to_read - 1)); + if (utf32 > kUnicodeMaxCodepoint) { + return -1; + } + + ret += num_to_read; + } + return ret; +} + +ssize_t utf16_to_utf8_length(const char16_t *src, size_t src_len) +{ + if (src == NULL || src_len == 0) { + return -1; + } + + size_t ret = 0; + const char16_t* const end = src + src_len; + while (src < end) { + if ((*src & 0xFC00) == 0xD800 && (src + 1) < end + && (*++src & 0xFC00) == 0xDC00) { + // surrogate pairs are always 4 bytes. + ret += 4; + src++; + } else { + ret += utf32_codepoint_utf8_length((char32_t) *src++); + } + } + return ret; +} + +/** + * Returns 1-4 based on the number of leading bits. + * + * 1111 -> 4 + * 1110 -> 3 + * 110x -> 2 + * 10xx -> 1 + * 0xxx -> 1 + */ +static inline size_t utf8_codepoint_len(uint8_t ch) +{ + return ((0xe5000000 >> ((ch >> 3) & 0x1e)) & 3) + 1; +} + +static inline void utf8_shift_and_mask(uint32_t* codePoint, const uint8_t byte) +{ + *codePoint <<= 6; + *codePoint |= 0x3F & byte; +} + +size_t utf8_to_utf32_length(const char *src, size_t src_len) +{ + if (src == NULL || src_len == 0) { + return 0; + } + size_t ret = 0; + const char* cur; + const char* end; + size_t num_to_skip; + for (cur = src, end = src + src_len, num_to_skip = 1; + cur < end; + cur += num_to_skip, ret++) { + const char first_char = *cur; + num_to_skip = 1; + if ((first_char & 0x80) == 0) { // ASCII + continue; + } + int32_t mask; + + for (mask = 0x40; (first_char & mask); num_to_skip++, mask >>= 1) { + } + } + return ret; +} + +void utf8_to_utf32(const char* src, size_t src_len, char32_t* dst) +{ + if (src == NULL || src_len == 0 || dst == NULL) { + return; + } + + const char* cur = src; + const char* const end = src + src_len; + char32_t* cur_utf32 = dst; + while (cur < end) { + size_t num_read; + *cur_utf32++ = static_cast<char32_t>(utf32_at_internal(cur, &num_read)); + cur += num_read; + } + *cur_utf32 = 0; +} + +static inline uint32_t utf8_to_utf32_codepoint(const uint8_t *src, size_t length) +{ + uint32_t unicode; + + switch (length) + { + case 1: + return src[0]; + case 2: + unicode = src[0] & 0x1f; + utf8_shift_and_mask(&unicode, src[1]); + return unicode; + case 3: + unicode = src[0] & 0x0f; + utf8_shift_and_mask(&unicode, src[1]); + utf8_shift_and_mask(&unicode, src[2]); + return unicode; + case 4: + unicode = src[0] & 0x07; + utf8_shift_and_mask(&unicode, src[1]); + utf8_shift_and_mask(&unicode, src[2]); + utf8_shift_and_mask(&unicode, src[3]); + return unicode; + default: + return 0xffff; + } + + //printf("Char at %p: len=%d, utf-16=%p\n", src, length, (void*)result); +} + +ssize_t utf8_to_utf16_length(const uint8_t* u8str, size_t u8len) +{ + const uint8_t* const u8end = u8str + u8len; + const uint8_t* u8cur = u8str; + + /* Validate that the UTF-8 is the correct len */ + size_t u16measuredLen = 0; + while (u8cur < u8end) { + u16measuredLen++; + int u8charLen = utf8_codepoint_len(*u8cur); + uint32_t codepoint = utf8_to_utf32_codepoint(u8cur, u8charLen); + if (codepoint > 0xFFFF) u16measuredLen++; // this will be a surrogate pair in utf16 + u8cur += u8charLen; + } + + /** + * Make sure that we ended where we thought we would and the output UTF-16 + * will be exactly how long we were told it would be. + */ + if (u8cur != u8end) { + return -1; + } + + return u16measuredLen; +} + +/** + * Convert a UTF-8 string to UTF-16. The destination UTF-16 buffer must have + * space for NULL at the end. + */ +void utf8_to_utf16(const uint8_t* u8str, size_t u8len, char16_t* u16str) +{ + const uint8_t* const u8end = u8str + u8len; + const uint8_t* u8cur = u8str; + char16_t* u16cur = u16str; + + while (u8cur < u8end) { + size_t u8len = utf8_codepoint_len(*u8cur); + uint32_t codepoint = utf8_to_utf32_codepoint(u8cur, u8len); + + // Convert the UTF32 codepoint to one or more UTF16 codepoints + if (codepoint <= 0xFFFF) { + // Single UTF16 character + *u16cur++ = (char16_t) codepoint; + } else { + // Multiple UTF16 characters with surrogates + codepoint = codepoint - 0x10000; + *u16cur++ = (char16_t) ((codepoint >> 10) + 0xD800); + *u16cur++ = (char16_t) ((codepoint & 0x3FF) + 0xDC00); + } + + u8cur += u8len; + } + *u16cur = 0; +} + +} diff --git a/libs/utils/ZipFileCRO.cpp b/libs/utils/ZipFileCRO.cpp index 16b219cad4..55dfd9f873 100644 --- a/libs/utils/ZipFileCRO.cpp +++ b/libs/utils/ZipFileCRO.cpp @@ -40,7 +40,7 @@ ZipEntryCRO ZipFileCRO_findEntryByName(ZipFileCRO zipToken, bool ZipFileCRO_getEntryInfo(ZipFileCRO zipToken, ZipEntryRO entryToken, int* pMethod, size_t* pUncompLen, - size_t* pCompLen, off_t* pOffset, long* pModWhen, long* pCrc32) { + size_t* pCompLen, off64_t* pOffset, long* pModWhen, long* pCrc32) { ZipFileRO* zip = (ZipFileRO*)zipToken; ZipEntryRO entry = (ZipEntryRO)entryToken; return zip->getEntryInfo(entry, pMethod, pUncompLen, pCompLen, pOffset, diff --git a/libs/utils/ZipFileRO.cpp b/libs/utils/ZipFileRO.cpp index 42611964b9..b18c383aee 100644 --- a/libs/utils/ZipFileRO.cpp +++ b/libs/utils/ZipFileRO.cpp @@ -146,7 +146,7 @@ status_t ZipFileRO::open(const char* zipFileName) return NAME_NOT_FOUND; } - mFileLength = lseek(fd, 0, SEEK_END); + mFileLength = lseek64(fd, 0, SEEK_END); if (mFileLength < kEOCDLen) { TEMP_FAILURE_RETRY(close(fd)); return UNKNOWN_ERROR; @@ -202,7 +202,7 @@ bool ZipFileRO::mapCentralDirectory(void) /* * Make sure this is a Zip archive. */ - if (lseek(mFd, 0, SEEK_SET) != 0) { + if (lseek64(mFd, 0, SEEK_SET) != 0) { LOGW("seek to start failed: %s", strerror(errno)); free(scanBuf); return false; @@ -240,9 +240,9 @@ bool ZipFileRO::mapCentralDirectory(void) * * We start by pulling in the last part of the file. */ - off_t searchStart = mFileLength - readAmount; + off64_t searchStart = mFileLength - readAmount; - if (lseek(mFd, searchStart, SEEK_SET) != searchStart) { + if (lseek64(mFd, searchStart, SEEK_SET) != searchStart) { LOGW("seek %ld failed: %s\n", (long) searchStart, strerror(errno)); free(scanBuf); return false; @@ -274,7 +274,7 @@ bool ZipFileRO::mapCentralDirectory(void) return false; } - off_t eocdOffset = searchStart + i; + off64_t eocdOffset = searchStart + i; const unsigned char* eocdPtr = scanBuf + i; assert(eocdOffset < mFileLength); @@ -473,7 +473,7 @@ ZipEntryRO ZipFileRO::findEntryByIndex(int idx) const * appear to be bogus. */ bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen, - size_t* pCompLen, off_t* pOffset, long* pModWhen, long* pCrc32) const + size_t* pCompLen, off64_t* pOffset, long* pModWhen, long* pCrc32) const { bool ret = false; @@ -489,7 +489,7 @@ bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen, * so we can just subtract back from that. */ const unsigned char* ptr = (const unsigned char*) hashEntry.name; - off_t cdOffset = mDirectoryOffset; + off64_t cdOffset = mDirectoryOffset; ptr -= kCDELen; @@ -536,12 +536,12 @@ bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen, #ifdef HAVE_PREAD /* * This file descriptor might be from zygote's preloaded assets, - * so we need to do an pread() instead of a lseek() + read() to + * so we need to do an pread64() instead of a lseek64() + read() to * guarantee atomicity across the processes with the shared file * descriptors. */ ssize_t actual = - TEMP_FAILURE_RETRY(pread(mFd, lfhBuf, sizeof(lfhBuf), localHdrOffset)); + TEMP_FAILURE_RETRY(pread64(mFd, lfhBuf, sizeof(lfhBuf), localHdrOffset)); if (actual != sizeof(lfhBuf)) { LOGW("failed reading lfh from offset %ld\n", localHdrOffset); @@ -556,17 +556,17 @@ bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen, } #else /* HAVE_PREAD */ /* - * For hosts don't have pread() we cannot guarantee atomic reads from + * For hosts don't have pread64() we cannot guarantee atomic reads from * an offset in a file. Android should never run on those platforms. * File descriptors inherited from a fork() share file offsets and * there would be nothing to protect from two different processes - * calling lseek() concurrently. + * calling lseek64() concurrently. */ { AutoMutex _l(mFdLock); - if (lseek(mFd, localHdrOffset, SEEK_SET) != localHdrOffset) { + if (lseek64(mFd, localHdrOffset, SEEK_SET) != localHdrOffset) { LOGW("failed seeking to lfh at offset %ld\n", localHdrOffset); return false; } @@ -579,7 +579,7 @@ bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen, } if (get4LE(lfhBuf) != kLFHSignature) { - off_t actualOffset = lseek(mFd, 0, SEEK_CUR); + off64_t actualOffset = lseek64(mFd, 0, SEEK_CUR); LOGW("didn't find signature at start of lfh; wanted: offset=%ld data=0x%08x; " "got: offset=" ZD " data=0x%08lx\n", localHdrOffset, kLFHSignature, (ZD_TYPE) actualOffset, get4LE(lfhBuf)); @@ -588,7 +588,7 @@ bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen, } #endif /* HAVE_PREAD */ - off_t dataOffset = localHdrOffset + kLFHLen + off64_t dataOffset = localHdrOffset + kLFHLen + get2LE(lfhBuf + kLFHNameLen) + get2LE(lfhBuf + kLFHExtraLen); if (dataOffset >= cdOffset) { LOGW("bad data offset %ld in zip\n", (long) dataOffset); @@ -596,14 +596,14 @@ bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen, } /* check lengths */ - if ((off_t)(dataOffset + compLen) > cdOffset) { + if ((off64_t)(dataOffset + compLen) > cdOffset) { LOGW("bad compressed length in zip (%ld + " ZD " > %ld)\n", (long) dataOffset, (ZD_TYPE) compLen, (long) cdOffset); return false; } if (method == kCompressStored && - (off_t)(dataOffset + uncompLen) > cdOffset) + (off64_t)(dataOffset + uncompLen) > cdOffset) { LOGE("ERROR: bad uncompressed length in zip (%ld + " ZD " > %ld)\n", (long) dataOffset, (ZD_TYPE) uncompLen, (long) cdOffset); @@ -649,7 +649,7 @@ FileMap* ZipFileRO::createEntryFileMap(ZipEntryRO entry) const FileMap* newMap; size_t compLen; - off_t offset; + off64_t offset; if (!getEntryInfo(entry, NULL, NULL, &compLen, &offset, NULL, NULL)) return NULL; @@ -679,7 +679,7 @@ bool ZipFileRO::uncompressEntry(ZipEntryRO entry, void* buffer) const int method; size_t uncompLen, compLen; - off_t offset; + off64_t offset; const unsigned char* ptr; getEntryInfo(entry, &method, &uncompLen, &compLen, &offset, NULL, NULL); @@ -739,7 +739,7 @@ bool ZipFileRO::uncompressEntry(ZipEntryRO entry, int fd) const int method; size_t uncompLen, compLen; - off_t offset; + off64_t offset; const unsigned char* ptr; getEntryInfo(entry, &method, &uncompLen, &compLen, &offset, NULL, NULL); diff --git a/libs/utils/tests/Android.mk b/libs/utils/tests/Android.mk index 00077eecf3..72d48769a5 100644 --- a/libs/utils/tests/Android.mk +++ b/libs/utils/tests/Android.mk @@ -8,7 +8,8 @@ ifneq ($(TARGET_SIMULATOR),true) test_src_files := \ ObbFile_test.cpp \ Looper_test.cpp \ - String8_test.cpp + String8_test.cpp \ + Unicode_test.cpp shared_libraries := \ libz \ diff --git a/libs/utils/tests/Unicode_test.cpp b/libs/utils/tests/Unicode_test.cpp new file mode 100644 index 0000000000..18c130c554 --- /dev/null +++ b/libs/utils/tests/Unicode_test.cpp @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "Unicode_test" +#include <utils/Log.h> +#include <utils/Unicode.h> + +#include <gtest/gtest.h> + +namespace android { + +class UnicodeTest : public testing::Test { +protected: + virtual void SetUp() { + } + + virtual void TearDown() { + } +}; + +TEST_F(UnicodeTest, UTF8toUTF16ZeroLength) { + ssize_t measured; + + const uint8_t str[] = { }; + + measured = utf8_to_utf16_length(str, 0); + EXPECT_EQ(0, measured) + << "Zero length input should return zero length output."; +} + +TEST_F(UnicodeTest, UTF8toUTF16ASCIILength) { + ssize_t measured; + + // U+0030 or ASCII '0' + const uint8_t str[] = { 0x30 }; + + measured = utf8_to_utf16_length(str, sizeof(str)); + EXPECT_EQ(1, measured) + << "ASCII glyphs should have a length of 1 char16_t"; +} + +TEST_F(UnicodeTest, UTF8toUTF16Plane1Length) { + ssize_t measured; + + // U+2323 SMILE + const uint8_t str[] = { 0xE2, 0x8C, 0xA3 }; + + measured = utf8_to_utf16_length(str, sizeof(str)); + EXPECT_EQ(1, measured) + << "Plane 1 glyphs should have a length of 1 char16_t"; +} + +TEST_F(UnicodeTest, UTF8toUTF16SurrogateLength) { + ssize_t measured; + + // U+10000 + const uint8_t str[] = { 0xF0, 0x90, 0x80, 0x80 }; + + measured = utf8_to_utf16_length(str, sizeof(str)); + EXPECT_EQ(2, measured) + << "Surrogate pairs should have a length of 2 char16_t"; +} + +TEST_F(UnicodeTest, UTF8toUTF16TruncatedUTF8) { + ssize_t measured; + + // Truncated U+2323 SMILE + // U+2323 SMILE + const uint8_t str[] = { 0xE2, 0x8C }; + + measured = utf8_to_utf16_length(str, sizeof(str)); + EXPECT_EQ(-1, measured) + << "Truncated UTF-8 should return -1 to indicate invalid"; +} + +TEST_F(UnicodeTest, UTF8toUTF16Normal) { + const uint8_t str[] = { + 0x30, // U+0030, 1 UTF-16 character + 0xC4, 0x80, // U+0100, 1 UTF-16 character + 0xE2, 0x8C, 0xA3, // U+2323, 1 UTF-16 character + 0xF0, 0x90, 0x80, 0x80, // U+10000, 2 UTF-16 character + }; + + char16_t output[1 + 1 + 1 + 2 + 1]; // Room for NULL + + utf8_to_utf16(str, sizeof(str), output); + + EXPECT_EQ(0x0030, output[0]) + << "should be U+0030"; + EXPECT_EQ(0x0100, output[1]) + << "should be U+0100"; + EXPECT_EQ(0x2323, output[2]) + << "should be U+2323"; + EXPECT_EQ(0xD800, output[3]) + << "should be first half of surrogate U+10000"; + EXPECT_EQ(0xDC00, output[4]) + << "should be second half of surrogate U+10000"; + EXPECT_EQ(NULL, output[5]) + << "should be NULL terminated"; +} + +} |