Android4.0 input touch解析

Android4.0 input touch解析 1

  (2013-09-26 12:47:48)
  分类: androidinput设备

Android4.0 input touch解析

前言

在网上看到好多关于android input device流程分析,但是都不全,有的只是从linux内核那边分析,有的从android上层分析,而且分析的代码也比较老,都是在android2.3以下,最近在做android4.0下的多点触摸以及校准程序,多点触摸的驱动很好写,在linux内核里面都有现成的例子,照着改就可以了。但是android下的校准程序比较复杂,一种是在android

Framework层进行,一种是在linux 内核层进行。

对于校准程序来说,需要全屏校准。但是在android4.0下面,下面的导航栏是systemui画的,无法去掉,因此在校准程序里面通过display得到分辨率高度比实际的小,差的那部分就是导航栏的高度。如果以小的高度进行校准,但使用实际的高度进行触摸坐标到屏幕坐标转换,就会导致触摸点偏下的问题。

为了解决这个问题,在网上找了很多资料,第一种就是想办法在校准程序里面得到整个屏幕的分辨率,进而让校准程序全屏显示,即把导航栏隐藏,在网上看到又网友用下面例子实现:

//for phone

getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);

//for pad View.SYSTEM_UI_FLAG_SHOW_FULLSCREEN= 4

getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_SHOW_FULLSCREEN);

经过自己实验,这两个都无法隐藏下面的导航栏,而且在最新的sdk里面也没有SYSTEM_UI_FLAG_SHOW_FULLSCREEN的定义。第二种就是在jni种通过fb0得到系统的分辨率,这个是真实的分辨率,这种方法需要apkroot或者graphics组权限,才能打开fb0,而且android4.0根据触摸屏类型是否使用外部显示分辨率,如果使用外部display的话,那么就不能用fb0的分辨率。为了解决这个问题,把整个input touch流程都看了一边。废话少说,进入正题。

android input touch流程

Android inout touch流程分两部分,一部分是从android framework开始,如何读取touch设备的事件并分发。一部分是从linux 内核开始,如何从触摸屏读取触摸坐标并送给touch设备。

android framework

2.1 文件结构

首先看看Event Input文件结构吧,在frameworks/base/services/input之下

图:

2.2 模块介绍

Eventhub

它是系统中所有事件的中央处理站。它管理所有系统中可以识别的输入设备的输入事件,此外,当设备增加或删除时,EventHub将产生相应的输入事件给系统。EventHub通过getEvents函数,给系统提供一个输入事件流。它也支持查询输入设备当前的状态(如哪些键当前被按下)。而且EventHub还跟踪每个输入调入的能力,比如输入设备的类别,输入设备支持哪些按键。

InputReader

InputReader从EventHub中读取原始事件数据(RawEvent),并由各个InputMapper处理之后输入对应的input listener.InputReader拥有一个InputMapper集合。它做的大部分工作在InputReader线程中完成,但是InputReader可以接受任意线程的查询。为了可管理性,InputReader使用一个简单的Mutex来保护它的状态。InputReader拥有一个EventHub对象,但这个对象不是它创建的,而是在创建InputReader时作为参数传入的。

InputDispatcher

InputDispatcher负责把事件分发给输入目标,其中的一些功能(如识别输入目标)由独立的policy对象控制。

InputManager

InputManager是系统事件处理的核心,它虽然不做具体的事,但管理工作还是要做的,比如接受我们客户的投诉和索赔要求,或者老板的出筒。

InputManager使用两个线程:

 1)InputReaderThread叫做"InputReader"线程,它负责读取并预处理RawEvent,applies policy并且把消息送入DispatcherThead管理的队列中。

2)InputDispatcherThread叫做"InputDispatcher"线程,它在队列上等待新的输入事件,并且异步地把这些事件分发给应用程序。

InputReaderThread类与InputDispatcherThread类不共享内部状态,所有的通信都是单向的,从InputReaderThread到InputDispatcherThread。两个类可以通过InputDispatchPolicy进行交互。

