Linux/Android——Input系统之InputReader (七)

在前文Linux/Android——Input系统之frameworks层InputManagerService (六)  这里介绍了android层input服务的启动,其中启动了一个读取来自底层event事件的线程.

而在Linux/Android——input系统之 kernel层 与 frameworks层交互 (五)有分析到是通过一个event%d的设备文件交互的,也就是说android层是通过读取event%d来获取event的,这个工作就是InputReader

撰写不易,转载需注明出处:http://blog.csdn.net/jscese/article/details/42739197

getEvents:

这个是运行在inputread线程里面的,上篇有介绍到,字面意思就是获取事件的,实现在/frameworks/base/services/input/EventHub.cpp中,函数很长,列出几个重要的:

size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {

...

    struct input_event readBuffer[bufferSize];

    RawEvent* event = buffer;

...

        if (mNeedToScanDevices) {  //这个在EventHub初始时为true
            mNeedToScanDevices = false;
            scanDevicesLocked();  // 扫描去打开设备,后面跟进去看
            mNeedToSendFinishedDeviceScan = true;
        }

        while (mOpeningDevices != NULL) {  //这个指针有指向,代表上面打开了某些input设备
            Device* device = mOpeningDevices;    //  初始化一个添加event,type 为DEVICE_ADDED
            ALOGV("Reporting device opened: id=%d, name=%s\n",
                 device->id, device->path.string());
            mOpeningDevices = device->next;
            event->when = now;
            event->deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
            event->type = DEVICE_ADDED;
            event += 1;
            mNeedToSendFinishedDeviceScan = true;
            if (--capacity == 0) {
                break;
            }
        }

...

   while (mPendingEventIndex < mPendingEventCount) {  //这个是为处理多个input event 做的一个epoll_event 类型数组
            const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];

            Device* device = mDevices.valueAt(deviceIndex)  ; // 上面会把打开的device 加入到 这个Vector
            if (eventItem.events & EPOLLIN) {
                int32_t readSize = read(device->fd, readBuffer,   //这个之前有提到过。最终会调用到evdev中的read
                        sizeof(struct input_event) * capacity);

...

       for (size_t i = 0; i < count; i++) {
                        const struct input_event& iev = readBuffer[i];   //这里把上面读到的input_event 转化过这里的RawEvent

     event->deviceId = deviceId;
                        event->type = iev.type;
                        event->code = iev.code;
                        event->value = iev.value;
                        event += 1;

     }

...
   int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis); //用于等待监测是否有事件可读
...
 }

    // All done, return the number of events we read.
    return event - buffer;   //指针相减,这个数组的元素个数

}

scanDevicesLocked:

这个往下就是打开一个input 设备文件,配置并抽象为一个android层这边的input device:

void EventHub::scanDevicesLocked() {
    status_t res = scanDirLocked(DEVICE_PATH);
    if(res < 0) {
        ALOGE("scan dir failed for %s\n", DEVICE_PATH); //这里的PATH为"/dev/input"
    }
    if (mDevices.indexOfKey(VIRTUAL_KEYBOARD_ID) < 0) {
        createVirtualKeyboardLocked();
    }
}

继续往下看会依次读取目录下的文件,并调用进openDeviceLocked打开,这个函数也比较长,关注几个地方先:

status_t EventHub::openDeviceLocked(const char *devicePath) {
    char buffer[80];

    ALOGV("Opening device: %s", devicePath);

    int fd = open(devicePath, O_RDWR | O_CLOEXEC);
    if(fd < 0) {
        ALOGE("could not open %s, %s\n", devicePath, strerror(errno));
        return -1;
    }

...  //  无非是根据打开的fd获取一些相关参数信息

    // Allocate device.  (The device object takes ownership of the fd at this point.)
    int32_t deviceId = mNextDeviceId++;
    Device* device = new Device(fd, deviceId, String8(devicePath), identifier);  //这里根据前面获取的参数new成一个device,算是初步打开完成抽象成一个Device了

...

    // Load the configuration file for the device.
    loadConfigurationLocked(device);   // 这个比较重要  ,加载这个device的配置信息,后面将会根据这个配置来定义规则

... //又是一系列的判断初始化,其中比较重要的就是 device->classes 这个变量,代表了input 设备类型,是键盘,鼠标,触摸屏...

    // Register with epoll.
    struct epoll_event eventItem;   //注册epoll
    memset(&eventItem, 0, sizeof(eventItem));
    eventItem.events = EPOLLIN;
    eventItem.data.u32 = deviceId;
    if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, &eventItem)) {
        ALOGE("Could not add device fd to epoll instance.  errno=%d", errno);
        delete device;
        return -1;
    }

...

    addDeviceLocked(device);  //添加到 KeyedVector中

}

