Linux/Android——Input系统之InputReader (七)【转】
本文转载自:http://blog.csdn.net/jscese/article/details/42739197
在前文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 charchar *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,每一个不同的InputMap 在InputReader中定义,有很多种处理方式.
大体就是这么回事,这里关于InputReader部分就先分析到这里,再往下InputMap中的事件分发处理,后续分析~
Linux/Android——Input系统之InputReader (七)【转】的更多相关文章
- Linux/Android——Input系统之InputMapper 处理 (八)【转】
本文转载自:http://blog.csdn.net/jscese/article/details/43561773 前文Linux/Android——Input系统之InputReader (七)介 ...
- Linux/Android——Input系统之frameworks层InputManagerService (六)【转】
本文转载自:http://blog.csdn.net/u013491946/article/details/72638954 版权声明:免责声明: 本人在此发文(包括但不限于汉字.拼音.拉丁字母)均为 ...
- Linux/Android——input系统之 kernel层 与 frameworks层交互 (五)【转】
本文转载自:http://blog.csdn.net/jscese/article/details/42291149 之前的四篇博文记录的都是linux中的input体系相关的东西,最底层以我调试的u ...
- input系统——android input系统
AndroidInput系统--JNI NativeInputManager InputManger InputReader AndroidInput系统--InputReader AndroidIn ...
- Linux/Android——input子系统核心 (三)【转】
本文转载自:http://blog.csdn.net/jscese/article/details/42123673 之前的博客有涉及到linux的input子系统,这里学习记录一下input模块. ...
- Linux 下Input系统应用编程实战
作者:杨源鑫(也是我们的校园代理) 经授权转载于公众号嵌入式开发圈,有些许修改. 什么是input子系统?不管是什么操作系统,都有一个程序用于管理各种输入设备,哪些是输入设备?比如,电脑键盘.鼠标,智 ...
- Linux & Android 多点触摸协议
Linux & Android 多点触摸协议 Android4.0多点触摸入门 1 KERNEL 对于触摸屏的驱动我们简单的划分为两个主要的部分,一个是注册,另一个是上报. 1.1 注册 单点 ...
- Linux/Android——input_handler之evdev (四) 【转】
转自:http://blog.csdn.net/u013491946/article/details/72638919 版权声明:免责声明: 本人在此发文(包括但不限于汉字.拼音.拉丁字母)均为随意敲 ...
- 图解Android - Android GUI 系统 (5) - Android的Event Input System
Android的用户输入处理 Android的用户输入系统获取用户按键(或模拟按键)输入,分发给特定的模块(Framework或应用程序)进行处理,它涉及到以下一些模块: Input Reader: ...
随机推荐
- Mybatis的一级二级缓存
Mybatis提供了缓存机制,可以减轻数据库的压力,提高性能 Mybatis的缓存分为两级:一个是一级缓存,一个二级缓存 一级缓存:即默认使用的缓存SqlSession级别的缓存,只在sqlsessi ...
- Linux内核0.11体系结构 ——《Linux内核完全注释》笔记打卡
0 总体介绍 一个完整的操作系统主要由4部分组成:硬件.操作系统内核.操作系统服务和用户应用程序,如图0.1所示.操作系统内核程序主要用于对硬件资源的抽象和访问调度. 图0.1 操作系统组成部分 内核 ...
- Python之布尔
Python之布尔(bool) 在计算机中的所用判断,都是用布尔的True和False来判断的. 条件成立:True ( 1 ) 条件不成立:False ( 0 ) 以下情况是False: [ ]:空 ...
- 当java web项目部署到服务器上时,无法将图片等媒体文件保存到服务器的最终奥义
今天,我在部署web项目至云服务器上(Tomcat8.0)时,突然发现我的应用,无法上传图片,视频等多媒体文件了,一再检查自己的代码逻辑没有问题之后,逐一排查,首先想到的就是看一下控制台打印的日志,日 ...
- 简单的python代码实现语音朗读
昨天女友生日,因为她一直对生日无感,所以我也就没有准备什么礼物.想起元旦前写的自动测试的脚本,添加了语音来提示测试和报告错误.灵机一动,为什么不用这个语音来庆祝她生日快乐呢?身为设计公司市场经理的她对 ...
- 集训第四周(高效算法设计)J题 (中途相遇法)
Description The SUM problem can be formulated as follows: given four lists A, B, C, D<tex2html_ ...
- Eclipse调试相关
Eclipse调试相关 F5 step into就是单步执行,遇到子函数就进入并且继续单步执行. F6 step over是在单步执行时,在函数内遇到子函数时不会进入子函数内单步执行,而是将子函数整个 ...
- bzoj2212 Tree Rotations
被BZOJ坑了一下午,原以为是我程序有问题一直WA,结果是我数组小了...为啥不给我RE!!! 线段树合并,对于逆序对而言,只能通过交换左右子树来达到,那么我们就可以想到对于一个结点而言,我们当然要取 ...
- Servlet处理日期
以下内容引用自http://wiki.jikexueyuan.com/project/servlet/handling-date.html: 使用Servlet的最重要的优势之一是可以使用核心Java ...
- Servlet中操作数据库
以下内容引用自http://wiki.jikexueyuan.com/project/servlet/database-access.html: 前提先新建数据库及插入模拟数据: create tab ...