InputManager类从不与Java交互,而InputDispatchPolicy负责执行所有与系统的外部交互,包括调用DVM业务。

看看下图理解input下面几个模块的关系

图:

2.3线程创建

SystemServer大家熟悉吧,它是android init进程启动的,它的任务就是启动android里面很多服务,并管理起来,如果大家不熟悉,请参考andorid启动流程分析

SystemServer.java (frameworks\base\services\java\com\android\server)里面

ServerThread::run调用

    Slog.i(TAG, "Window Manager");

            wm WindowManagerService.main(context, power,

                    factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL,

                    !firstBoot);

            ServiceManager.addService(Context.WINDOW_SERVICE, wm);

 

WindowManagerService.java (frameworks\base\services\java\com\android\server\wm)里面

WindowManagerService main调用

WMThread thr new WMThread(context, pm, haveInputMethods, allowBootMsgs);

        thr.start();

接着调用

WMThread:: run调用

WindowManagerService new WindowManagerService(mContext, mPM,

                    mHaveInputMethods, mAllowBootMessages);

接着在WindowManagerService里面调用

mInputManager new InputManager(context, this);

至此我们创建了一个javainput设备管理器

 

InputManager.java (frameworks\base\services\java\com\android\server\wm)里面

InputManager调用

       nativeInit(mContext, mCallbacks, looper.getQueue());

从下面开始就进入native空间

 

com_android_server_InputManager.cpp (frameworks\base\services\jni)里面

nativeInit对应android_server_InputManager_nativeInit调用

gNativeInputManager new NativeInputManager(contextObj, callbacksObj, looper);

NativeInputManager里面调用

sp eventHub new EventHub();

    mInputManager new InputManager(eventHub, this, this);

这个函数创建一个EventHub对象,然后把它作为参数来创建InputManager对象。特别注意,InputManager是在C++里,具体在InputManager.cpp里。EventHub类在EventHub.cpp里,这个类和input事件获取有关。

至此我们创建了一个nativeinput设备管理器,具体作用见上面说明

 

首先是去InputManager.cpp (frameworks\base\services\input)文件里面

InputManager::InputManager调用

         mDispatcher new InputDispatcher(dispatcherPolicy);

    mReader new InputReader(eventHub, readerPolicy, mDispatcher);

    initialize();

它创建了InputDispatcher对象,同时也创建了InputReader对象。并分别暂存于mDispatcher和mReader变量中。注意eventHub和mDispatcher都作为参数创建InputReader对象。后面还用initialize来初始化。下面是initialize函数的定义:

void InputManager::initialize() {

    mReaderThread new InputReaderThread(mReader);

    mDispatcherThread new InputDispatcherThread(mDispatcher);

}

它创建两个线程对象,一个是InputReaderThread线程对象,负责input事件的获取;另一个是InputDispatcherThread线程对象,负责input消息的发送。

注:以上两个线程对象都有自己的threadLoop函数,它将在Thread::_threadLoop中被调用,这个Thread::_threadLoop是线程入口函数,线程在Thread::run中被真正地创建

 

InputDispatcher.cpp (frameworks\base\services\input)里面

InputDispatcher::InputDispatcher做一些准备工作

 

InputReader.cpp (frameworks\base\services\input)里面

InputReader::InputReader做一些准备工作

2.4线程启动

在上面讲到在WindowManagerService里面调用

mInputManager new InputManager(context, this);

创建input 管理器

紧接着调用

mInputManager.start();

 

InputManager.java (frameworks\base\services\java\com\android\server\wm)里面

start调用

       Slog.i(TAG, "Starting input manager");

       nativeStart();

从下面开始就进入native空间

 

com_android_server_InputManager.cpp (frameworks\base\services\jni)里面

nativeStart对应android_server_InputManager_nativeStart调用

status_t result gNativeInputManager->getInputManager()->start();

 