这个里面有很多原始打印,想深入理解的可以放开调试看看,这里不多做介绍,可以看下 加载配置那个函数loadConfigurationLocked:

void EventHub::loadConfigurationLocked(Device* device) {
    device->configurationFile = getInputDeviceConfigurationFilePathByDeviceIdentifier(   //根据上面获取到的一些设备信息,进一步去找config文件
            device->identifier, INPUT_DEVICE_CONFIGURATION_FILE_TYPE_CONFIGURATION);
    if (device->configurationFile.isEmpty()) {
        ALOGD("No input device configuration file found for device '%s'.",
                device->identifier.name.string());
    } else {
        status_t status = PropertyMap::load(device->configurationFile,  //找到之后 ,把这个device 的config file保存起来
                &device->configuration);
        if (status) {
            ALOGE("Error loading input device configuration file for device '%s'.  "
                    "Using default configuration.",
                    device->identifier.name.string());
        }
    }
}

接下来看怎么找对应config文件的,这里调用到了/frameworks/base/libs/androidfw/InputDevice.cpp中的:

String8 getInputDeviceConfigurationFilePathByDeviceIdentifier(
        const InputDeviceIdentifier& deviceIdentifier,
        InputDeviceConfigurationFileType type) {
    if (deviceIdentifier.vendor !=0 && deviceIdentifier.product != 0) {
        if (deviceIdentifier.version != 0) {
            // Try vendor product version.
            String8 versionPath(getInputDeviceConfigurationFilePathByName(
                    String8::format("Vendor_%04x_Product_%04x_Version_%04x",   //用前面获取的input device 的相关VID PID 来找文件
                            deviceIdentifier.vendor, deviceIdentifier.product,
                            deviceIdentifier.version),
                    type));
            if (!versionPath.isEmpty()) {
                return versionPath;
            }
        }

        // Try vendor product.
        String8 productPath(getInputDeviceConfigurationFilePathByName(
                String8::format("Vendor_%04x_Product_%04x",
                        deviceIdentifier.vendor, deviceIdentifier.product),
                type));
        if (!productPath.isEmpty()) {
            return productPath;
        }
    }

    // Try device name.
    return getInputDeviceConfigurationFilePathByName(deviceIdentifier.name, type);  //这个函数也在同文件中,就是到  path.setTo(getenv("ANDROID_ROOT"));  path.append("/usr/"); 也就是文件系统中/system/usr目录下去找
}

所以我们对一些输出设备需要把它的配置文件编译进系统,而且命名一般都是Vendor_%04x_Product_%04x.XXX 类型,这里就明白啦!

到这里getEvents应该差不多了,细节部分就需要另行细读代码了,当上面read到事件数组返回之后接下来就是初步处理了!

processEventsLocked:

上面getEvents返回了mEventBuffer之后,做初步的处理:

void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {

    for (const RawEvent* rawEvent = rawEvents; count;) {   遍历获取到的event数组
        int32_t type = rawEvent->type;
        size_t batchSize = 1;

        if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) {  //如果是常规的event事件
            int32_t deviceId = rawEvent->deviceId;
            while (batchSize < count) {

                if (rawEvent[batchSize].type >= EventHubInterface::FIRST_SYNTHETIC_EVENT
                        || rawEvent[batchSize].deviceId != deviceId ) {
                    break;
                }
                batchSize += 1;
            }
//#if DEBUG_RAW_EVENTS
            ALOGW("BatchSize: %d Count: %d", batchSize, count);
//#endif
            processEventsForDeviceLocked(deviceId, rawEvent, batchSize);  //把这次获取到的event数组中属于同一批次的,进一步处理,判定条件就是:常规event以及是属于同一设备
        } else {
            switch (rawEvent->type) {  //这里就是一些特殊的event类型了,上面有说到,打开设备的时候会有这个ADD事件
            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:
                ALOG_ASSERT(false); // can't happen
                break;
            }
        }
        count -= batchSize;   //如果再上面没有处理完event数组中的成员,那么依次继续
        rawEvent += batchSize;
    }
}

传进来的参数为 前面getEvents一定时间内获取到的event数组以及个数,原型定义在/frameworks/base/services/input/InputReader.h中:

    // The event queue.
    static const int EVENT_BUFFER_SIZE = 256;
    RawEvent mEventBuffer[EVENT_BUFFER_SIZE];
/*
 * A raw event as retrieved from the EventHub.
 */
struct RawEvent {
    nsecs_t when;
    int32_t deviceId;
    int32_t type;
    int32_t code;
    int32_t value;
};

这里可以看到 processEventsLocked 只是对get到的event做一个初步的分发处理,先看添加的事件类型.

可以看到在InputReader里面又来了一次addDeviceLocked  ,这个要更最上面getEvents中往下打开设备时addDeviceLocked 区分开来,不要给绕晕了哟~

