由浅入深学习android input系统 - input事件采集(InputReader)
Posted 许佳佳233
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了由浅入深学习android input系统 - input事件采集(InputReader)相关的知识,希望对你有一定的参考价值。
android input系列文章
由浅入深学习android input系统(一) - input事件如何传递到View
由浅入深学习android input系统(二) - input事件如何传递到app进程( InputDispatcher )
由浅入深学习android input系统(三) - InputChannel解析
由浅入深学习android input系统(四) - input事件采集(InputReader)
由浅入深学习android input系统(五) - input系统的启动
概述
前面讲到,系统进程是通过InputDispatcher将事件分发给app进程的。
那么InputDispatcher的事件又是从哪里来的呢?
android是怎么收到硬件层的事件的呢?
本文将对此进行探索。
InputReader
本身的答案就是在InputReader这个类中,InputReader会启动一个线程一直循环处理逻辑,主要做两件事:
- 获取硬件设备的事件
- 将获取到的原始数据进行处理后传递给InputDispatcher
void InputReader::loopOnce()
————————————————省略
size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
————————————————省略
if (count)
processEventsLocked(mEventBuffer, count);
————————————————省略
InputReader获取硬件事件
InputReader获取硬件事件是通过mEventHub的getEvents()方法。
mEventHub监听的是/dev/input目录下的设备节点,对这块内容由兴趣的读者可以自行看下mEventHub源码。
代码逻辑如下:
- 启动一个for循环去获取事件,如果没有事件就会上锁,一直到有事件进来才会继续执行逻辑。
- 如果有设备已经关闭,需要从监听的设备中删除,并且将”设备关闭“作为一个事件记录到event中。
- 如果有新的设备添加进来,需要加入监听设备的链表,并且将”设备添加“作为事件记录到event中。
- 遍历发生的硬件事件,首先找到事件对应的硬件,然后从该硬件读取事件相关的数据存储到event中。
- 最终返回发生事件的个数。(方法外本身就有对event对象的引用,因此不需要将event传出去。)
size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize)
ALOG_ASSERT(bufferSize >= 1);
AutoMutex _l(mLock);
struct input_event readBuffer[bufferSize];
RawEvent* event = buffer;
size_t capacity = bufferSize;
bool awoken = false;
for (;;)
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
// Reopen input devices if needed.
if (mNeedToReopenDevices)
mNeedToReopenDevices = false;
ALOGI("Reopening all input devices due to a configuration change.");
closeAllDevicesLocked();
mNeedToScanDevices = true;
break; // return to the caller before we actually rescan
// Report any devices that had last been added/removed.
while (mClosingDevices)
Device* device = mClosingDevices;
ALOGV("Reporting device closed: id=%d, name=%s\\n",
device->id, device->path.string());
mClosingDevices = device->next;
event->when = now;
event->deviceId = device->id == mBuiltInKeyboardId ? BUILT_IN_KEYBOARD_ID : device->id;
event->type = DEVICE_REMOVED;
event += 1;
delete device;
mNeedToSendFinishedDeviceScan = true;
if (--capacity == 0)
break;
if (mNeedToScanDevices)
mNeedToScanDevices = false;
scanDevicesLocked();
mNeedToSendFinishedDeviceScan = true;
while (mOpeningDevices != NULL)
Device* device = mOpeningDevices;
ALOGV("Reporting device opened: id=%d, name=%s\\n",
device->id, device->path.string());
mOpeningDevices = device->next;
event->when = now;
event->deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
event->type = DEVICE_ADDED;
event += 1;
mNeedToSendFinishedDeviceScan = true;
if (--capacity == 0)
break;
if (mNeedToSendFinishedDeviceScan)
mNeedToSendFinishedDeviceScan = false;
event->when = now;
event->type = FINISHED_DEVICE_SCAN;
event += 1;
if (--capacity == 0)
break;
// Grab the next input event.
bool deviceChanged = false;
while (mPendingEventIndex < mPendingEventCount)
const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];
if (eventItem.data.u32 == EPOLL_ID_INOTIFY)
if (eventItem.events & EPOLLIN)
mPendingINotify = true;
else
ALOGW("Received unexpected epoll event 0x%08x for INotify.", eventItem.events);
continue;
if (eventItem.data.u32 == EPOLL_ID_WAKE)
if (eventItem.events & EPOLLIN)
ALOGV("awoken after wake()");
awoken = true;
char buffer[16];
ssize_t nRead;
do
nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer));
while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer));
else
ALOGW("Received unexpected epoll event 0x%08x for wake read pipe.",
eventItem.events);
continue;
ssize_t deviceIndex = mDevices.indexOfKey(eventItem.data.u32);
if (deviceIndex < 0)
ALOGW("Received unexpected epoll event 0x%08x for unknown device id %d.",
eventItem.events, eventItem.data.u32);
continue;
Device* device = mDevices.valueAt(deviceIndex);
if (eventItem.events & EPOLLIN)
int32_t readSize = read(device->fd, readBuffer,
sizeof(struct input_event) * capacity);
if (readSize == 0 || (readSize < 0 && errno == ENODEV))
// Device was removed before INotify noticed.
ALOGW("could not get event, removed? (fd: %d size: %" PRId32
" bufferSize: %zu capacity: %zu errno: %d)\\n",
device->fd, readSize, bufferSize, capacity, errno);
deviceChanged = true;
closeDeviceLocked(device);
else if (readSize < 0)
if (errno != EAGAIN && errno != EINTR)
ALOGW("could not get event (errno=%d)", errno);
else if ((readSize % sizeof(struct input_event)) != 0)
ALOGE("could not get event (wrong size: %d)", readSize);
else
int32_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
size_t count = size_t(readSize) / sizeof(struct input_event);
for (size_t i = 0; i < count; i++)
struct input_event& iev = readBuffer[i];
ALOGV("%s got: time=%d.%06d, type=%d, code=%d, value=%d",
device->path.string(),
(int) iev.time.tv_sec, (int) iev.time.tv_usec,
iev.type, iev.code, iev.value);
if (iev.type == EV_MSC)
if (iev.code == MSC_ANDROID_TIME_SEC)
device->timestampOverrideSec = iev.value;
continue;
else if (iev.code == MSC_ANDROID_TIME_USEC)
device->timestampOverrideUsec = iev.value;
continue;
if (device->timestampOverrideSec || device->timestampOverrideUsec)
iev.time.tv_sec = device->timestampOverrideSec;
iev.time.tv_usec = device->timestampOverrideUsec;
if (iev.type == EV_SYN && iev.code == SYN_REPORT)
device->timestampOverrideSec = 0;
device->timestampOverrideUsec = 0;
ALOGV("applied override time %d.%06d",
int(iev.time.tv_sec), int(iev.time.tv_usec));
event->when = nsecs_t(iev.time.tv_sec) * 1000000000LL
+ nsecs_t(iev.time.tv_usec) * 1000LL;
ALOGV("event time %" PRId64 ", now %" PRId64, event->when, now);
if (event->when >= now + 10 * 1000000000LL)
// Double-check. Time may have moved on.
nsecs_t time = systemTime(SYSTEM_TIME_MONOTONIC);
if (event->when > time)
ALOGW("An input event from %s has a timestamp that appears to "
"have been generated using the wrong clock source "
"(expected CLOCK_MONOTONIC): "
"event time %" PRId64 ", current time %" PRId64
", call time %" PRId64 ". "
"Using current time instead.",
device->path.string(), event->when, time, now);
event->when = time;
else
ALOGV("Event time is ok but failed the fast path and required "
"an extra call to systemTime: "
"event time %" PRId64 ", current time %" PRId64
", call time %" PRId64 ".",
event->when, time, now);
event->deviceId = deviceId;
event->type = iev.type;
event->code = iev.code;
event->value = iev.value;
event += 1;
capacity -= 1;
if (capacity == 0)
// The result buffer is full. Reset the pending event index
// so we will try to read the device again on the next iteration.
mPendingEventIndex -= 1;
break;
else if (eventItem.events & EPOLLHUP)
ALOGI("Removing device %s due to epoll hang-up event.",
device->identifier.name.string());
deviceChanged = true;
closeDeviceLocked(device);
else
ALOGW("Received unexpected epoll event 0x%08x for device %s.",
eventItem.events, device->identifier.name.string());
if (mPendingINotify && mPendingEventIndex >= mPendingEventCount)
mPendingINotify = false;
readNotifyLocked();
deviceChanged = true;
// Report added or removed devices immediately.
if (deviceChanged)
continue;
// Return now if we have collected any events or if we were explicitly awoken.
if (event != buffer || awoken)
break;
mPendingEventIndex = 0;
mLock.unlock(); // release lock before poll, must be before release_wake_lock
release_wake_lock(WAKE_LOCK_ID);
int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
mLock.lock(); // reacquire lock after poll, must be after acquire_wake_lock
if (pollResult == 0)
// Timed out.
mPendingEventCount = 0;
break;
if (pollResult < 0)
mPendingEventCount = 0;
if (errno != EINTR)
ALOGW("poll failed (errno=%d)\\n", errno);
usleep(100000);
else
// Some events occurred.
mPendingEventCount = size_t(pollResult);
return event - buffer;
InputReader处理硬件事件
硬件的事件有很多种,包括”设备输入“、”设备添加“、”设备删除“等。
”设备输入“中也包括”键盘事件“和”触摸事件“。
我们本文主要探索”设备输入“中的”触摸事件“。
调用的任务栈如下:
- InputReader.loopOnce()
- InputReader.processEventsLocked()
- InputReader.processEventsForDeviceLocked()
- InputDevice.process()
- InputMapper.process()
等同于TouchInputMapper.process() - TouchInputMapper.sync()
- TouchInputMapper.processRawTouches()
- TouchInputMapper.cookAndDispatch()
- TouchInputMapper.dispatchTouches()
- TouchInputMapper.dispatchMotion()
- getListener().notifyMotion()
等同于InputDispatcher.notifyMotion() - InputDispatcher.dispatchOnce()
逻辑大致如下:
- InputReader在接收到事件之后将原始数据交给InputDevice处理。
- 每个InputDevice会有自己对应的处理事件的Mapper,InputDevice会把事件交给InputDevice继续处理,此处探索的是触摸事件,因此是TouchInputMapper。
(InputDevice还有SwitchInputMapper,KeyBoardInputMapper等,分别对应不同的输入方式) - TouchInputMapper中会将原始数据转化成InputDispatcher可以接收的数据后,将数据传给InputDispatcher,然后唤醒InputDispatcher线程。最终会在InputDispatcher的dispatchOnce()中处理。
对InputDispatcher还不了解的读者可以看下笔者前几篇文章:
继续探索
问题:
- InputReader什么时候初始化?
- InputDispatcher什么时候初始化?
有兴趣的读者可以关注下笔者后续的文章,或者自行阅读下相关源码。
以上是关于由浅入深学习android input系统 - input事件采集(InputReader)的主要内容,如果未能解决你的问题,请参考以下文章
由浅入深学习android input系统 - input系统的启动
由浅入深学习android input系统 - input事件采集(InputReader)
由浅入深学习android input系统 - input事件采集(InputReader)
由浅入深学习android input系统 - input事件如何传递到View