InputManager.cpp (frameworks\base\services\input)文件里面

InputManager::start调用

status_t result mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);

result mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);

 

上面两个线程对象是Thread子类,于是继承它的run方法,在Thread::run中,调用createThreadEtc函数,并以Thread::_threadLoop作为入口函数,以上面的mDispatcherThread或mReaderThread作为userdata创建线程,然后会调用threadLoop(),在Thread类中它是虚函数,得由子类来复写

 

因此会调用InputReader.cpp (frameworks\base\services\input)里面的threadLoop InputReaderThread::threadLoop调用

mReader->loopOnce();

mReader就是上面创建的inputreader对象,作为参数传给mReaderThread

 

InputReader::loopOnce调用

count mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);

得到input 输入事件

processEventsLocked

处理input输入事件

 

因此会调用InputDispatcher.cpp (frameworks\base\services\input)里面的threadLoop InputDispatcherThread::threadLoop调用

mDispatcher->dispatchOnce ();

mDispatcher就是上面创建的InputDispatcher对象,作为参数传给mDispatcherThread

 

InputDispatcher::dispatchOnce调用

     dispatchOnceInnerLocked(&nextWakeupTime)

dispatchOnceInnerLocked函数处理input输入消息,mLooper->pollOnce是等待下一次输入事件。

mLooper->pollOnce(timeoutMillis):

这个请看Looper.cpp文件中的Looper::pollOnce()函数。Looper里主要通过linux管道方式实现进程间通信,通过epoll机制实现外界事件请求作出响应。

 

至此整个android input event框架已经运转起来了,好像到现在还没有提到touch,别着急,且看下面的分析

2.5 event初始化

还记得android_server_InputManager_nativeInit里面创建

sp eventHub new EventHub();

 

EventHub.cpp (frameworks\base\services\input)里面

EventHub::EventHub初始化

mOpeningDevices(0)  表示需要打开的设备链表,为NULL

mClosingDevices(0)  表示需要关闭的设备链表,为NULL

mNeedToSendFinishedDeviceScan(false) 表示需要发送设备扫描完成,默认为0

mNeedToReopenDevices(false) 表示需要重新打开设备,默认为0

mNeedToScanDevices(true) 表示需要扫描设备,默认为1

mPendingEventCount(0) 表示需要处理event个数,默认为0

mPendingEventIndex(0) 表示当前需要处理event的索引,默认为0

mPendingINotify(false) 表示需要处理的通知,默认为0

mEpollFd epoll_create(EPOLL_SIZE_HINT);  epoll实例,在EventHub::EventHub中初始化此例,所有输入事件通过epoll_wait来获取

mINotifyFd inotify_init();

int result inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE IN_CREATE);

创建mINotifyFd,用于监控/dev/input目录下删除和创建设备节点的事件

result epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem)

mINotifyFd注册到mEpollFd里面,通过epoll来监听mINotifyFd的变化

result pipe(wakeFds);

mWakeReadPipeFd wakeFds[0];

mWakeWritePipeFd wakeFds[1];

result fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK)

result fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK)

创建唤醒管道,并设置为非阻塞,如果向mWakeWritePipeFd写,那么mWakeReadPipeFd就会有变化

eventItem.data.u32 EPOLL_ID_WAKE;

result epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem);

mWakeReadPipeFd注册到mEpollFd里面,通过epoll来监听mWakeReadPipeFd的变化

 

至此EventHub对象以及构造完成了,mEpollFd监听mINotifyFdmWakeReadPipeFd的变化。

2.6读取事件

在上面2.4节最后我们看到InputReaderThread线程里面会循环调用

InputReader::loopOnce  接着调用

count mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);

这里的mEventHub就是上节实例化的eventhub,我们来看getEvents

 

EventHub.cpp (frameworks\base\services\input)里面

EventHub::getEvents

