本文转载自: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中,函数很长,列出几个重要的:

  1. size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
  2. ...
  3. struct input_event readBuffer[bufferSize];
  4. RawEvent* event = buffer;
  5. ...
  6. if (mNeedToScanDevices) {  //这个在EventHub初始时为true
  7. mNeedToScanDevices = false;
  8. scanDevicesLocked();  // 扫描去打开设备,后面跟进去看
  9. mNeedToSendFinishedDeviceScan = true;
  10. }
  11. while (mOpeningDevices != NULL) {  //这个指针有指向,代表上面打开了某些input设备
  12. Device* device = mOpeningDevices;    //  初始化一个添加event,type 为DEVICE_ADDED
  13. ALOGV("Reporting device opened: id=%d, name=%s\n",
  14. device->id, device->path.string());
  15. mOpeningDevices = device->next;
  16. event->when = now;
  17. event->deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
  18. event->type = DEVICE_ADDED;
  19. event += 1;
  20. mNeedToSendFinishedDeviceScan = true;
  21. if (--capacity == 0) {
  22. break;
  23. }
  24. }
  25. ...
  26. while (mPendingEventIndex < mPendingEventCount) {  //这个是为处理多个input event 做的一个epoll_event 类型数组
  27. const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];
  28. Device* device = mDevices.valueAt(deviceIndex)  ; // 上面会把打开的device 加入到 这个Vector
  29. if (eventItem.events & EPOLLIN) {
  30. int32_t readSize = read(device->fd, readBuffer,   //这个之前有提到过。最终会调用到evdev中的read
  31. sizeof(struct input_event) * capacity);
  32. ...
  33. for (size_t i = 0; i < count; i++) {
  34. const struct input_event& iev = readBuffer[i];   //这里把上面读到的input_event 转化过这里的RawEvent
  35. event->deviceId = deviceId;
  36. event->type = iev.type;
  37. event->code = iev.code;
  38. event->value = iev.value;
  39. event += 1;
  40. }
  41. ...
  42. int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis); //用于等待监测是否有事件可读
  43. ...
  44. }
  45. // All done, return the number of events we read.
  46. return event - buffer;   //指针相减,这个数组的元素个数
  47. }

scanDevicesLocked:

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

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

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

  1. status_t EventHub::openDeviceLocked(const charchar *devicePath) {
  2. char buffer[80];
  3. ALOGV("Opening device: %s", devicePath);
  4. int fd = open(devicePath, O_RDWR | O_CLOEXEC);
  5. if(fd < 0) {
  6. ALOGE("could not open %s, %s\n", devicePath, strerror(errno));
  7. return -1;
  8. }
  9. ...  //  无非是根据打开的fd获取一些相关参数信息
  10. // Allocate device.  (The device object takes ownership of the fd at this point.)
  11. int32_t deviceId = mNextDeviceId++;
  12. Device* device = new Device(fd, deviceId, String8(devicePath), identifier);  //这里根据前面获取的参数new成一个device,算是初步打开完成抽象成一个Device了
  13. ...
  14. // Load the configuration file for the device.
  15. loadConfigurationLocked(device);   // 这个比较重要  ,加载这个device的配置信息,后面将会根据这个配置来定义规则
  16. ... //又是一系列的判断初始化,其中比较重要的就是 device->classes 这个变量,代表了input 设备类型,是键盘,鼠标,触摸屏...
  17. // Register with epoll.
  18. struct epoll_event eventItem;   //注册epoll
  19. memset(&eventItem, 0, sizeof(eventItem));
  20. eventItem.events = EPOLLIN;
  21. eventItem.data.u32 = deviceId;
  22. if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, &eventItem)) {
  23. ALOGE("Could not add device fd to epoll instance.  errno=%d", errno);
  24. delete device;
  25. return -1;
  26. }
  27. ...
  28. addDeviceLocked(device);  //添加到 KeyedVector中
  29. }

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

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

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

  1. String8 getInputDeviceConfigurationFilePathByDeviceIdentifier(
  2. const InputDeviceIdentifier& deviceIdentifier,
  3. InputDeviceConfigurationFileType type) {
  4. if (deviceIdentifier.vendor !=0 && deviceIdentifier.product != 0) {
  5. if (deviceIdentifier.version != 0) {
  6. // Try vendor product version.
  7. String8 versionPath(getInputDeviceConfigurationFilePathByName(
  8. String8::format("Vendor_%04x_Product_%04x_Version_%04x",   //用前面获取的input device 的相关VID PID 来找文件
  9. deviceIdentifier.vendor, deviceIdentifier.product,
  10. deviceIdentifier.version),
  11. type));
  12. if (!versionPath.isEmpty()) {
  13. return versionPath;
  14. }
  15. }
  16. // Try vendor product.
  17. String8 productPath(getInputDeviceConfigurationFilePathByName(
  18. String8::format("Vendor_%04x_Product_%04x",
  19. deviceIdentifier.vendor, deviceIdentifier.product),
  20. type));
  21. if (!productPath.isEmpty()) {
  22. return productPath;
  23. }
  24. }
  25. // Try device name.
  26. return getInputDeviceConfigurationFilePathByName(deviceIdentifier.name, type);  //这个函数也在同文件中,就是到  path.setTo(getenv("ANDROID_ROOT"));  path.append("/usr/"); 也就是文件系统中/system/usr目录下去找
  27. }

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

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