void InputReader::addDeviceLocked(nsecs_t when, int32_t deviceId) {
    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
    if (deviceIndex >= 0) {
        ALOGW("Ignoring spurious device added event for deviceId %d.", deviceId);
        return;
    }

    InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(deviceId);  //这里取之前在EventHub中解析出来的设备相关参数
    uint32_t classes = mEventHub->getDeviceClasses(deviceId);  //这个上面有说到,在open设备时 初始化,代表类型

    ALOGW("jscese display in addDeviceLocked classes == 0x%x \n",classes);
    InputDevice* device = createDeviceLocked(deviceId, identifier, classes);  //这里又创建一个 InputDervice,这个就不继续跟进了,会根据classes 选择对应的事件处理map与当前的设备绑定
    device->configure(when, &mConfig, 0);
    device->reset(when);

    if (device->isIgnored()) {
        ALOGI("Device added: id=%d, name='%s' (ignored non-input device)", deviceId,
                identifier.name.string());
    } else {
        ALOGI("Device added: id=%d, name='%s', sources=0x%08x", deviceId,
                identifier.name.string(), device->getSources());
    }

    mDevices.add(deviceId, device); //添加这个input device
    bumpGenerationLocked();
}

看处理平常事件时接下来的处理:

void InputReader::processEventsForDeviceLocked(int32_t deviceId,
        const RawEvent* rawEvents, size_t count) {
    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
    if (deviceIndex < 0) {
        ALOGW("Discarding event for unknown deviceId %d.", deviceId);
        return;
    }

    InputDevice* device = mDevices.valueAt(deviceIndex); //这里根据id 取出上面添加进去的inputdevice
    if (device->isIgnored()) {
        //ALOGD("Discarding event for ignored deviceId %d.", deviceId);
        return;
    }

    device->process(rawEvents, count); //这里调用了一个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();  //这里有个map个数,这个也在上面提到过,在create时 会根据classes类型去匹配处理map,一般都是匹配一个
    for (const RawEvent* rawEvent = rawEvents; count--; rawEvent++) {   //遍历事件数组,依次去处理
#if DEBUG_RAW_EVENTS
        ALOGD("Input event: device=%d type=0x%04x code=0x%04x value=0x%08x when=%lld",
                rawEvent->deviceId, rawEvent->type, rawEvent->code, rawEvent->value,
                rawEvent->when);
#endif

        if (mDropUntilNextSync) {
            if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
                mDropUntilNextSync = false;
#if DEBUG_RAW_EVENTS
                ALOGD("Recovered from input event buffer overrun.");
#endif
            } else {
#if DEBUG_RAW_EVENTS
                ALOGD("Dropped input event while waiting for next input sync.");
#endif
            }
        } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) {
            ALOGI("Detected input event buffer overrun for device %s.", getName().string());
            mDropUntilNextSync = true;
            reset(rawEvent->when);
        } else {
            for (size_t i = 0; i < numMappers; i++) {
                InputMapper* mapper = mMappers[i];
                mapper->process(rawEvent);  //这里就是调用处理map的process 函数啦
            }
        }
    }
}

多次提到classes 设备类型,看下定义,在EventHub.h中:

/*//classes=0x80000004
 * 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 has a vibrator (supports FF_RUMBLE). */
    INPUT_DEVICE_CLASS_VIBRATOR      = 0x00000200,

    /* The input device is virtual (not a real device, not part of UI configuration). */
    INPUT_DEVICE_CLASS_VIRTUAL       = 0x40000000,

    /* The input device is external (not built-in). */
    INPUT_DEVICE_CLASS_EXTERNAL      = 0x80000000,
};

那么接下来需要处理的就是 定义的InputReader中的各个InputMap类了。

总结:

这里牵扯到了EventHub     InputReader      Device     InputDevice     InputMap

把关系理一下:

EventHub 中 会创建堆的Device  ,这个是根据/dev/input下面的设备文件 一一对应的,也就是kernel中注册的input设备.

InputReader 中会create多个 InputDevice ,这个是由Device中的参数为基础创建的,所以与EventHub中的Device 一一对应.

InputDevice 在创建的时候会根据 classes 来添加需要的InputMap,每一个不同的InputMapInputReader中定义,有很多种处理方式.

大体就是这么回事,这里关于InputReader部分就先分析到这里,再往下InputMap中的事件分发处理,后续分析~

时间: 2024-10-10 12:22:40

Linux/Android——Input系统之InputReader (七)的相关文章

Linux/Android——Input系统之InputMapper 处理 (八)