for (;;) {

        nsecs_t now systemTime(SYSTEM_TIME_MONOTONIC);

进入for循环

        // Reopen input devices if needed.

        if (mNeedToReopenDevices) {

            mNeedToReopenDevices false;

 

            LOGI("Reopening all input devices due to configuration change.");

 

            closeAllDevicesLocked();

            mNeedToScanDevices true;

            break; // return to the caller before we actually rescan

        }

检查mNeedToReopenDevices是否为ture,如果为true,在closeAllDevicesLocked里面关闭所有打开的硬件设备描述符,并把需要删除的设备放在mClosingDevices链表里面,如果这个设备是在mOpeningDevices里面,就忽略跳过,并删除eventhub层的device对象。然后设置mNeedToScanDevicestrue,因为mNeedToReopenDevices默认为false,所以不会执行这段代码

 

// Report any devices that had last been added/removed.

        while (mClosingDevices) {

            Device* device mClosingDevices;

            LOGV("Reporting device closed: id=%d, name=%s\n",

                 device->id, device->path.string());

            mClosingDevices device->next;

            event->when now;

            event->deviceId device->id == mBuiltInKeyboardId device->id;

            event->type DEVICE_REMOVED;

            event += 1;

            delete device;

            mNeedToSendFinishedDeviceScan true;

            if (--capacity == 0) {

                break;

            }

        }

检查mClosingDevices链表是否存在,如果存在,循环把需要删除的设备信息放在event里面,同时设置event typeDEVICE_REMOVED并删除eventhub层的device对象。设置mNeedToSendFinishedDeviceScantrue。每循环一次,capacity1capacity等于0,就退出for循环,表明这次getEvents已经取得256event事件了,返回给inputreader处理。因为一开始mClosingDevices不存在,所以不会执行这段代码,只有上面的closeAllDevicesLocked执行了,才会执行这段代码。

 

if (mNeedToScanDevices) {

            mNeedToScanDevices false;

            scanDevicesLocked();

            mNeedToSendFinishedDeviceScan true;

        }

检查mNeedToScanDevices是否为true,如果为true,就执行设备扫描。在scanDevicesLocked里面,会打开/dev/input目录,并把循环调用openDeviceLocked,在openDeviceLocked里面

int fd open(devicePath, O_RDWR)

打开一个input 设备

// Check to see if the device is on our excluded list

    for (size_t 0; mExcludedDevices.size(); i++) {

        const String8& item mExcludedDevices.itemAt(i);

        if (identifier.name == item) {

            LOGI("ignoring event id %s driver %s\n", devicePath, item.string());

            close(fd);

            return -1;

        }

}

判断这个设备是否已经存在,如果存在,就关闭退出。

下面得到设备一系列信息。

Device* device new Device(fd, deviceId, String8(devicePath), identifier);

创建一个eventhub层的device对象

// Load the configuration file for the device.

    loadConfigurationLocked(device);

得到设备的idc配置文件,这就是为什么android4.0需要idc文件

// Figure out the kinds of events the device reports.

    ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(device->keyBitmask)), device->keyBitmask);

    ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(device->absBitmask)), device->absBitmask);

    ioctl(fd, EVIOCGBIT(EV_REL, sizeof(device->relBitmask)), device->relBitmask);

    ioctl(fd, EVIOCGBIT(EV_SW, sizeof(device->swBitmask)), device->swBitmask);

    ioctl(fd, EVIOCGBIT(EV_LED, sizeof(device->ledBitmask)), device->ledBitmask);

    ioctl(fd, EVIOCGPROP(sizeof(device->propBitmask)), device->propBitmask);

得到设备各种配置,接下设置deviceclass,就设备的类型