processEventsLocked:

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

  1. void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {
  2. for (const RawEvent* rawEvent = rawEvents; count;) {   遍历获取到的event数组
  3. int32_t type = rawEvent->type;
  4. size_t batchSize = 1;
  5. if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) {  //如果是常规的event事件
  6. int32_t deviceId = rawEvent->deviceId;
  7. while (batchSize < count) {
  8. if (rawEvent[batchSize].type >= EventHubInterface::FIRST_SYNTHETIC_EVENT
  9. || rawEvent[batchSize].deviceId != deviceId ) {
  10. break;
  11. }
  12. batchSize += 1;
  13. }
  14. //#if DEBUG_RAW_EVENTS
  15. ALOGW("BatchSize: %d Count: %d", batchSize, count);
  16. //#endif
  17. processEventsForDeviceLocked(deviceId, rawEvent, batchSize);  //把这次获取到的event数组中属于同一批次的,进一步处理,判定条件就是:常规event以及是属于同一设备
  18. } else {
  19. switch (rawEvent->type) {  //这里就是一些特殊的event类型了,上面有说到,打开设备的时候会有这个ADD事件
  20. case EventHubInterface::DEVICE_ADDED:
  21. addDeviceLocked(rawEvent->when, rawEvent->deviceId);
  22. break;
  23. case EventHubInterface::DEVICE_REMOVED:
  24. removeDeviceLocked(rawEvent->when, rawEvent->deviceId);
  25. break;
  26. case EventHubInterface::FINISHED_DEVICE_SCAN:
  27. handleConfigurationChangedLocked(rawEvent->when);
  28. break;
  29. default:
  30. ALOG_ASSERT(false); // can't happen
  31. break;
  32. }
  33. }
  34. count -= batchSize;   //如果再上面没有处理完event数组中的成员,那么依次继续
  35. rawEvent += batchSize;
  36. }
  37. }

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

  1. // The event queue.
  2. static const int EVENT_BUFFER_SIZE = 256;
  3. RawEvent mEventBuffer[EVENT_BUFFER_SIZE];
  1. /*
  2. * A raw event as retrieved from the EventHub.
  3. */
  4. struct RawEvent {
  5. nsecs_t when;
  6. int32_t deviceId;
  7. int32_t type;
  8. int32_t code;
  9. int32_t value;
  10. };

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

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

  1. void InputReader::addDeviceLocked(nsecs_t when, int32_t deviceId) {
  2. ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
  3. if (deviceIndex >= 0) {
  4. ALOGW("Ignoring spurious device added event for deviceId %d.", deviceId);
  5. return;
  6. }
  7. InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(deviceId);  //这里取之前在EventHub中解析出来的设备相关参数
  8. uint32_t classes = mEventHub->getDeviceClasses(deviceId);  //这个上面有说到,在open设备时 初始化,代表类型
  9. ALOGW("jscese display in addDeviceLocked classes == 0x%x \n",classes);
  10. InputDevice* device = createDeviceLocked(deviceId, identifier, classes);  //这里又创建一个 InputDervice,这个就不继续跟进了,会根据classes 选择对应的事件处理map与当前的设备绑定
  11. device->configure(when, &mConfig, 0);
  12. device->reset(when);
  13. if (device->isIgnored()) {
  14. ALOGI("Device added: id=%d, name='%s' (ignored non-input device)", deviceId,
  15. identifier.name.string());
  16. } else {
  17. ALOGI("Device added: id=%d, name='%s', sources=0x%08x", deviceId,
  18. identifier.name.string(), device->getSources());
  19. }
  20. mDevices.add(deviceId, device); //添加这个input device
  21. bumpGenerationLocked();
  22. }

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

  1. void InputReader::processEventsForDeviceLocked(int32_t deviceId,
  2. const RawEvent* rawEvents, size_t count) {
  3. ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
  4. if (deviceIndex < 0) {
  5. ALOGW("Discarding event for unknown deviceId %d.", deviceId);
  6. return;
  7. }
  8. InputDevice* device = mDevices.valueAt(deviceIndex); //这里根据id 取出上面添加进去的inputdevice
  9. if (device->isIgnored()) {
  10. //ALOGD("Discarding event for ignored deviceId %d.", deviceId);
  11. return;
  12. }
  13. device->process(rawEvents, count); //这里调用了一个process的函数
  14. }

