Input调用流程(好文)
原址
先介绍一下每个模块的工作职责:EventHub, InputReader, InputManager...
1 模块功能
1.1 EventHub
它是系统中所有事件的中央处理站。它管理所有系统中可以识别的输入设备的输入事件,此外,当设备增加或删除时,EventHub将产生相应的输入事件给系统。
EventHub通过getEvents函数,给系统提供一个输入事件流。它也支持查询输入设备当前的状态(如哪些键当前被按下)。而且EventHub还跟踪每个输入调入的能力,比如输入设备的类别,输入设备支持哪些按键。
1.2 InputReader
InputReader从EventHub中读取原始事件数据(RawEvent),并由各个InputMapper处理之后输入对应的input listener.
InputReader拥有一个InputMapper集合。它做的大部分工作在InputReader线程中完成,但是InputReader可以接受任意线程的查询。为了可管理性,InputReader使用一个简单的Mutex来保护它的状态。
InputReader拥有一个EventHub对象,但这个对象不是它创建的,而是在创建InputReader时作为参数传入的。
1.3 InputDispatcher
InputDispatcher负责把事件分发给输入目标,其中的一些功能(如识别输入目标)由独立的policy对象控制。
1.4 InputManager
InputManager是系统事件处理的核心,它虽然不做具体的事,但管理工作还是要做的,比如接受我们客户的投诉和索赔要求,或者老板的出所筒。
InputManager使用两个线程:
1)InputReaderThread叫做"InputReader"线程,它负责读取并预处理RawEvent,applies policy并且把消息送入DispatcherThead管理的队列中。
2)InputDispatcherThread叫做"InputDispatcher"线程,它在队列上等待新的输入事件,并且异步地把这些事件分发给应用程序。
InputReaderThread类与InputDispatcherThread类不共享内部状态,所有的通信都是单向的,从InputReaderThread到InputDispatcherThread。两个类可以通过InputDispatchPolicy进行交互。
InputManager类从不与Java交互,而InputDispatchPolicy负责执行所有与系统的外部交互,包括调用DVM业务。
2 创建流程
1)在android_server_InputManager_nativeInit中创建NativeInputManager对象,并保存到gNativeInputManager中;
2)在创建NativeInputManager对象时,它会创建EventHub对象<且创建是其成员mNeedToScanDevices的值为true>,然后把刚创建的EventHub对象作为参数创建InputManager对象;
3)在创建InputManager对象时,创建InputReader对象,然后把它作为参数创建InputReaderThread;创建InputDispatcher对象,然后把它作为参数创建InputDispatcherThread对象;(注:以上两个线程对象都有自己的threadLoop函数,它将在Thread::_threadLoop中被调用,这个Thread::_threadLoop是线程入口函数,线程在Thread::run中被真正地创建)
4.1)创建InputReader对象
4.1.1)把EventHub、readerPolicy<实质为NativeInputManager对象>和创建的InputDispatcher对象作为参数创建InputReader对象:mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
4.1.2)在创建InputReader时, 保存EventHub对象到mEventHub中,并创建QueuedInputListener对象并保存在mQueuedListener中
4.2)创建InputDispatcher对象
4.2.1)把传入的参数dispatcherPolicy<实质为NativeInputManager对象>作为参数创建InputDispatcher对象:mDispatcher = new InputDispatcher(dispatcherPolicy);
4.2.1)在创建InputDispatcher时,创建了一个looper对象:mLooper = new Looper(false);
3 启动流程
1)在android_server_InputManager_nativeStart中调用InputManager::start,代码如下:
result = gNativeInputManager->getInputManager()->start();
2)在InputManager::start中,调用mDispatcherThread->run和mReaderThread->run,代码如下:
result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
3)在上面的Thread::run中,调用createThreadEtc函数,并以Thread::_threadLoop作为入口函数,以上面的mDispatcherThread或mReaderThread作为userdata创建线程
4)至此InputReader线程和InputDispatcher线程都已经工作,详细信息见Thread::_threadLoop,在此函数中它将调用mDispatcherThread或mReaderThread的threadLoop函数来做真正的事
5.1)mReaderThread->threadLoop
bool InputReaderThread::threadLoop() {
mReader->loopOnce();
return true;
}
5.2)mDispatcherThread->threadLoop
bool InputDispatcherThread::threadLoop() {
mDispatcher->dispatchOnce();
return true;
}
EventInput对象关系图
4. 设备操作流程
从EventHub::getEvents读取的事件数据结构如下:
- struct RawEvent {
- nsecs_t when; //事件发生的时间
- int32_t deviceId; //产生此事件的设备,比如发送FINISHED_DEVICE_SCAN,不需要填此项
- int32_t type; //事件类型(如:DEVICE_ADDED,DEVICE_REMOVED,FINISHED_DEVICE_SCAN)
- int32_t scanCode;
- int32_t keyCode;
- int32_t value;
- uint32_t flags;
- };
读取事件时的调用流程为:
Thread::_threadLoop->
InputReaderThread::threadLoop->
InputReader::loopOnce->
EventHub::getEvents->
4.1 打开设备
在EventHub::getEvents中,当mNeedToScanDevices为true时<当创建EventHub对象时,它就为true>,它将从/dev/input目录下查找所有设备,并进行打开,获取其相关属性,最后加入mDevices列表中。
EventHub::scanDevicesLocked->
EventHub::scanDirLocked("/dev/input")->
EventHub::openDeviceLocked
4.1.1 打开事件输入设备
打开事件输入设备,在用户态调用open,则在kernel态中调用evdev_open函数,evdev_open处理流程如下:
1)首先从参数inode中获取在evdev_table中的索引,从而获取对应的evdev对象
2)创建evdev_client对象,创建此对象时同时为其buffer成员分配对应的内存
3)把新创建evdev_client对象添加到client_list链表中
4)把client保存在file的private_data中
5)调用evdev_open_device->input_open_device->input_dev.open函数打开设备。
4.2 读取输入事件
要说EventHub::getEvents如何获取输入事件,不得不先说说它的几个相关的成员变量:
1)mPendingEventCount:调用epoll_wait时的返回值,当然如果没有事件,则其值为0;
2)mPendingEventIndex:当前需要处理的事件索引
- typedef union epoll_data
- {
- void *ptr;
- int fd;
- unsigned int u32;
- unsigned long long u64;
- } epoll_data_t;
- struct epoll_event
- {
- unsigned int events;
- epoll_data_t data;
- };
每个设备被创建(在函数EventHub::openDeviceLocked中)时,都会向epoll注册,代码如下:
- // Register with epoll.
- struct epoll_event eventItem;
- memset(&eventItem, 0, sizeof(eventItem));
- eventItem.events = EPOLLIN;
- eventItem.data.u32 = deviceId;
- if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, &eventItem)) {
- LOGE("Could not add device fd to epoll instance. errno=%d", errno);
- delete device;
- return -1;
- }
4.2.1 查看设备上是否有事件
在调用epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis)之后,读到的epoll_event事件保存在mPendingEventItems,总共的事件数保存在mPendingEventCount,当然,在调用epoll_event之前,mPendingEventIndex被清0,直正的事件处理在下面的代码中。
- // 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 {
- LOGW("Received unexpected epoll event 0x%08x for INotify.", eventItem.events);
- }
- continue;
- }
- if (eventItem.data.u32 == EPOLL_ID_WAKE) {
- if (eventItem.events & EPOLLIN) {
- LOGV("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 {
- LOGW("Received unexpected epoll event 0x%08x for wake read pipe.",
- eventItem.events);
- }
- continue;
- }
- ssize_t deviceIndex = mDevices.indexOfKey(eventItem.data.u32);
- if (deviceIndex < 0) {
- LOGW("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.
- LOGW("could not get event, removed? (fd: %d size: %d bufferSize: %d capacity: %d errno: %d)\n",
- device->fd, readSize, bufferSize, capacity, errno);
- deviceChanged = true;
- closeDeviceLocked(device);
- } else 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 {
- 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++) {
- const struct input_event& iev = readBuffer[i];
- LOGV("%s got: t0=%d, t1=%d, 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);
- #ifdef HAVE_POSIX_CLOCKS
- // Use the time specified in the event instead of the current time
- // so that downstream code can get more accurate estimates of
- // event dispatch latency from the time the event is enqueued onto
- // the evdev client buffer.
- //
- // The event's timestamp fortuitously uses the same monotonic clock
- // time base as the rest of Android. The kernel event device driver
- // (drivers/input/evdev.c) obtains timestamps using ktime_get_ts().
- // The systemTime(SYSTEM_TIME_MONOTONIC) function we use everywhere
- // calls clock_gettime(CLOCK_MONOTONIC) which is implemented as a
- // system call that also queries ktime_get_ts().
- event->when = nsecs_t(iev.time.tv_sec) * 1000000000LL
- + nsecs_t(iev.time.tv_usec) * 1000LL;
- LOGV("event time %lld, now %lld", event->when, now);
- #else
- event->when = now;
- #endif
- event->deviceId = deviceId;
- event->type = iev.type;
- event->scanCode = iev.code;
- event->value = iev.value;
- event->keyCode = AKEYCODE_UNKNOWN;
- event->flags = 0;
- if (iev.type == EV_KEY && device->keyMap.haveKeyLayout()) {
- status_t err = device->keyMap.keyLayoutMap->mapKey(iev.code,
- &event->keyCode, &event->flags);
- LOGV("iev.code=%d keyCode=%d flags=0x%08x err=%d\n",
- iev.code, event->keyCode, event->flags, err);
- }
- event += 1;
- }
- capacity -= count;
- 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 {
- LOGW("Received unexpected epoll event 0x%08x for device %s.",
- eventItem.events, device->identifier.name.string());
- }
- }
4.2.2 读取设备上真正的事件
epoll_wait只是告诉我们Device已经有事件了,让我们去读,真正读取设备输入事件的代码如上,其流程如下:
1)根据eventItem.data.u32获取设备索引,从而获取对应的Device
2)从device->fd中读取input_event事件。read(device->fd, readBuffer, sizeof(struct input_event) * capacity);这些input_event是由各个注册的input_device报告给input子系统的。具体读入流程参见Input Core和evdev基本知识 - Kernel3.0.8
至此,事件已经读取到用户态,哪我们就看看EventHub怎么处理这些事件了。
4.3 处理输入事件
在4.2中,首先通过epoll_wait查看哪些设备有事件,然后通过read从有事件的设备中读取事件,现在事件已经读取到用户态,且数据结构为input_event,保存在EventHub::getEvents的readBuffer中。下面就看看这些事件下一步的东家是谁?
1)首先把input_event的信息填入RawEvent中,其相关代码如下:
- #ifdef HAVE_POSIX_CLOCKS
- // Use the time specified in the event instead of the current time
- // so that downstream code can get more accurate estimates of
- // event dispatch latency from the time the event is enqueued onto
- // the evdev client buffer.
- //
- // The event's timestamp fortuitously uses the same monotonic clock
- // time base as the rest of Android. The kernel event device driver
- // (drivers/input/evdev.c) obtains timestamps using ktime_get_ts().
- // The systemTime(SYSTEM_TIME_MONOTONIC) function we use everywhere
- // calls clock_gettime(CLOCK_MONOTONIC) which is implemented as a
- // system call that also queries ktime_get_ts().
- event->when = nsecs_t(iev.time.tv_sec) * 1000000000LL
- + nsecs_t(iev.time.tv_usec) * 1000LL;
- LOGV("event time %lld, now %lld", event->when, now);
- #else
- event->when = now;
- #endif
- event->deviceId = deviceId;
- event->type = iev.type;
- event->scanCode = iev.code;
- event->value = iev.value;
- event->keyCode = AKEYCODE_UNKNOWN;
- event->flags = 0;
- if (iev.type == EV_KEY && device->keyMap.haveKeyLayout()) {
- status_t err = device->keyMap.keyLayoutMap->mapKey(iev.code,
- &event->keyCode, &event->flags);
- LOGV("iev.code=%d keyCode=%d flags=0x%08x err=%d\n",
- iev.code, event->keyCode, event->flags, err);
- }
2)如果是input_event的类型为EV_KEY,则需要调用device->keyMap.keyLayoutMap->mapKey函数把iput_event.code映射为RawEvent.keyCode。相关数据结构关系如下图所示:
至此,EventHub::getEvents读取事件的任务已经完成,下面看看这些RawEvent的命运如何呢?
4.3.1 InputReader::loopOnce如何处理RawEvent?
为此,先温习一下读取事件时的调用流程为:
Thread::_threadLoop->
InputReaderThread::threadLoop->
InputReader::loopOnce->
EventHub::getEvents->
在InputReader::loopOnce中,当调用EventHub->getEvents获取到RawEvent之后,调用InputReader::processEventsLocked来处理这些事件,然后调用mQueuedListener->flush()把这些队列中的事件发送到Listener。
4.3.1.1 InputReader::processEventsLocked
在InputReader::processEventsLocked主要分两步处理:
1)处理来自于事件驱动设备的事件(processEventsForDeviceLocked)
按照程序执行流程,应该是先有设备,然后才会有设备事件,所以先分析设备增加。 其代码如下:
- <span style="font-size:10px;">void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count)
- {
- for (const RawEvent* rawEvent = rawEvents; count;) {
- int32_t type = rawEvent->type;
- size_t batchSize = 1;
- //处理来自于事件驱动设备的事件
- if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) {
- int32_t deviceId = rawEvent->deviceId;
- while (batchSize < count) {
- if (rawEvent[batchSize].type >= EventHubInterface::FIRST_SYNTHETIC_EVENT
- || rawEvent[batchSize].deviceId != deviceId) {
- break;
- }
- batchSize += 1;
- }
- //处理来自于同一个事件驱动设备的1个或多个事件
- processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
- }
- else
- {
- //处理增加或删除事件驱动设备的事件,在EventHub::getEvents中产生,
- //不是由事件驱动设备产生的。
- switch (rawEvent->type) {
- case EventHubInterface::DEVICE_ADDED:
- addDeviceLocked(rawEvent->when, rawEvent->deviceId);
- break;
- case EventHubInterface::DEVICE_REMOVED:
- removeDeviceLocked(rawEvent->when, rawEvent->deviceId);
- break;
- case EventHubInterface::FINISHED_DEVICE_SCAN:
- handleConfigurationChangedLocked(rawEvent->when);
- break;
- default:
- LOG_ASSERT(false); // can't happen
- break;
- }
- }
- count -= batchSize;
- rawEvent += batchSize;
- }
- }</span>
4.3.1.1.1 设备增加事件处理 addDeviceLocked
它处理其中的EventHubInterface::DEVICE_ADDED, EventHubInterface:: DEVICE_REMOVED和EventHubInterface::FINISHED_DEVICE_SCAN事件,即与Device相关的事件,这些事件是在EventHub::getEvents中产生的,并不是Kernel态的事件输入设备产生的。
下面分析它如何处理EventHubInterface::DEVICE_ADDED事件。查看其它代码,它是调用InputReader::addDeviceLocked(nsecs_t when, int32_t deviceId)来处理此事件。
在InputReader::addDeviceLocked中的调用流程:
1)先根据mContext, deviceId, name, classes创建一个InputDevice对象,它用于表示单个输入设备的状态。其中的classes为对应Device的classes成员,它用于表示设备类型,其定义如下:
- /*
- * Input device classes.
- */
- enum {
- /* The input device is a keyboard or has buttons. */
- INPUT_DEVICE_CLASS_KEYBOARD = 0x00000001,
- /* The input device is an alpha-numeric keyboard (not just a dial pad). */
- INPUT_DEVICE_CLASS_ALPHAKEY = 0x00000002,
- /* The input device is a touchscreen or a touchpad (either single-touch or multi-touch). */
- INPUT_DEVICE_CLASS_TOUCH = 0x00000004,
- /* The input device is a cursor device such as a trackball or mouse. */
- INPUT_DEVICE_CLASS_CURSOR = 0x00000008,
- /* The input device is a multi-touch touchscreen. */
- INPUT_DEVICE_CLASS_TOUCH_MT = 0x00000010,
- /* The input device is a directional pad (implies keyboard, has DPAD keys). */
- INPUT_DEVICE_CLASS_DPAD = 0x00000020,
- /* The input device is a gamepad (implies keyboard, has BUTTON keys). */
- INPUT_DEVICE_CLASS_GAMEPAD = 0x00000040,
- /* The input device has switches. */
- INPUT_DEVICE_CLASS_SWITCH = 0x00000080,
- /* The input device is a joystick (implies gamepad, has joystick absolute axes). */
- INPUT_DEVICE_CLASS_JOYSTICK = 0x00000100,
- /* The input device is external (not built-in). */
- INPUT_DEVICE_CLASS_EXTERNAL = 0x80000000,
- }
创建InputDevice对象之后, 对于多点触摸设备(class为INPUT_DEVICE_CLASS_TOUCH_MT),创建MultiTouchInputMapper对象并增加到InputDevice的mMappers向量列表中。
对于单点触摸设备(class为INPUT_DEVICE_CLASS_TOUCH),创建SingleTouchInputMapper对象并增加到InputDevice的mMappers向量列表中。相关代码如下:
- InputDevice* InputReader::createDeviceLocked(int32_t deviceId,
- const String8& name, uint32_t classes) {
- InputDevice* device = new InputDevice(&mContext, deviceId, name, classes);
- ....
- if (keyboardSource != 0) {
- device->addMapper(new KeyboardInputMapper(device, keyboardSource, keyboardType));
- }
- // Cursor-like devices.
- if (classes & INPUT_DEVICE_CLASS_CURSOR) {
- device->addMapper(new CursorInputMapper(device));
- }
- // Touchscreens and touchpad devices.
- if (classes & INPUT_DEVICE_CLASS_TOUCH_MT) {
- device->addMapper(new MultiTouchInputMapper(device));
- } else if (classes & INPUT_DEVICE_CLASS_TOUCH) {
- device->addMapper(new SingleTouchInputMapper(device));
- }
- // Joystick-like devices.
- if (classes & INPUT_DEVICE_CLASS_JOYSTICK) {
- device->addMapper(new JoystickInputMapper(device));
- }
- return device;
- }
总之,它调用createDeviceLocked创建一个InputDevice设备,并根据class类别创建对应的事件转换器(InputMapper),然后把这些新那建的InputMapper增加到InputDevice::mMappers中。InputMapper关系如下图所示:
2)调用InputDevice::configure配置此InputDevice
3)调用InputDevice::reset重置此InputDevice
4)把新建的InputDevice增加到InputReader::mDevices中。
InputReader::processEventsLocked设备增加、删除处理总结:
它负责处理Device 增加、删除事件。增加事件的流程为:为一个新增的Device创建一个InputDevice,并增加到InputReader::mDevices中;根据新增加设备的class类别,创建对应的消息转换器(InputMapper),然后此消息转换器加入InputDevice::mMappers中。消息转换器负责把读取的RawEvent转换成特定的事件,以供应用程序使用。
EventHub与InputReader各自管理功能:
1)EventHub管理一堆Device,每一个Device与Kernel中一个事件输入设备对应
2)InputReader管理一堆InputDevice,每一个InputDevice与EventHub中的Device对应
3)InputDevice管理一些与之相关的InputMapper,每一个InputMapper与一个特定的应用事件相对应,如:SingleTouchInputMapper。
4.3.1.1.2 事件驱动设备事件处理processEventsForDeviceLocked
下面的分析处理以单点触摸为例,对于单点触摸Touch Down时,它将报告以下事件:
input_report_abs(myInputDev, ABS_X, event->x);
input_report_abs(myInputDev, ABS_Y, event->y);
产生的事件:*type, code, value
EV_ABS,ABS_X,event->x
EV_ABS,ABS_Y,event->y
input_report_key(myInputDev, BTN_TOUCH, 1);
产生的事件:*type, code, value
EV_KEY, BTN_TOUCH, 1
它负责处理来自于同一个设备且在mEventBuffer中连续的多个事件,其函数原型如下:
- void InputReader::processEventsForDeviceLocked(int32_t deviceId,
- const RawEvent* rawEvents, size_t count) {
- 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(rawEvents, count);
- }
它其实很简单,根据输入的deviceId找到对应的InputDevice,然后调用InputDevice::process以对设备输入事件进行处理。InputDevice::process主要源码如下:
- void InputDevice::process(const RawEvent* rawEvents, size_t count) {
- // Process all of the events in order for each mapper.
- // We cannot simply ask each mapper to process them in bulk because mappers may
- // have side-effects that must be interleaved. For example, joystick movement events and
- // gamepad button presses are handled by different mappers but they should be dispatched
- // in the order received.
- size_t numMappers = mMappers.size();
- for (const RawEvent* rawEvent = rawEvents; count--; rawEvent++)
- {
- for (size_t i = 0; i < numMappers; i++) {
- InputMapper* mapper = mMappers[i];
- mapper->process(rawEvent);
- }
- }
- }
从上面的代码中可以看出,在InputDevice::process中,对于传入的每一个RawEvent,依次调用InputDevice中的每一个InputMapper来进行处理。前面提到过,InputDevice包含一组处理对应设备事件InputMapper,现在这些InputMapper开始干活了。
下面以处理一个单点触摸事件设备的事件为例,进行分析,其它的处理流程类似。对于mapper->process需要查看InputReader::createDeviceLocked中创建的具体的InputMapper的process函数。下面就看看SingleTouchInputMapper的process是如何处理的,其代码如下:
- void SingleTouchInputMapper::process(const RawEvent* rawEvent) {
- TouchInputMapper::process(rawEvent);
- mSingleTouchMotionAccumulator.process(rawEvent);
- }
1)TouchInputMapper::process
由此可见,它将首先调用TouchInputMaaper::process处理此事件,其处理代码如下:
- void TouchInputMapper::process(const RawEvent* rawEvent) {
- mCursorButtonAccumulator.process(rawEvent);
- mCursorScrollAccumulator.process(rawEvent);
- mTouchButtonAccumulator.process(rawEvent);
- if (rawEvent->type == EV_SYN && rawEvent->scanCode == SYN_REPORT) {
- sync(rawEvent->when);
- }
- }
1.1) mCursorButtonAccumulator.process(rawEvent)
记录mouse或touch pad按键状态,记录rawEvent->type为EV_KEY,且rawEvent->scanCode为BTN_LEFT、BTN_RIGHT、BTN_MIDDLE、BTN_BACK、BTN_SIDE、BTN_FORWARD、BTN_EXTRA、BTN_TASK的事件。
1.2) mCursorScrollAccumulator.process(rawEvent)
记录cursor scrolling motions,记录rawEvent->type为EV_REL,且rawEvent->scanCode为REL_WHEEL、REL_HWHEEL的事件。
1.3) mTouchButtonAccumulator.process(rawEvent)
记录touch, stylus and tool buttons状态,记录rawEvent->type为EV_KEY,且rawEvent->scanCode为BTN_TOUCH、BTN_STYLUS、BTN_STYLUS2、BTN_TOOL_FINGER、BTN_TOOL_PEN、BTN_TOOL_RUBBER、BTN_TOOL_BRUSH、BTN_TOOL_PENCIL、BTN_TOOL_AIRBRUSH、BTN_TOOL_MOUSE、BTN_TOOL_LENS、BTN_TOOL_DOUBLETAP、BTN_TOOL_TRIPLETAP、BTN_TOOL_QUADTAP的事件。
看到了吧,我们的BTN_TOUCH在这儿被处理了,且其value被保存在mBtnTouch成员变量中。
1.4) sync(rawEvent->when)
处理EV_SYN:SYN_REPORT,我们的EV_SYN就在这儿被处理了,当然它是Touch Down时,所发事件的最后一个事件。这儿才是处理的重点。
TouchInputMapper::sync将调用SingleTouchInputMapper::syncTouch函数。
a)SingleTouchInputMapper::syncTouch
把mCurrentRawPointerData中的ABS_X和ABS_Y的值保存在TouchInputMapper::mCurrentRawPointerData->pointers中。
单点触摸的syncTouch一次处理一个RawEvent,在pointers中只有一个值;而多点触摸的syncTouch一次处理多个RawEvent,在pointers中有多个值,最多16个。
b)TouchInputMapper::cookPointerData
根据TouchInputMapper::mCurrentRawPointerData->pointers中的数据,通过计算,最后生成TouchInputMapper::mCurrentCookedPointerData.pointerCoords,mCurrentCookedPointerData.pointerProperties和mCurrentCookedPointerData.idToIndex的数据。把Raw进行cook,之后生成了cooked数据。
c)TouchInputMapper::dispatchHoverExit
d)TouchInputMapper::dispatchTouches
d.a)它调用dispatchMotion
d.b)在dispatchMotion中,根据cooked数据创建NotifyMotionArg对象,它描述了一个移动事件
d.c)调用TouchInputMapper::getListener()->notifyMotion(&args)
TouchInputMapper::getListener()调用mContext->getListener(),此mContext为InputReader::mContext,所以其getListener()返回的则为InputReader::mQueuedListener,则最后调用QueuedInputListener::notifyMotion
补充1) InputReader::mContext在构造时用自己的指针初始化了mContext,从而mContext::mReader则为此InputReader实例。
补充2) 在InputReader::createDeviceLocked中创建InputDevice时,把自己的mContext作为参数传入,从而把它保存在InputDevice::mContext中;在创建InputMapper时,以InputDevice作为参数,且InputMapper把它保存在mDevice中,然后从把InputDevice中的mContext也保存在InputMapper的mContext中。
d.d)把传递过来的NotifyMotionArg参数复制一份,然后加入QueuedInputListener::mArgsQueue例表中。
e)TouchInputMapper::dispatchHoverEnterAndMove
2)mSingleTouchMotionAccumulator.process
记录ABS相关的值,记录rawEvent->type为EV_ABS,且rawEvent->scanCode为ABS_X、ABS_Y、ABS_PRESSURE、ABS_TOOL_WIDTH、ABS_DISTANCE、ABS_TILT_X、ABS_TILT_Y的事件。我们发的ABS_X和ABS_Y在这儿被处理了。
事件处理相关数据结构如下图所示:
4.3.1.2 InputReader::mQueuedListener->flush()
- void QueuedInputListener::flush() {
- size_t count = mArgsQueue.size();
- for (size_t i = 0; i < count; i++) {
- NotifyArgs* args = mArgsQueue[i];
- args->notify(mInnerListener);
- delete args;
- }
- mArgsQueue.clear();
- }
看到了吧,确实很简单,调用链表中每个NotifyArgs的notify函数,且有一个有意思的参数 mInnerListener,这个参数在前面多次提到过,它是在创建mQueuedListener时提供的,它其实就是InputManager中的mDispatcher,前面一直在InputReader中打转转,现在终于看到InputDispatcher登场了,说明事件很快就可以谢幕了。
再向下看一下吧,这么多类NotifyArgs,为描述方便,下面以NotifyMotionArgs为例,其代码为:
- void NotifyMotionArgs::notify(const sp<InputListenerInterface>& listener) const {
- listener->notifyMotion(this);
- }
下面就看看InputDispatcher(mDispatcher)的notifyMotion函数做了些什么。这个InputDispatcher::notifyMotion(const NotifyMotionArgs* args)可就不简单了。
在InputDispatcher::notifyMotion中,
1)根据NotifyMotionArgs提供的信息,构造一个MotionEvent,再调用mPolicy->filterInputEvent看是否需要丢弃此事件,如果需要丢弃则马上返加。其中mPolicy为NativeInputManager实例,在构造InputDispatcher时提供的参数。
2)对于AMOTION_EVENT_ACTION_UP或AMOTION_EVENT_ACTION_DOWN事件,则直接根据NotifyMotionArgs提供的信息,构造一个MotionEntry。
3)调用InputDispatcher::enqueueInboundEventLocked把新构造的MotionEntry添加到InputDispatcher::mInboundQueue中,并返回是否需要唤醒mLooper<向pipe中写入数据>的标识。
以上操作都是在InputReader线程中完成的,现在应该InputDispatcher线程开始工作了。
4. 4 分发输入事件
InputDispatcherThread主循环如下:
Thread::_threadLoop->
InputDispatcherThread::threadLoop->
mDispatcher->dispatchOnce(InputDispatcher::dispatchOnce)->
dispatchOnceInnerLocked then
mLooper->pollOnce
下面先看看简单的mLooper->pollOnce
4.4.1 mLooper->pollOnce
其功能为等待超时或被pipe唤醒(InputReader线程调用InputDispatcher::notifyMotion时, InputDispatcher::notifyMotion根据情况调用mLooper->wake)。
其调用流程如下:
mLooper->pollOnce(int timeoutMillis)->
Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData)->
4.4.2 dispatchOnceInnerLocked
1)从mInboundQueue从中依次取出EventEntry<MotionEntry的基类>,
2)调用InputDispatcher::dispatchMotionLocked处理此MotionEntry
3)调用InputDispatcher::dispatchEventToCurrentInputTargetsLocked
对于InputDispatcher::mCurrentInputTargets中的每一个InputTarget,并获取对应的Connection,调用InputDispatcher::prepareDispatchCycleLocked,
其相关代码如下:
- <span style="font-size:10px;"> 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
- }
- }</span>
4)InputDispatcher::prepareDispatchCycleLocked
4.1)调用enqueueDispatchEntryLocked创建DispatchEntry对象,并把它增加到Connection::outboundQueue队列中。
4.2)调用activateConnectionLocked把当前Connection增加到InputDispatcher::mActiveConnections链表中
4.3)调用InputDispatcher::startDispatchCycleLocked,接着它调用Connection::inputPublisher.publishMotionEvent来发布事件到ashmem buffer中,调用Connection::inputPublisher.sendDispatchSignal发送一个dispatch信号到InputConsumer通知它有一个新的消息到了,快来消费吧! 关于消费者如何注册和如何消息的流程在下一个专题中再写。本文到此结束!!!
Input调用流程(好文)相关推荐
- java扫描接口_一种扫描接口并生成可调用API接口文档的方法与流程
本发明属于JavaWeb开发技术领域,涉及一种API接口文档的生成方法,尤其是一种扫描接口并生成可调用API接口文档的方法. 背景技术: API(Application Programming Int ...
- 腾讯(表格OCR)API调用流程
目录 1.调用费用: 2.调用流程 1)腾讯云账号进行注册 2)开通文字识别服务 2)1)进入文字识别控制台:https://console.cloud.tencent.com/ocr/overvie ...
- .net core 源码解析-mvc route的注册,激活,调用流程(三)
.net core mvc route的注册,激活,调用流程 mvc的入口是route,当前请求的url匹配到合适的route之后,mvc根据route所指定的controller和action激活c ...
- Android深入源代码分析理解Aidl总体调用流程(雷惊风)
2017年開始上班的第一天.老不想工作了,假期感觉还没開始就已经结束了,唉,时间就是这样,新的一年開始了,尽管非常不想干正事,没办法,必须干起来.由于后边的路还非常长,距离六十岁还非常远. 刚上班也没 ...
- 8.5.3耳麦拔插事件调用流程分析
目录 回顾 input系统方法 回顾 在前面的小节中,我们编写了一个驱动程序,模拟耳机的插拔事件,其可以上报耳机的拔插事件,并且修改了android的源代码,可以根据耳机的拔插事件,在状态栏上现实或者 ...
- RTSP播放器高RTSP兼容推流网页无插件流媒体播放器EasyPlayer-RTSP关于MP4的封装调用流程介绍
EasyPlayer-RTSP播放器是一套RTSP专用的播放器,包括有:Windows(支持IE插件,npapi插件).Android.iOS三个平台,是区别于市面上大部分的通用播放器,EasyPla ...
- Linux ALSA声卡驱动之七:录音(Capture) 调用流程
ALSA声卡驱动: 1.Linux ALSA声卡驱动之一:ALSA架构简介和ASOC架构简介 2.Linux ALSA声卡驱动之二:Platform 3. Linux ALSA声卡驱动之三:Platf ...
- photon Unity RPC 调用流程
本文章由cartzhang编写,转载请注明出处. 所有权利保留. 文章链接:http://blog.csdn.net/cartzhang/article/details/51425225 作者:car ...
- android从应用到驱动之—camera(1)---程序调用流程
一.开篇 写博客还得写开篇介绍,可惜,这个不是我所擅长的.就按我自己的想法写吧. 话说camera模块,从上层到底层一共包含着这么几个部分: 1.apk------java语言 2.camera的ja ...
最新文章
- struts2中 ServletActionContext与ActionContext区别
- iphone个系列尺寸_iPhone 12系列、11系列尺寸对比
- 八个开源的 Spring Boot 学习资源,你值得拥有
- **Java有哪些悲观锁的实现_「Java并发编程」何谓悲观锁与乐观锁,Java编程你会吗...
- c语言交通违章编程代码,C语言程序设计之交通处罚单管理系统 报告(内含代码).doc...
- Java文档阅读笔记-JPA中getOne()和findById的区别
- tcp协议的主要功能是什么_计算机网络 | 一文搞懂什么是TCP/IP协议
- HDOJ2035 人见人爱A^B
- NOTEBOOK随笔
- php搜索文件名,PHP搜索文件且列出文件名的代码参考
- 某大型银行深化系统技术方案之九:核心层之流程数据管理
- c语言的循环代码大全,循环 (C语言代码)
- Python自制“超级马里奥”小游戏
- 基于Hadoop的云计算平台配置
- twilio php 发送短信,如何使用 Twilio 实现语音和短信功能 (PHP) | Microsoft Docs
- Spring Boot使用EasyExcel导入导出Excel
- html文件在Chrome打开中文乱码
- 天龙八部单机修改之解决地图上限报错
- 【English】新征程,我们在路上
- 百度贴吧测试部门实习生电话面试
热门文章
- C++ string类和字符串的访问和拼接操作
- 使用UTL_SMTP包发送邮件
- Screens的开发一
- CMS软件系统架构设计思考
- 开源bot工具Rasa学习---1
- 2016年linux认证,2016年Linux认证考试要点
- 什么是VGA,QVGA,SVGA,XGA?
- oracle add命令详解,oracle job详解
- python语言中、外部模块先导入、再使用_python引入导入自定义模块和外部文件--转载Sumomo的博客...
- mysql 强项_mysql数据目录迁移