// See if this is touch pad.

    // Is this new modern multi-touch driver?

    if (test_bit(ABS_MT_POSITION_X, device->absBitmask)

            && test_bit(ABS_MT_POSITION_Y, device->absBitmask)) {

        // Some joysticks such as the PS3 controller report axes that conflict

        // with the ABS_MT range.  Try to confirm that the device really is

        // touch screen.

        if (test_bit(BTN_TOUCH, device->keyBitmask) || !haveGamepadButtons) {

            device->classes |= INPUT_DEVICE_CLASS_TOUCH INPUT_DEVICE_CLASS_TOUCH_MT;

        }

    // Is this an old style single-touch driver?

    else if (test_bit(BTN_TOUCH, device->keyBitmask)

            && test_bit(ABS_X, device->absBitmask)

            && test_bit(ABS_Y, device->absBitmask)) {

        device->classes |= INPUT_DEVICE_CLASS_TOUCH;

    }

上面就是根据驱动程序里面的设置来判断input device是多点触摸还是单点触摸,现在是不是看到和触摸屏有点关系了

 

// Determine whether the device is external or internal.

    if (isExternalDeviceLocked(device)) {

        device->classes |= INPUT_DEVICE_CLASS_EXTERNAL;

    }

判断是不是外部设备,根据两个条件判断,一是在idc文件里面如果有“device.internal”存在,就是内部设备,否则是外部设备。如果没有这个域存在,根据硬件设备的总线判断,如果是usbbluetooth bus,就是外部设备。这个留着后面有作用。

 

if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, &eventItem))

将设备加入到mEpollFd监控里面

 

device->next mOpeningDevices;

    mOpeningDevices device;

将设备加入需要打开设备链表里面

 

至此,/dev/input/下面所有的设备对于linux层都已经打开,并且都添加到了mEpollFd监控里面,但是android层面的device还没有添加和初始化,只是放在需要打开设备链表里面。接着设置mNeedToSendFinishedDeviceScantrue。因为mNeedToScanDevices初始化为true,因此第一次进入getEvents就会执行这部分代码。

 

while (mOpeningDevices != NULL) {

            Device* device mOpeningDevices;

            LOGV("Reporting device opened: id=%d, name=%s\n",

                 device->id, device->path.string());

            mOpeningDevices device->next;

            event->when now;

            event->deviceId device->id == mBuiltInKeyboardId device->id;

            event->type DEVICE_ADDED;

            event += 1;

            mNeedToSendFinishedDeviceScan true;

            if (--capacity == 0) {

                break;

            }

        }

检查mOpeningDevices链表是否存在,如果存在,循环把需要添加的设备信息放在event里面,同时设置event typeDEVICE_ADDED。设置mNeedToSendFinishedDeviceScantrue。每循环一次,capacity1capacity等于0,就退出for循环,表明这次getEvents已经取得256event事件了,返回给inputreader处理。因为一开始会执行mNeedToScanDevices 代码,只要/dev/input下面有设备节点存在,mOpeningDevices也会存在,所以开始就会执行这段代码。

 

if (mNeedToSendFinishedDeviceScan) {

            mNeedToSendFinishedDeviceScan false;

            event->when now;

            event->type FINISHED_DEVICE_SCAN;

            event += 1;

            if (--capacity == 0) {

                break;

            }

        }

如果mNeedToSendFinishedDeviceScantrue,就把FINISHED_DEVICE_SCAN信息放在event里面,同时capacity1capacity等于0,就退出for循环,表明这次getEvents已经取得256event事件了,返回给inputreader处理。

 

至此mEpollFd监听mINotifyFdmWakeReadPipeFd和/dev/input里面所有设备的变化

 

// Grab the next input event.

        bool deviceChanged false;

        while (mPendingEventIndex mPendingEventCount) {

            const struct epoll_event& eventItem mPendingEventItems[mPendingEventIndex++];

上面这段代码通过mPendingEventIndex  mPendingEventCount关系判断mEpollFd是否监听到了事件发生,如果有事件发声,从mPendingEventItems取出一个事件

 

            if (eventItem.data.u32 == EPOLL_ID_INOTIFY) {

                if (eventItem.events EPOLLIN) {

                    mPendingINotify true;

                else {

                    LOGW("Received unexpected epoll event 0xx for INotify.", eventItem.events);

                }

                continue;

            }

判断是否是EPOLL_ID_INOTIFY事件,即mINotifyFd有没有变化,也就是在有没有设备热拔插发声,如果有设置mPendingINotify为true,继续循环取下一个事件。如果不是继续往下走。

 

            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 0xx for wake read pipe.",

                            eventItem.events);

                }

                continue;

            }