继续看InputDevice的process:

  1. void InputDevice::process(const RawEvent* rawEvents, size_t count) {
  2. // Process all of the events in order for each mapper.
  3. // We cannot simply ask each mapper to process them in bulk because mappers may
  4. // have side-effects that must be interleaved.  For example, joystick movement events and
  5. // gamepad button presses are handled by different mappers but they should be dispatched
  6. // in the order received.
  7. size_t numMappers = mMappers.size();  //这里有个map个数,这个也在上面提到过,在create时 会根据classes类型去匹配处理map,一般都是匹配一个
  8. for (const RawEvent* rawEvent = rawEvents; count--; rawEvent++) {   //遍历事件数组,依次去处理
  9. #if DEBUG_RAW_EVENTS
  10. ALOGD("Input event: device=%d type=0x%04x code=0x%04x value=0x%08x when=%lld",
  11. rawEvent->deviceId, rawEvent->type, rawEvent->code, rawEvent->value,
  12. rawEvent->when);
  13. #endif
  14. if (mDropUntilNextSync) {
  15. if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
  16. mDropUntilNextSync = false;
  17. #if DEBUG_RAW_EVENTS
  18. ALOGD("Recovered from input event buffer overrun.");
  19. #endif
  20. } else {
  21. #if DEBUG_RAW_EVENTS
  22. ALOGD("Dropped input event while waiting for next input sync.");
  23. #endif
  24. }
  25. } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) {
  26. ALOGI("Detected input event buffer overrun for device %s.", getName().string());
  27. mDropUntilNextSync = true;
  28. reset(rawEvent->when);
  29. } else {
  30. for (size_t i = 0; i < numMappers; i++) {
  31. InputMapper* mapper = mMappers[i];
  32. mapper->process(rawEvent);  //这里就是调用处理map的process 函数啦
  33. }
  34. }
  35. }
  36. }

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

  1. /*//classes=0x80000004
  2. * Input device classes.
  3. */
  4. enum {
  5. /* The input device is a keyboard or has buttons. */
  6. INPUT_DEVICE_CLASS_KEYBOARD      = 0x00000001,
  7. /* The input device is an alpha-numeric keyboard (not just a dial pad). */
  8. INPUT_DEVICE_CLASS_ALPHAKEY      = 0x00000002,
  9. /* The input device is a touchscreen or a touchpad (either single-touch or multi-touch). */
  10. INPUT_DEVICE_CLASS_TOUCH         = 0x00000004,
  11. /* The input device is a cursor device such as a trackball or mouse. */
  12. INPUT_DEVICE_CLASS_CURSOR        = 0x00000008,
  13. /* The input device is a multi-touch touchscreen. */
  14. INPUT_DEVICE_CLASS_TOUCH_MT      = 0x00000010,
  15. /* The input device is a directional pad (implies keyboard, has DPAD keys). */
  16. INPUT_DEVICE_CLASS_DPAD          = 0x00000020,
  17. /* The input device is a gamepad (implies keyboard, has BUTTON keys). */
  18. INPUT_DEVICE_CLASS_GAMEPAD       = 0x00000040,
  19. /* The input device has switches. */
  20. INPUT_DEVICE_CLASS_SWITCH        = 0x00000080,
  21. /* The input device is a joystick (implies gamepad, has joystick absolute axes). */
  22. INPUT_DEVICE_CLASS_JOYSTICK      = 0x00000100,
  23. /* The input device has a vibrator (supports FF_RUMBLE). */
  24. INPUT_DEVICE_CLASS_VIBRATOR      = 0x00000200,
  25. /* The input device is virtual (not a real device, not part of UI configuration). */
  26. INPUT_DEVICE_CLASS_VIRTUAL       = 0x40000000,
  27. /* The input device is external (not built-in). */
  28. INPUT_DEVICE_CLASS_EXTERNAL      = 0x80000000,
  29. };