前文Linux/Android--Input系统之InputReader (七)介绍到了inputreader的运作流程,如何获取events到初步的分发,依次分析到InputMapper做第一步的处理. 前文有解析Mapper类型的依赖规则,不做重述.,这里单以触摸屏input_device 对应的SingleTouchInputMapper 为例. 撰写不易,转载需注明出处:http://blog.csdn.net/jscese/article/details/43561773本博文来自[

Linux/Android——Input系统之frameworks层InputManagerService (六)

上一篇Linux/Android--input系统之 kernel层 与 frameworks层交互 (五)中有介绍kernel层一下以及与android这边frameworks层之间的联系,算是打通android 应用层与 kernel驱动层,对整个input系统的学习是至关重要的,其中frameworks层只是简单记录了几个接入点,这里开始分析frameworks层的细节部分. 撰写不易,转载需注明出处:http://blog.csdn.net/jscese/article/details/

Linux/Android——input子系统核心

之前的博客有涉及到linux的input子系统,这里学习记录一下input模块. input子系统,作为管理输入设备与系统进行交互的中枢,任何的输入设备驱动都要通过input向内核注册其设备, 常用的输入设备也就是鼠标,键盘,触摸屏. 稍微细分一点整个输入体系,就是 硬件驱动层,input核心中转层,事件处理层.层次之间传递都以event事件的形式,这其中input连接上下层,分别提供接口. 之前有分析usbtouchscreen的驱动,也就是硬件驱动部分,这里简单记录一下input核心中转处理

Linux/Android——input子系统核心 (三)【转】

本文转载自:http://blog.csdn.net/jscese/article/details/42123673 之前的博客有涉及到linux的input子系统,这里学习记录一下input模块. input子系统,作为管理输入设备与系统进行交互的中枢,任何的输入设备驱动都要通过input向内核注册其设备, 常用的输入设备也就是鼠标,键盘,触摸屏. 稍微细分一点整个输入体系,就是 硬件驱动层,input核心中转层,事件处理层.层次之间传递都以event事件的形式,这其中input连接上下层,分

Linux/Android——input_handler之evdev

在前文Linux/Android--input子系统核心中概括了总体的结构,以及介绍了input核心的职责,其中有说道注册input设备时会去匹配已有的事件处理器handler, 而这个handler也是存放在一个链表里面的,这里介绍下input子系统中的事件处理input_handler机制. 撰写不易,转载需注明出处:http://blog.csdn.net/jscese/article/details/42238377#t6 evdev: /kernel/drivers/input下众多事

Linux/Android——input_handler之evdev (四)【转】

本文转载自: 在前文Linux/Android——input子系统核心 (三)中概括了总体的结构,以及介绍了input核心的职责,其中有说道注册input设备时会去匹配已有的事件处理器handler, 而这个handler也是存放在一个链表里面的,这里介绍下input子系统中的事件处理input_handler机制. 撰写不易,转载需注明出处:http://blog.csdn.net/jscese/article/details/42238377#t6 evdev: /kernel/driver

Linux input系统数据上报流程【转】

转自:https://segmentfault.com/a/1190000017255939 作为鸡生蛋系列文章,这里主要关注Linux input系统,主要为触摸事件上报流程. 读该文章最好有对linux驱动的入门知识.其实当你自己去分析了input系统后,再分析别的就相对很轻松了,linux里好多套路都差不多的. 本文例子以ft6236.c驱动为例, 当然你也可以用goodix或者别的触摸来分析.但是分析基于的内核版本用4.19.6(我写这篇文档时最新稳定版)(https://git.ker

iConvert Icons 图标转换生成利器,支持Windows, Mac OS X, Linux, iOS,和Android等系统

这是一款在线图标转换工具,生成的图标支持Windows, Mac OS X, Linux, iOS, 和 Android等主流系统. 可以上传图标文件转化成另一个平台下的图标文件,例如将windows系统下的icon图标格式转换成mac os下的icns图标格式,也可以通过上传图片从而生成你指定平台下的图标格式文件.例如上传png文件转换成windows系统下的icon图标文件. 官网在线应用地址:iConvert Icons 其也有app可供下载,但要付费. 原创文章,转载请注明: 转载自 h

热烈庆祝华清远见2014嵌入式系统(Linux&amp;Android)开发就业培训课程全面升级

近日,华清远见公开宣布:2014嵌入式系统 (Linux&Android)开发就业培训课程再次升级!据悉,华清远见如今已经持续10年,一直保持课程每年2次的更新的频率.华清远见的每 次课程更新都能够带给行业一些新的思路和方向,这次也不例外.通过知情人,我们了解到华清远见此次针对嵌入式培训和Anrdoid培训两个方向均有重大变 革. 首先,华清远见嵌入式系统 (Linux&Android)开发就业培训课程升级后主课程中全面采用自主研发的Cortex-A9 四核ARM硬件平台.这也是目前在嵌入