判断是不是EPOLL_ID_WAKE事件,即mWakeReadPipeFd有没有变化,如果有设置awoken为true,继续循环取下一个事件。如果不是EPOLL_ID_WAKE事件,继续往下走,肯定是有设备输入事件发生。

 

            ssize_t deviceIndex mDevices.indexOfKey(eventItem.data.u32);

            if (deviceIndex 0) {

                LOGW("Received unexpected epoll event 0xx for unknown device id %d.",

                        eventItem.events, eventItem.data.u32);

                continue;

            }

得到有事件发生的设备索引,如果deviceIndex 0,有错误发声,继续循环取下一个事件。

 

            Device* device mDevices.valueAt(deviceIndex);

            if (eventItem.events EPOLLIN) {

                int32_t readSize read(device->fd, readBuffer,

                        sizeof(struct input_event) capacity);

                if (readSize == || (readSize && 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 device->id;

 

                    size_t count size_t(readSize) sizeof(struct input_event);

                    for (size_t 0; 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=0xx 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 0xx for device %s.",

                        eventItem.events, device->identifier.name.string());

            }

        }

根据设备索引的设备文件句柄,通过read函数读取input_event事件,读取个数为capacity,根据read返回值readSize除以sizeof(struct input_event)得到实际读取的事件个数,然后循环把input_event赋给event,同时capacity减去读取事件个数,如果capacity等于0,就退出循环,表明这次getEvents已经取得256event事件了,返回给inputreader处理。如果不等于0,判断mPendingEventIndex  mPendingEventCount关系,如果小于继续循环从mPendingEventItems取下一个事件,如果相等,就表示事件已经取完了,执行下面的代码

 

if (mPendingINotify && mPendingEventIndex >= mPendingEventCount) {

            mPendingINotify false;

            readNotifyLocked();

            deviceChanged true;

        }

如果mPendingINotify为true,且mPendingEventIndex >= mPendingEventCount,就表明有设备热拔插事件发生,调用readNotifyLocked()

readNotifyLocked()调用

read(mINotifyFd, event_buf, sizeof(event_buf))

event_buf循环取出inotify_event事件,包括设备节点创建或者删除,以及设备名字

如果是IN_CREATE,就调用openDeviceLocked打开设备,添加打开设备链表。如果是IN_DELETE,就调用closeDeviceByPathLocked关闭设备,添加关闭设备链表。并设置deviceChangedtrue

 

// Report added or removed devices immediately.

        if (deviceChanged) {

            continue;

        }

如果deviceChangedtrue,结束本次循环,从头执行循环,即立即执行设备添加或删除。

 

// Return now if we have collected any events or if we were explicitly awoken.

        if (event != buffer || awoken) {

            break;

        }

如果event != buffer,就表示有event事件发生,或者awoken存在,结束循环,立即返回给inputreader处理event事件。

 

mPendingEventIndex 0;

        int pollResult epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);

 

        

 

调用epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis)之后,读到的epoll_event事件保存在mPendingEventItems,总共的事件数保存在mPendingEventCount

mPendingEventCount size_t(pollResult);

当然,在调用epoll_wait之前,mPendingEventIndex被清0,直正的事件处理在上面的代码中。epoll_event只表明某个设备上有事件,并不包含事件内容,具体事件内容需要通过read来读取

 

// All done, return the number of events we read.

return event buffer;

返回得到的event个数,支持整个getEvents已经执行完成,所有的event事件都保存在inputreader传递的RawEvent里面,看看下面的图,理解数据结构的变化

图:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值