那么接下来需要处理的就是 定义的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 (七)【转】的更多相关文章

  1. Linux/Android——Input系统之InputMapper 处理 (八)【转】

    本文转载自:http://blog.csdn.net/jscese/article/details/43561773 前文Linux/Android——Input系统之InputReader (七)介 ...

  2. Linux/Android——Input系统之frameworks层InputManagerService (六)【转】

    本文转载自:http://blog.csdn.net/u013491946/article/details/72638954 版权声明:免责声明: 本人在此发文(包括但不限于汉字.拼音.拉丁字母)均为 ...

  3. Linux/Android——input系统之 kernel层 与 frameworks层交互 (五)【转】

    本文转载自:http://blog.csdn.net/jscese/article/details/42291149 之前的四篇博文记录的都是linux中的input体系相关的东西,最底层以我调试的u ...

  4. input系统——android input系统

    AndroidInput系统--JNI NativeInputManager InputManger InputReader AndroidInput系统--InputReader AndroidIn ...

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

    本文转载自:http://blog.csdn.net/jscese/article/details/42123673 之前的博客有涉及到linux的input子系统,这里学习记录一下input模块. ...

  6. Linux 下Input系统应用编程实战

    作者:杨源鑫(也是我们的校园代理) 经授权转载于公众号嵌入式开发圈,有些许修改. 什么是input子系统?不管是什么操作系统,都有一个程序用于管理各种输入设备,哪些是输入设备?比如,电脑键盘.鼠标,智 ...

  7. Linux & Android 多点触摸协议

    Linux & Android 多点触摸协议 Android4.0多点触摸入门 1 KERNEL 对于触摸屏的驱动我们简单的划分为两个主要的部分,一个是注册,另一个是上报. 1.1 注册 单点 ...

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

    转自:http://blog.csdn.net/u013491946/article/details/72638919 版权声明:免责声明: 本人在此发文(包括但不限于汉字.拼音.拉丁字母)均为随意敲 ...

  9. 图解Android - Android GUI 系统 (5) - Android的Event Input System

    Android的用户输入处理 Android的用户输入系统获取用户按键(或模拟按键)输入,分发给特定的模块(Framework或应用程序)进行处理,它涉及到以下一些模块: Input Reader: ...

随机推荐

  1. linux通用makefile文件

    OUTPUT_DLL := libmy.so LIBS :=-L../public/lib/x64/linux -lzookeeper_mt -lcurl \ -lfreetype -lopencv_ ...

  2. js 小练习

    js 学习之路代码记录 js 加载时间线 1.创建Document对象,开始解析web页面.解析HTML元素和他们的文本内容后添加Element对象和Text节点到文档中.这个阶段document.r ...

  3. Java 集合相关

    对整个体系做一个记录,并不涉及详细应用 Object类 1. 重写toString方法 System.out.println可以打印任何对象在于Object类拥有一个方法 public String ...

  4. 【分治】输出前k大的数

    描述 给定一个数组,统计前k大的数并且把这k个数从大到小输出. 输入第一行包含一个整数n,表示数组的大小.n < 100000.第二行包含n个整数,表示数组的元素,整数之间以一个空格分开.每个整 ...

  5. Linux 文件与目录结构

    [Linux文件] Linux 系统中一切皆文件. [Linux目录结构] --/bin 是Binary的缩写, 这个目录存放着最经常使用的命令. --/sbin s就是Super User的意思,这 ...

  6. js判断对象是否为空对象的几种方法

    1.将json对象转化为json字符串,再判断该字符串是否为"{}" var data = {}; var b = (JSON.stringify(data) == "{ ...

  7. 解方程(codevs 3732)

    题目描述 已知多项式方程: a0+a1x+a2x^2+..+anx^n=0 求这个方程在[1, m ] 内的整数解(n 和m 均为正整数) 输入输出格式 输入格式: 输入文件名为equation .i ...

  8. LOJ#539. 「LibreOJ NOIP Round #1」旅游路线

    n<=100,m<=1000的图,在此图上用油箱容量C<=1e5的车来旅行,旅行时,走一条边会耗一单伟油,在点i时,若油量<ci,则可以把油以pi的价格补到ci,pi<= ...

  9. 封装HttpURLConnection

    package com.pingyijinren.test; import java.io.BufferedReader; import java.io.InputStream; import jav ...

  10. 51nod 1010 只包含因子2 3 5的数 && poj - 1338 Ugly Numbers(打表)

    http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1010 http://poj.org/problem?id=1338 首先 ...