zoukankan      html  css  js  c++  java
  • 在 Android 4.1上,分析 input -- android framework 部分 2



    1 android input touch流程

    Android inout touch流程分两部分:


         一部分是从android framework开始,如何读取touch设备的事件并分发。

         另一部分是从linux 内核开始,如何从触摸屏读取触摸坐标并送给touch设备。


    2 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 (frameworksaseservicesjavacomandroidserver)里面

    ServerThread::run调用

                wm = WindowManagerService.main(context, power, factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL, !firstBoot);

                ServiceManager.addService(Context.WINDOW_SERVICE, wm);


    WindowManagerService.java (frameworksaseservicesjavacomandroidserverwm)里面

    WindowManagerService main 调用

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

                        thr.start(); 接着调用

                                 WMThread:: run调用

                                 WindowManagerService s = new WindowManagerService(mContext, mPM, mHaveInputMethods, mAllowBootMessages);

    接着在WindowManagerService里面调用

    mInputManager = new InputManagerService(context, this);

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


    InputManagerService.java (frameworksaseservicesjavacomandroidserverinput)里面

    InputManagerService调用

           nativeInit(this, mContext, mHandler.getLooper().getQueue());

    从下面开始就进入native空间


    com_android_server_input_InputManagerService.cpp (frameworksaseservicesjni)里面

    nativeInit对应 nativeInit调用

              NativeInputManager* im = new NativeInputManager(contextObj, callbacksObj, messageQueue->getLooper());

    NativeInputManager里面调用

    sp<EventHub> eventHub = new EventHub();

             mInputManager = new InputManager(eventHub, this, this);

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


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


    ===================================



    首先是去InputManager.cpp (frameworksaseservicesinput)文件里面

    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消息的发送,这两个类 都是 继承 Thread类。

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

    InputDispatcher.cpp (frameworksaseservicesinput)里面

           InputDispatcher::InputDispatcher做一些准备工作

    InputReader.cpp (frameworksaseservicesinput)里面

           InputReader::InputReader做一些准备工作


    ==================================


    2.4 线程启动


    在上面讲到在WindowManagerService里面调用

     mInputManager = new InputManagerService(context, mInputMonitor);

    创建input 管理器,紧接着调用

    mInputManager.start();

    InputManagerService.java (frameworksaseservicesjavacomandroidserverinput)里面start调用

           nativeStart();


    从下面开始就进入native空间

    com_android_server_input_InputManagerService.cpp (frameworksaseservicesjni)里面

    nativeStart对应nativeStart调用

         status_t result = im->getInputManager()->start();


    InputManager.cpp (frameworksaseservicesinput)文件里面

    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 (frameworksaseservicesinput)里面的threadLoopInputReaderThread::threadLoop调用

    mReader->loopOnce();


    ===================================


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

    InputReader::loopOnce调用

    size_t count =mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);// 得到input 输入事件

    processEventsLocked(mEventBuffer, count);//处理input输入事件


    因此会调用InputDispatcher.cpp (frameworksaseservicesinput)里面的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初始化


    还记得nativeInit里面创建

            sp<EventHub> eventHub = new EventHub();


    EventHub.cpp (frameworksaseservicesinput)里面

    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,用于监控/dev/input/ 目录下 删除 和 创建设备节点的事件

         mINotifyFd = inotify_init();

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


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

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


    int wakeFds[2];

    result = pipe(wakeFds);

    mWakeReadPipeFd = wakeFds[0];

    mWakeWritePipeFd = wakeFds[1];


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

    result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK)

    result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK)


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

    eventItem.data.u32 = EPOLL_ID_WAKE;

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


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



    2.6读取事件


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

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


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

    EventHub.cpp (frameworksaseservicesinput)里面

    EventHub::getEvents


    for (;;) {进入for循环

            nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);


            // Reopen input devices if needed.

            if (mNeedToReopenDevices) {

                mNeedToReopenDevices = false;

                LOGI("Reopening all input devices due to a 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 ",

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

                mClosingDevices = device->next;

                event->when = now;

                event->deviceId = device->id == mBuiltInKeyboardId ? 0 : 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,就执行设备扫描。这个值 初始值 为true;在scanDevicesLocked里面,会调用scanDirLocked 打开/dev/input目录,并循环调用openDeviceLocked;在openDeviceLocked里面


    status_t EventHub::openDeviceLocked(const char *devicePath) {


       //打开一个input 设备

        int fd = open(devicePath, O_RDWR)


        // Check to see if the device is on our excluded list检查这个设备是否在我们的链表上,若是 之前有 打开这个 设备,那么就在;

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

            const String8& item = mExcludedDevices.itemAt(i);

            if (identifier.name == item) {

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

                close(fd);

                return -1;

            }

    }



    // 创建一个eventhub层的device对象

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


    // Load the configuration file for the device.得到设备的idc配置文件,这就是为什么android4.0需要 idc文件

        loadConfigurationLocked(device);


    // 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 a touch pad.

        // Is this a 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

            // a 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,就是外部设备。这个留着后面有作用。


    //将设备加入到mEpollFd监控里面

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


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

    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 ",

                     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;

                }

            }


    检查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 0x%08x 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 0x%08x 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 0x%08x 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 == 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) ",

                             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 ",

                                        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());

                }

            }


    根据设备索引的设备文件句柄,通过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里面,看看下面的图,理解数据结构的变化



    2.7处理事件


    在上面2.4节最后我们看到InputReaderThread线程里面会 循环调用 InputReader::loopOnce  也就是 循环调用

    count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE); 读取事件;上一节已经介绍了,得到事件后,接着调用

    processEventsLocked(mEventBuffer, count)处理事件;


    processEventsLocked里面主要分两步处理:


           1)处理来自于事件驱动设备的事件(processEventsForDeviceLocked


           2)处理设备增加、删除 和 修改事件 为处理事件做准备;


    for (const RawEvent* rawEvent = rawEvents; count;) {

            int32_t type = rawEvent->type;


            size_t batchSize = 1;

            //进入for循环,取得本次循环头一个rawEvent,然后判断事件type如果小于FIRST_SYNTHETIC_EVENT,就表示是真正的input事件,如果大于等于,就表示是input设备变化事件。

            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;

                }

                processEventsForDeviceLocked(deviceId, rawEvent, batchSize);

    //判断下一事件type,从rawEvent数组里面取得属于同一个设备的连续input事件,然后交给设备处理程序去处理,如果后面的事件不属于同一个设备,或者事件typeFIRST_SYNTHETIC_EVENT以后的事件,就终止查询,运行processEventsForDeviceLocked

            } else {

                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;

                }

            }

    //如果事件typeFIRST_SYNTHETIC_EVENT以后的事件,是与Device相关的事件,这些事件是在EventHub::getEvents中产生的,并不是Kernel态的事件输入设备产生的。就调用设备添加,删除,配置变化等函数。

            count -= batchSize;

            rawEvent += batchSize;

    //去掉已经处理的事件,为下一次循环做准备。

    }

    至此,我们看到inputreadergetEvents得到的事件都有一一对应的处理。



    2.7.1 处理事件准备 —-- 设备 添加删除


    按照程序执行流程,应该是先有设备,然后才会有设备事件,所以先分析设备增加。 其代码如下:

    InputReader::addDeviceLocked

    String8 name = mEventHub->getDeviceName(deviceId);

    uint32_t classes = mEventHub->getDeviceClasses(deviceId);

    得到设备名字和类型

    InputDevice* device = createDeviceLocked(deviceId, name, classes);

    得到一个inputreader层的device

    device->configure(when, &mConfig, 0);

    device->reset(when);

    进行device配置和reset

    if (device->isIgnored()) {

            LOGI("Device added: id=%d, name='%s' (ignored non-input device)", deviceId, name.string());

        } else {

            LOGI("Device added: id=%d, name='%s', sources=0x%08x", deviceId, name.string(),

                    device->getSources());

        }

    判断devicemapper是否存在,如果不存在,这个设备就不是input device

    mDevices.add(deviceId, device);

    新建的InputDevice增加到InputReader::mDevices中

    InputReader::createDeviceLocked

    InputDevice* device = new InputDevice(&mContext, deviceId, name, classes);

    创建一个inputreader层的device

    // External devices.

        if (classes & INPUT_DEVICE_CLASS_EXTERNAL) {

            device->setExternal(true);

    }

    根据类型设置是否是外部设备

    接下来就是根据类型给device创建和增加事件转换器,即mapper,我们只分析touch

    // 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));

        }

    根据多点还是单点分别创建事件转换器。我们只分析单点设备,我们的touch只有一个mapper--- SingleTouchInputMapper

    SingleTouchInputMapper::SingleTouchInputMapper

    它继承自TouchInputMapper--- InputMapper

    做一些初始化的工作

    InputDevice::addMapper

    mMappers.add(mapper)

    新建的InputMapper增加到InputDevice::mMappers

    至此inputreader层的input device创建完成,并且每个device都创建了一个对应的事件转换器。

    创建完就要进行配置

    device->configure(when, &mConfig, 0);

    InputDevice::configure

    if (!isIgnored()) {

            if (!changes) { // first time only

                mContext->getEventHub()->getConfiguration(mId, &mConfiguration);

            }

            size_t numMappers = mMappers.size();

            for (size_t i = 0; i < numMappers; i++) {

                InputMapper* mapper = mMappers[i];

                mapper->configure(when, config, changes);

                mSources |= mapper->getSources();

            }

    }

    判断mapper是否为空,如果不存在,就不需要配置。判断是否是配置改变,不是配置改变,那就是第一次进行配置,需要从eventhub里面得到设备的idc配置文件

    接着对mapper进行配置,可能有多个事件转换器,一一对相应的mapper进行转换。

    mapper->configure,我们分析的是单点touch,因此mapper对应的是SingleTouchInputMapper,它里面没有configure,继续找TouchInputMapper

    TouchInputMapper::configure

    InputMapper::configure(when, config, changes)

    这个什么也没有做

    if (!changes) { // first time only

            // Configure basic parameters.

            configureParameters();

            // Configure common accumulators.

            mCursorScrollAccumulator.configure(getDevice());

            mTouchButtonAccumulator.configure(getDevice());

            // Configure absolute axis information.

            configureRawPointerAxes();

            // Prepare input device calibration.

            parseCalibration();

            resolveCalibration();

    }

    如果是第一次配置,就进入里面,调用

    TouchInputMapper::configureParameters

    mParameters.gestureMode = getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_SEMI_MT)

                ? Parameters::GESTURE_MODE_POINTER : Parameters::GESTURE_MODE_SPOTS;

    首先从驱动文件里面得到mParameters.gestureMode类型

    String8 gestureModeString;

        if (getDevice()->getConfiguration().tryGetProperty(String8("touch.gestureMode"),

                gestureModeString)) {

            if (gestureModeString == "pointer") {

                mParameters.gestureMode = Parameters::GESTURE_MODE_POINTER;

            } else if (gestureModeString == "spots") {

                mParameters.gestureMode = Parameters::GESTURE_MODE_SPOTS;

            } else if (gestureModeString != "default") {

                LOGW("Invalid value for touch.gestureMode: '%s'", gestureModeString.string());

            }

    }

    如果idc文件有touch.gestureMode存在,使用idc文件的配置

    if (getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_DIRECT)) {

            // The device is a touch screen.

            mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_SCREEN;

        } else if (getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_POINTER)) {

            // The device is a pointing device like a track pad.

            mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER;

        } else if (getEventHub()->hasRelativeAxis(getDeviceId(), REL_X)

                || getEventHub()->hasRelativeAxis(getDeviceId(), REL_Y)) {

            // The device is a cursor device with a touch pad attached.

            // By default don't use the touch pad to move the pointer.

            mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_PAD;

        } else {

            // The device is a touch pad of unknown purpose.

            mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER;

        }

    从驱动文件里面得到touch的类型

    String8 deviceTypeString;

        if (getDevice()->getConfiguration().tryGetProperty(String8("touch.deviceType"),

                deviceTypeString)) {

            if (deviceTypeString == "touchScreen") {

                mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_SCREEN;

            } else if (deviceTypeString == "touchPad") {

                mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_PAD;

            } else if (deviceTypeString == "pointer") {

                mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER;

            } else if (deviceTypeString != "default") {

                LOGW("Invalid value for touch.deviceType: '%s'", deviceTypeString.string());

            }

        }

    如果idc文件有touch.deviceType存在,使用idc文件的配置,这里我们配置是touchScreen,即mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_SCREEN

    mParameters.orientationAware = mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN;

        getDevice()->getConfiguration().tryGetProperty(String8("touch.orientationAware"),

                mParameters.orientationAware);

    从idc文件里面得到mParameters.orientationAware的值

    mParameters.associatedDisplayId = -1;

        mParameters.associatedDisplayIsExternal = false;

        if (mParameters.orientationAware

                || mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN

                || mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER) {

            mParameters.associatedDisplayIsExternal =

                    mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN

                            && getDevice()->isExternal();

            mParameters.associatedDisplayId = 0;

    }

    根据mParameters.deviceTypegetDevice()->isExternal来判断是否使用外部显示配置。在eventhub里面我们的触摸屏是usb bus,被配置成外部设备,触摸屏配置成DEVICE_TYPE_TOUCH_SCREEN,因此mParameters.associatedDisplayIsExternal等于1,及使用外部的显示配置。

    至此TouchInputMapper::configureParameters配置完成

    // Configure common accumulators.

            mCursorScrollAccumulator.configure(getDevice());

            mTouchButtonAccumulator.configure(getDevice());

    配置光标和按键加速,都是根据驱动文件或者idc文件,这个都不需要。

    // Configure absolute axis information.

            configureRawPointerAxes();

    配置原始信息,它先调用TouchInputMapper::configureRawPointerAxes

    mRawPointerAxes.clear()先将mRawPointerAxes清除干净

    接着调用SingleTouchInputMapper::configureRawPointerAxes

    getAbsoluteAxisInfo(ABS_X, &mRawPointerAxes.x);

        getAbsoluteAxisInfo(ABS_Y, &mRawPointerAxes.y);

        getAbsoluteAxisInfo(ABS_PRESSURE, &mRawPointerAxes.pressure);

        getAbsoluteAxisInfo(ABS_TOOL_WIDTH, &mRawPointerAxes.toolMajor);

        getAbsoluteAxisInfo(ABS_DISTANCE, &mRawPointerAxes.distance);

        getAbsoluteAxisInfo(ABS_TILT_X, &mRawPointerAxes.tiltX);

    getAbsoluteAxisInfo(ABS_TILT_Y, &mRawPointerAxes.tiltY);

    InputMapper::getAbsoluteAxisInfo调用

    getEventHub()->getAbsoluteAxisInfo从驱动文件里面得到需要参数

    // Prepare input device calibration.

            parseCalibration();

            resolveCalibration();

    根据idc文件配置校正参数。

    if (!changes || (changes & InputReaderConfiguration::CHANGE_POINTER_SPEED)) {

            // Update pointer speed.

            mPointerVelocityControl.setParameters(mConfig.pointerVelocityControlParameters);

            mWheelXVelocityControl.setParameters(mConfig.wheelVelocityControlParameters);

            mWheelYVelocityControl.setParameters(mConfig.wheelVelocityControlParameters);

    }

    如果是第一次配置或者是改变速度,需要update pointer speed

    bool resetNeeded = false;

        if (!changes || (changes & (InputReaderConfiguration::CHANGE_DISPLAY_INFO

                | InputReaderConfiguration::CHANGE_POINTER_GESTURE_ENABLEMENT

                | InputReaderConfiguration::CHANGE_SHOW_TOUCHES))) {

            // Configure device sources, surface dimensions, orientation and

            // scaling factors.

            configureSurface(when, &resetNeeded);

        }

    如果是第一次配置或者是显示等改变,需要调用configureSurface

    TouchInputMapper::configureSurface

    // Determine device mode.

        if (mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER

                && mConfig.pointerGesturesEnabled) {

            mSource = AINPUT_SOURCE_MOUSE;

            mDeviceMode = DEVICE_MODE_POINTER;

        } else if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN

                && mParameters.associatedDisplayId >= 0) {

            mSource = AINPUT_SOURCE_TOUCHSCREEN;

            mDeviceMode = DEVICE_MODE_DIRECT;

        } else {

            mSource = AINPUT_SOURCE_TOUCHPAD;

            mDeviceMode = DEVICE_MODE_UNSCALED;

    }

    根据mParameters.deviceType决定mSourcemDeviceMode

    // Ensure we have valid X and Y axes.

        if (!mRawPointerAxes.x.valid || !mRawPointerAxes.y.valid) {

            LOGW(INDENT "Touch device '%s' did not report support for X or Y axis!  "

                    "The device will be inoperable.", getDeviceName().string());

            mDeviceMode = DEVICE_MODE_DISABLED;

            return;

        }

    判断x和y参数是否有效,这里就是判断触摸屏x和y的坐标范围的,在eventhub里面,只要最大和最小不相等,就是有效的。如果无效,设备模式就是关闭的,不能使用。

    // Get associated display dimensions.

        if (mParameters.associatedDisplayId >= 0) {

            if (!mConfig.getDisplayInfo(mParameters.associatedDisplayId,

                    mParameters.associatedDisplayIsExternal,

                    &mAssociatedDisplayWidth, &mAssociatedDisplayHeight,

                    &mAssociatedDisplayOrientation)) {

                LOGI(INDENT "Touch device '%s' could not query the properties of its associated "

                        "display %d.  The device will be inoperable until the display size "

                        "becomes available.",

                        getDeviceName().string(), mParameters.associatedDisplayId);

                mDeviceMode = DEVICE_MODE_DISABLED;

                return;

            }

        }

    根据associatedDisplayIdassociatedDisplayIsExternal得到显示屏的分辨率, associatedDisplayIdconfigureParameters里面设为0,associatedDisplayIsExternal根据触摸屏类型和bus设为1.

    调用InputReaderConfiguration::getDisplayInfo得到设置好的surface size

    if (displayId == 0) {

            const DisplayInfo& info = external ? mExternalDisplay : mInternalDisplay;

            if (info.width > 0 && info.height > 0) {

                if (width) {

                    *width = info.width;

                }

                if (height) {

                    *height = info.height;

                }

                if (orientation) {

                    *orientation = info.orientation;

                }

                return true;

            }

    }

    可以看到android4.0里面,分内部和外部分辨率两种。如果info里面都是0,这个函数返回false,就表示android设备还没有走到设置surface size这一步,就会打印提示信息,稍后android启动里面就会执行设置surface size的程序。

    // Configure dimensions.

        int32_t width, height, orientation;

        if (mDeviceMode == DEVICE_MODE_DIRECT || mDeviceMode == DEVICE_MODE_POINTER) {

            width = mAssociatedDisplayWidth;

            height = mAssociatedDisplayHeight;

            orientation = mParameters.orientationAware ?

                    mAssociatedDisplayOrientation : DISPLAY_ORIENTATION_0;

        } else {

            width = mRawPointerAxes.x.maxValue - mRawPointerAxes.x.minValue + 1;

            height = mRawPointerAxes.y.maxValue - mRawPointerAxes.y.minValue + 1;

            orientation = DISPLAY_ORIENTATION_0;

        }

    根据mDeviceMode类型设置宽和高参数,根据上面的分析宽和高就是取自显示屏的分辨率。

    bool sizeChanged = mSurfaceWidth != width || mSurfaceHeight != height;

        if (sizeChanged || deviceModeChanged) {

            LOGI("Device reconfigured: id=%d, name='%s', surface size is now %dx%d, mode is %d",

                    getDeviceId(), getDeviceName().string(), width, height, mDeviceMode);

            mSurfaceWidth = width;

            mSurfaceHeight = height;

            // Configure X and Y factors.

            mXScale = float(width) / (mRawPointerAxes.x.maxValue - mRawPointerAxes.x.minValue + 1);

            mYScale = float(height) / (mRawPointerAxes.y.maxValue - mRawPointerAxes.y.minValue + 1);

            mXPrecision = 1.0f / mXScale;

            mYPrecision = 1.0f / mYScale;

    如果显示屏分辨率不等于开始保存的,就需要重新设置一些参数。红色部分就是显示屏分辨率和触摸屏坐标范围得到的转换因子,使用这个转换因子就可以把触摸屏坐标转换成屏幕坐标。

    接着下面是根据校准参数配置校准因子的。这里我们不使用这种方式,所以不执行

    下面如果sizeChanged改变,重新配置一些参数,同时设置

    *outResetNeeded = true;

    至此configureSurface执行完成,与触摸屏坐标有关的配置也完成了。

    if (changes && resetNeeded) {

            // Send reset, unless this is the first time the device has been configured,

            // in which case the reader will call reset itself after all mappers are ready.

            getDevice()->notifyReset(when);

    }

    如果有改变而且需要reset,reader将reset自己

    至此Input Device的configure和mapper configure都已完成完成

    配置完成就要进行初始化

    device->reset(when);

    调用InputDevice::reset

              mapper->reset(when)

             mapper初始化

    至此整个InputReader::addDeviceLocked已经分析完成了,到了这一步,我们的整个input系统都已经准备好去接收真正的input event并处理。

    分析设备删除, 其代码如下:

    void InputReader::removeDeviceLocked(nsecs_t when, int32_t deviceId) {

        InputDevice* device = NULL;

        ssize_t deviceIndex = mDevices.indexOfKey(deviceId);

        if (deviceIndex >= 0) {

            device = mDevices.valueAt(deviceIndex);

            mDevices.removeItemsAt(deviceIndex, 1);

    把设备从mDevices链表里面移除

        } else {

            LOGW("Ignoring spurious device removed event for deviceId %d.", deviceId);

            return;

        }

        if (device->isIgnored()) {

            LOGI("Device removed: id=%d, name='%s' (ignored non-input device)",

                    device->getId(), device->getName().string());

        } else {

            LOGI("Device removed: id=%d, name='%s', sources=0x%08x",

                    device->getId(), device->getName().string(), device->getSources());

        }

    device->reset(when);

    清除device配置

    delete device;

    删除device

    }

     InputReader::processEventsLocked设备增加、删除处理总结:

      它负责处理inputreader层Device 增加、删除事件。增加事件的流程为:为一个新增的Device创建一个InputDevice,并增加到InputReader::mDevices中;根据新增加设备的class类别,创建对应的消息转换器(InputMapper),然后此消息转换器加入InputDevice::mMappers中。消息转换器负责把读取的RawEvent转换成特定的事件,以供应用程序使用。

    EventHub与InputReader各自管理功能:

    EventHub管理一堆Device,每一个Device与Kernel中一个事件输入设备对应

    InputReader管理一堆InputDevice,每一个InputDevice与EventHub中的Device对应

    InputDevice管理一些与之相关的InputMapper,每个device类型不同,会有一个InputMapper或者多个InputMapper,如我们touch只有:SingleTouchInputMapper。

    下面再来看看inputreader里面touch类的关系

    2.7.2 处理事件准备设置surface size

    上一节讲到input devicetouch mapper配置时,会得到surface size,如果得到为0,就会把mDeviceMode 配置为DEVICE_MODE_DISABLED,表示这个设备暂时无法使用,因此即使我们的input系统都准备好了,但是touch还是无法使用。下面讲如何配置surface size

    在2.3节线程创建里面看到:

    SystemServer.java (frameworksaseservicesjavacomandroidserver)里面

    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);

    下面接着就会调用

    try {

                wm.displayReady();   wm就是上面创建的,为后面整个显示做准备

            } catch (Throwable e) {

                reportWtf("making display ready", e);

            }

    WindowManagerService.java (frameworksaseservicesjavacomandroidserverwm)里面

    displayReady里面调用

    WindowManager wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);

                mDisplay = wm.getDefaultDisplay();

                mInitialDisplayWidth = mDisplay.getRawWidth();

                mInitialDisplayHeight = mDisplay.getRawHeight();

    得到原始显示分辨率,这时就是整个屏幕的分辨率

    mInputManager.setDisplaySize(Display.DEFAULT_DISPLAY,

                        mDisplay.getRawWidth(), mDisplay.getRawHeight(),

                        mDisplay.getRawExternalWidth(), mDisplay.getRawExternalHeight());

    mInputManager是在WindowManagerService一开始就创建的,我们来看看setDisplaySize里面后四个参数:

    public int getRawWidth() {

            int w = getRawWidthNative();

            if (DEBUG_DISPLAY_SIZE) Slog.v(

                    TAG, "Returning raw display  " + w);

            return w;

        }

    private native int getRawWidthNative();

    public int getRawHeight() {

            int h = getRawHeightNative();

            if (DEBUG_DISPLAY_SIZE) Slog.v(

                    TAG, "Returning raw display height: " + h);

            return h;

        }

    private native int getRawHeightNative();

    这两个函数要调用native空间函数

    android_view_Display.cpp (frameworksasecorejni)

    static jint android_view_Display_getRawWidthNative(

            JNIEnv* env, jobject clazz)

    {

        DisplayID dpy = env->GetIntField(clazz, offsets.display);

        return SurfaceComposerClient::getDisplayWidth(dpy);

    }

    static jint android_view_Display_getRawHeightNative(

            JNIEnv* env, jobject clazz)

    {

        DisplayID dpy = env->GetIntField(clazz, offsets.display);

        return SurfaceComposerClient::getDisplayHeight(dpy);

    }

    可以看到通过surface的client端得到屏幕的分辨率。

    如何得到外部分辨率

    public int getRawExternalWidth() {

            return 1280;

        }

        /**

         * If the display is mirrored to an external HDMI display, returns the

         * height of that display.

         * @hide

         */

        public int getRawExternalHeight() {

            return 720;

        }

    可以看到如果设备外界hdmi显示,就用1280*720分辨率,这里有个疑问:如果客户设备外的是1920*1080的hdmi,那么这两个值是否需要变化?而且看这两个函数用处,只有触摸坐标转换和HeightReceiver.java使用,如果使用外部分辨率,那么android显示系统是如何知道的?

    继续看mInputManager.setDisplaySize

    InputManager.java (frameworksaseservicesjavacomandroidserverwm)里面

    setDisplaySize调用

           nativeSetDisplaySize(displayId, width, height, externalWidth, externalHeight);

    从下面开始就进入native空间

    com_android_server_InputManager.cpp (frameworksaseservicesjni)里面

    nativeSetDisplaySize对应android_server_InputManager_nativeSetDisplaySize调用

    gNativeInputManager->setDisplaySize(displayId, width, height, externalWidth, externalHeight);

    setDisplaySize里面判断

    if (mLocked.displayWidth != width || mLocked.displayHeight != height) {

                changed = true;

                mLocked.displayWidth = width;

                mLocked.displayHeight = height;

                sp<PointerController> controller = mLocked.pointerController.promote();

                if (controller != NULL) {

                    controller->setDisplaySize(width, height);

                }

            }

            if (mLocked.displayExternalWidth != externalWidth

                    || mLocked.displayExternalHeight != externalHeight) {

                changed = true;

                mLocked.displayExternalWidth = externalWidth;

                mLocked.displayExternalHeight = externalHeight;

            }

    如果这次设置的值和旧值相等,就什么也不做退出。如果不相等,设置changed = true,同时保存新的值

    if (changed) {

            mInputManager->getReader()->requestRefreshConfiguration(

                    InputReaderConfiguration::CHANGE_DISPLAY_INFO);

    }

    如果值有变化,就调用inputreader刷新配置,提示是displayinfo改变

    首先是去InputManager.cpp (frameworksaseservicesinput)文件里面

    getReader()

    return mReader;

    这个就是InputManager创建是创建的inputreader

    InputReader.cpp (frameworksaseservicesinput)里面

    InputReader::requestRefreshConfiguration调用

    if (changes) {

            bool needWake = !mConfigurationChangesToRefresh;

            mConfigurationChangesToRefresh |= changes;

            if (needWake) {

                mEventHub->wake();

            }

    }

    如果改变类型不为0,就mConfigurationChangesToRefresh取反送给needWakemConfigurationChangesToRefresh表示需要改变配置的类型集合,初始化为0,因此needWake就为1,同时把改变类型与给mConfigurationChangesToRefresh,接着判断needWake,如果为1,就进入mEventHub的唤醒程序。

    这段话意思就是如果有正在改变配置需求,就表明整个input系统正在运行,所以不需要唤醒。只需要把新的改变类型放在mConfigurationChangesToRefresh就行了,如果没有,那么input系统有可能在睡眠,为了快速响应改变,需要唤醒整个input系统。

    EventHub.cpp (frameworksaseservicesinput)里面

    EventHub::wake()调用

    nWrite = write(mWakeWritePipeFd, "W", 1);

    直接向mWakeWritePipeFd管道里面写一个字符。前面2.5节讲到mWakeReadPipeFd已经被mEpollFd监控了,向mWakeWritePipeFd写就会引起mWakeReadPipeFd变化。在EventHub::getEvents里面就会执行if (eventItem.data.u32 == EPOLL_ID_WAKE)这个分支,设置awokentrue,当mPendingEventItems事件处理完,就会判断awoken,如果为true就立即结束循环,返回给inputreader进行处理。

    至此,配置surface size执行部分结束了,但是size并没有真正配置到mExternalDisplaymInternalDisplay里面,只是改变类型放在mConfigurationChangesToRefresh里面,真正的size还保存在NativeInputManagermLocked里面

    这时并没有输入事件或者设备变化发生,因此InputReader::loopOnce很快结束,进入下一次循环

    InputReader::loopOnce  接着调用

    uint32_t changes = mConfigurationChangesToRefresh;

            if (changes) {

                mConfigurationChangesToRefresh = 0;

                refreshConfigurationLocked(changes);

            }

    InputReader::refreshConfigurationLocked

    mPolicy->getReaderConfiguration(&mConfig);

    mPolicy就是NativeInputManager的对象,在创建inputreader传入的。

    com_android_server_InputManager.cpp (frameworksaseservicesjni)里面

    NativeInputManager::getReaderConfiguration调用

    { // acquire lock

            AutoMutex _l(mLock)

            outConfig->setDisplayInfo(0, false /*external*/,

                    mLocked.displayWidth, mLocked.displayHeight, mLocked.displayOrientation);

            outConfig->setDisplayInfo(0, true /*external*/,

                    mLocked.displayExternalWidth, mLocked.displayExternalHeight,

                    mLocked.displayOrientation);

    } // release lock

    outConfig就是inputreader里面的mConfig,因此调用

    InputReaderConfiguration::setDisplayInfo

    if (displayId == 0) {

            DisplayInfo& info = external ? mExternalDisplay : mInternalDisplay;

            info.width = width;

            info.height = height;

            info.orientation = orientation;

    }

    看到没有,饶了一个大圈,这里才把surface size真正放在mExternalDisplay mInternalDisplay里面,供后面调用InputReaderConfiguration::getDisplayInfo时使用。

    refreshConfigurationLocked函数会在inputreader创建时执行一次,但那个时候input device还没有创建,而且changes为0,因此不会执行下面的部分。

    if (changes & InputReaderConfiguration::CHANGE_MUST_REOPEN) {

                mEventHub->requestReopenDevices();

    如果改变类型是reopen,就调用eventhub的requestReopenDevices

    EventHub::requestReopenDevices里面设置mNeedToReopenDevices = true,这个会在EventHub::getEvents里面进行判断,前面2.6节已经讲了

            } else {

                for (size_t i = 0; i < mDevices.size(); i++) {

                    InputDevice* device = mDevices.valueAt(i);

                    device->configure(now, &mConfig, changes);

                }

            }

    得到所有的device,循环调用每个device的configure去重新配置。

    InputDevice::configure已经在InputReader::addDeviceLocked讲过了,刚开始第一次配置changes是0,表示需要全面的初始化。现在只需要配置改变的部分了。

    至此,我们整个input touch的工作环境已经配置好了,就等有触摸事件发生然后处理了

    2.7.3处理来自于事件驱动设备的事件

    InputReader::processEventsForDeviceLocked它负责处理来自于同一个设备且在mEventBuffer中连续的多个事件,其函数原型如下:

    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);

        if (deviceIndex < 0) {

            LOGW("Discarding event for unknown deviceId %d.", deviceId);

            return;

        }

    得到发生事件设备索引

    InputDevice* device = mDevices.valueAt(deviceIndex);

    根据索引得到发生事件的device

        if (device->isIgnored()) {

            //LOGD("Discarding event for ignored deviceId %d.", deviceId);

            return;

        }

    如果device没有mapper,就返回不做任何处理。

    device->process(rawEvents, count);

    调用process处理

    InputDevice::process

    for (const RawEvent* rawEvent = rawEvents; count--; rawEvent++)

    一次取出每一个事件

    for (size_t i = 0; i < numMappers; i++) {

                    InputMapper* mapper = mMappers[i];

                    mapper->process(rawEvent);

                }

    对每一个事件都用这个device所有mapper进行处理

    从上面的代码中可以看出,在InputDevice::process中,对于传入的每一个RawEvent,依次调用InputDevice中的每一个InputMapper来进行处理。前面提到过,InputDevice包含一组处理对应设备事件InputMapper,现在这些InputMapper开始干活了。

    因为我们的touch只有一个SingleTouchInputMapper

    这里先说说单点touch需要处理事件集合

    代码:

        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

        代码:

        input_sync(myInputDev);
        它调用input_event(dev, EV_SYN, SYN_REPORT, 0);
       产生的事件:*type, code, value
               EV_SYN, SYN_REPORT, 0

    SingleTouchInputMapper::process调用

    TouchInputMapper::process(rawEvent);

    TouchInputMapper::process

    mCursorButtonAccumulator.process(rawEvent); 

    因为是touchrawEvent->type  EV_KEY,但是rawEvent->scanCode不匹配里面任何值,不起任何作用

    mCursorScrollAccumulator.process(rawEvent);

    因为是touchrawEvent->type  EV_KEY,不是EV_REL,不起任何作用

    mTouchButtonAccumulator.process(rawEvent);

    TouchButtonAccumulator::process

    if (rawEvent->type == EV_KEY) {

            switch (rawEvent->scanCode) {

            case BTN_TOUCH:

                mBtnTouch = rawEvent->value;

                break;

    可以看到把BTN_TOUCH的值放在mBtnTouch里面

    接着处理坐标信息:

    mSingleTouchMotionAccumulator.process(rawEvent)

    SingleTouchMotionAccumulator::process

    case ABS_X:

                mAbsX = rawEvent->value;

                break;

            case ABS_Y:

                mAbsY = rawEvent->value;

                break;

    将坐标信息保存在mAbsXmAbsY里面

    BTN_TOUCHABS_XABS_Y处理完,接下来就会处理EV_SYN事件

    TouchInputMapper::process里面, 

    if (rawEvent->type == EV_SYN && rawEvent->scanCode == SYN_REPORT) {

            sync(rawEvent->when);

    }

    TouchInputMapper::sync调用

    syncTouch(when, &havePointerIds)

    SingleTouchInputMapper::syncTouch

    if (mTouchButtonAccumulator.isToolActive()) {

    判断BTN_TOUCH是否等于1,即是否有touch down,如果有进入下面处理

            mCurrentRawPointerData.pointerCount = 1; 设置触摸点数1

            mCurrentRawPointerData.idToIndex[0] = 0;  触摸点索引为0

       RawPointerData::Pointer& outPointer = mCurrentRawPointerData.pointers[0];

            outPointer.id = 0;

            outPointer.x = mSingleTouchMotionAccumulator.getAbsoluteX();

            outPointer.y = mSingleTouchMotionAccumulator.getAbsoluteY();

            outPointer.pressure = mSingleTouchMotionAccumulator.getAbsolutePressure();

            outPointer.touchMajor = 0;

            outPointer.touchMinor = 0;

            outPointer.toolMajor = mSingleTouchMotionAccumulator.getAbsoluteToolWidth();

            outPointer.toolMinor = mSingleTouchMotionAccumulator.getAbsoluteToolWidth();

            outPointer.orientation = 0;

            outPointer.distance = mSingleTouchMotionAccumulator.getAbsoluteDistance();

            outPointer.tiltX = mSingleTouchMotionAccumulator.getAbsoluteTiltX();

            outPointer.tiltY = mSingleTouchMotionAccumulator.getAbsoluteTiltY();

            outPointer.toolType = mTouchButtonAccumulator.getToolType();

            if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {

                outPointer.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;

            }

            outPointer.isHovering = isHovering;

    把相关的信息放在mCurrentRawPointerData.pointers[0]里面,这里主要是xy坐标

    TouchInputMapper::sync继续处理

    // Reset state that we will compute below.

        mCurrentFingerIdBits.clear();

        mCurrentStylusIdBits.clear();

        mCurrentMouseIdBits.clear();

    mCurrentCookedPointerData.clear();

    这几个清零,后面填入相应的值

    if (mDeviceMode == DEVICE_MODE_DISABLED) {

            // Drop all input if the device is disabled.

            mCurrentRawPointerData.clear();

            mCurrentButtonState = 0;

        }

    如果设备状态是关闭的,就把mCurrentRawPointerData保存的数据清除,返回

    uint32_t policyFlags = 0;

            bool initialDown = mLastRawPointerData.pointerCount == 0

                    && mCurrentRawPointerData.pointerCount != 0;

            bool buttonsPressed = mCurrentButtonState & ~mLastButtonState;

            if (initialDown || buttonsPressed) {

                // If this is a touch screen, hide the pointer on an initial down.

                if (mDeviceMode == DEVICE_MODE_DIRECT) {

                    getContext()->fadePointer();

                }

                // Initial downs on external touch devices should wake the device.

                // We don't do this for internal touch screens to prevent them from waking

                // up in your pocket.

                // TODO: Use the input device configuration to control this behavior more finely.

                if (getDevice()->isExternal()) {

                    policyFlags |= POLICY_FLAG_WAKE_DROPPED;

                }

            }

    判断是不是第一次按下,如果是,If this is a touch screen, hide the pointer on an initial down

    如果是外部设备,就唤醒整个系统,如果是内部设备,就不用唤醒,注释写的很清楚,有可能放在口袋里面误触摸唤醒系统

    // Consume raw off-screen touches before cooking pointer data.

            // If touches are consumed, subsequent code will not receive any pointer data.

            if (consumeRawTouches(when, policyFlags)) {

                mCurrentRawPointerData.clear();

    如果是唤醒设备的点击,就把mCurrentRawPointerData清零,只需要唤醒设备就行了。

    cookPointerData();

    进行触摸坐标到原始坐标的转换

    TouchInputMapper::cookPointerData

    首先进行一系列的坐标校准,接下来进行坐标转换

    // X and Y

            // Adjust coords for surface orientation.

            float x, y;

            switch (mSurfaceOrientation) {

            case DISPLAY_ORIENTATION_90:

                x = float(in.y - mRawPointerAxes.y.minValue) * mYScale;

                y = float(mRawPointerAxes.x.maxValue - in.x) * mXScale;

                orientation -= M_PI_2;

                if (orientation < - M_PI_2) {

                    orientation += M_PI;

                }

                break;

            case DISPLAY_ORIENTATION_180:

                x = float(mRawPointerAxes.x.maxValue - in.x) * mXScale;

                y = float(mRawPointerAxes.y.maxValue - in.y) * mYScale;

                break;

            case DISPLAY_ORIENTATION_270:

                x = float(mRawPointerAxes.y.maxValue - in.y) * mYScale;

                y = float(in.x - mRawPointerAxes.x.minValue) * mXScale;

                orientation += M_PI_2;

                if (orientation > M_PI_2) {

                    orientation -= M_PI;

                }

                break;

            default:

                x = float(in.x - mRawPointerAxes.x.minValue) * mXScale;

                y = float(in.y - mRawPointerAxes.y.minValue) * mYScale;

                break;

            }

    TouchInputMapper::configureSurface里面

    通过mConfig.getDisplayInfo(mParameters.associatedDisplayId,

                    mParameters.associatedDisplayIsExternal,

                    &mAssociatedDisplayWidth, &mAssociatedDisplayHeight,

                    &mAssociatedDisplayOrientation))

    得到mAssociatedDisplayOrientation的值,这个值是通过setDisplayInfomLocked.displayOrientation得到的。在NativeInputManager创建是初始化这个值

    mLocked.displayOrientation = DISPLAY_ORIENTATION_0

    orientation = mParameters.orientationAware ?

                    mAssociatedDisplayOrientation : DISPLAY_ORIENTATION_0;

    mParameters.orientationAwareidc文件里面的值,我们这里是1,即

    orientation = mAssociatedDisplayOrientation 默认为DISPLAY_ORIENTATION_0

    bool orientationChanged = mSurfaceOrientation != orientation;

        if (orientationChanged) {

            mSurfaceOrientation = orientation;

    }

    mSurfaceOrientation = orientation = mAssociatedDisplayOrientation = mLocked.displayOrientation

    因此在cookPointerData会执行

    x = float(in.x - mRawPointerAxes.x.minValue) * mXScale;

                y = float(in.y - mRawPointerAxes.y.minValue) * mYScale;

    这就是坐标转换的地方,in.xin.y是触摸屏坐标,mRawPointerAxes.x.minValuemRawPointerAxes.y.minValue是触摸屏坐标范围最小值,mXScalemYScale就是

    mXScale = float(width) / (mRawPointerAxes.x.maxValue - mRawPointerAxes.x.minValue + 1);

            mYScale = float(height) / (mRawPointerAxes.y.maxValue - mRawPointerAxes.y.minValue + 1);

    把它们和在一起就是http://source.android.com/tech/input/touch-devices.html这个里面说的:

    For a touch screen, the system automatically interpolates the reported touch positions in surface units to obtain touch positions in display pixels according to the following calculation:

    displayX = (x - minX) * displayWidth / (maxX - minX + 1)

    displayY = (y - minY) * displayHeight / (maxY - minY + 1)

    接着把转换后的坐标放在out里面

    PointerCoords& out = mCurrentCookedPointerData.pointerCoords[i];

            out.clear();

            out.setAxisValue(AMOTION_EVENT_AXIS_X, x);

            out.setAxisValue(AMOTION_EVENT_AXIS_Y, y);

    dispatchTouches(when, policyFlags) 调用

    if (currentIdBits == lastIdBits)

    如果当前点id和上一次id相同,表明这是个移动事件,不是的话就判断是downup或者move,然后调用dispatchMotion,它的第四个参数就是downupmove等类型

    在dispatchMotion中,根据cooked数据创建NotifyMotionArg对象,它描述了一个移动事件,接着调用TouchInputMapper::getListener()->notifyMotion(&args)

    TouchInputMapper::getListener()调用mContext->getListener(),此mContextInputReader::mContext所以其getListener()返回的则为InputReader::mQueuedListener,则最后调用QueuedInputListener::notifyMotion

    QueuedInputListener::notifyMotion(const NotifyMotionArgs* args) {

        mArgsQueue.push(new NotifyMotionArgs(*args));

    }

    把传递过来的NotifyMotionArg参数复制一份,然后加入QueuedInputListener::mArgsQueue例表中

      补充1) InputReader::mContext在构造时用自己的指针初始化了mContext,从而mContext::mReader则为此InputReader实例。
     补充2) 在InputReader::createDeviceLocked中创建InputDevice时,把自己的mContext作为参数传入,从而把它保存在InputDevice::mContext中;在创建InputMapper时,以InputDevice作为参数,且InputMapper把它保存在mDevice中,然后从把InputDevice中的mContext也保存在InputMapper的mContext中。

    dispatchHoverEnterAndMove(when, policyFlags);

       调用dispatchMotion

    至此整个processEventsLocked处理流程结束,已经把来自于事件设备的事件处理之后放入到各种NotifyArgs(如NotifyMotionArgs)之中,然后把这些各种NotifyArgs加入InputReader::mQueuedListener::mArgsQueue链表中。接着InputReader::loopOnce调用

    mQueuedListener->flush()

    Flush函数就是要把mArgsQueue中的所有NotifyArgs进行处理。

    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_MOVE事件,则从mInboundQueue队列里面寻找到对应的entry,把args信息放在这个entry里面

    3)对于AMOTION_EVENT_ACTION_UP或AMOTION_EVENT_ACTION_DOWN事件,则直接根据NotifyMotionArgs提供的信息,构造一个MotionEntry。

    4)调用InputDispatcher::enqueueInboundEventLocked把新构造的MotionEntry添加到InputDispatcher::mInboundQueue中,并返回是否需要唤醒mLooper<向pipe中写入数据>的标识。

    if (needWake) {

            mLooper->wake();

    }

    根据表示唤醒mLooper

    以上操作都是在InputReader线程中完成的,现在应该InputDispatcher线程开始工作了。

    至此InputReader::loopOnce一次循环结束,所有的input事件已经处理并放在了InputDispatcher::mInboundQueue里面

    事件处理相关数据结构如下图所示:

    至此的消息结构变化流程:

    2.8分发事件

    前面线程启动提到InputDispatcher::dispatchOnce调用

    mLooper->pollOnce(timeoutMillis);

    其功能为等待超时或被pipe唤醒(InputReader线程调用InputDispatcher::notifyMotion时, InputDispatcher::notifyMotion根据情况调用mLooper->wake)。

    其调用流程如下:

      mLooper->pollOnce(int timeoutMillis)->

    Looper.cpp (frameworksaselibsutils)

    Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData)

    如果没有事件输入,那么InputDispatcher::dispatchOnce就会被阻塞在pollOnce,调用mLooper->wake唤醒。就会重新执行dispatchOnce,就会调用dispatchOnceInnerLocked

    InputDispatcher::dispatchOnceInnerLocked

    1)从mInboundQueue从中依次取出EventEntry<MotionEntry的基类>

     2)调用InputDispatcher::dispatchMotionLocked处理此MotionEntry

     3)调用InputDispatcher::dispatchEventToCurrentInputTargetsLocked

     对于InputDispatcher::mCurrentInputTargets中的每一个InputTarget,并获取对应的Connection,调用InputDispatcher::prepareDispatchCycleLocked,

    InputDispatcher::dispatchEventToCurrentInputTargetsLocked

    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

            }

        }

    InputDispatcher::prepareDispatchCycleLocked

    1)调用enqueueDispatchEntryLocked创建DispatchEntry对象,并把它增加到Connection::outboundQueue队列中。

    2)调用activateConnectionLocked把当前Connection增加到InputDispatcher::mActiveConnections链表中

    3)调用InputDispatcher::startDispatchCycleLocked,接着它调用Connection::inputPublisher.publishMotionEvent来发布事件到ashmem buffer中,调用Connection::inputPublisher.sendDispatchSignal发送一个dispatch信号到InputConsumer通知它有一个新的消息到了,快来消费吧!


    内核层驱动


    请参考网上的linux内核input子系统解析






  • 相关阅读:
    Stm32高级定时器(一)
    AES算法简介
    vsim仿真VHDL输出fsdb格式文件
    ncsim仿真VHDL
    云贵高原骑行
    触发器(笔记)
    几种常见的十进制代码(笔记)
    时序电路分类
    组合逻辑电路和时序逻辑电路比较
    数字电路基础(网络整理)
  • 原文地址:https://www.cnblogs.com/liulaolaiu/p/11744630.html
Copyright © 2011-2022 走看看