• 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中,函数很长,列出几个重要的:

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

    scanDevicesLocked:

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

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

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

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

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

    [objc] view plain copy
     
    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中的:

    [objc] view plain copy
     
    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.   
    17.         // Try vendor product.  
    18.         String8 productPath(getInputDeviceConfigurationFilePathByName(  
    19.                 String8::format("Vendor_%04x_Product_%04x",  
    20.                         deviceIdentifier.vendor, deviceIdentifier.product),  
    21.                 type));  
    22.         if (!productPath.isEmpty()) {  
    23.             return productPath;  
    24.         }  
    25.     }  
    26.   
    27.     // Try device name.  
    28.     return getInputDeviceConfigurationFilePathByName(deviceIdentifier.name, type);  //这个函数也在同文件中,就是到  path.setTo(getenv("ANDROID_ROOT"));  path.append("/usr/"); 也就是文件系统中/system/usr目录下去找  
    29. }  


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

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

    processEventsLocked:

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

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


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

    [objc] view plain copy
     
    1. // The event queue.  
    2. static const int EVENT_BUFFER_SIZE = 256;  
    3. RawEvent mEventBuffer[EVENT_BUFFER_SIZE];  
    [objc] view plain copy
     
    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 区分开来,不要给绕晕了哟~

    [objc] view plain copy
     
    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.   
    8.     InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(deviceId);  //这里取之前在EventHub中解析出来的设备相关参数  
    9.     uint32_t classes = mEventHub->getDeviceClasses(deviceId);  //这个上面有说到,在open设备时 初始化,代表类型  
    10.   
    11.     ALOGW("jscese display in addDeviceLocked classes == 0x%x  ",classes);  
    12.     InputDevice* device = createDeviceLocked(deviceId, identifier, classes);  //这里又创建一个 InputDervice,这个就不继续跟进了,会根据classes 选择对应的事件处理map与当前的设备绑定  
    13.     device->configure(when, &mConfig, 0);  
    14.     device->reset(when);  
    15.   
    16.     if (device->isIgnored()) {  
    17.         ALOGI("Device added: id=%d, name='%s' (ignored non-input device)", deviceId,  
    18.                 identifier.name.string());  
    19.     } else {  
    20.         ALOGI("Device added: id=%d, name='%s', sources=0x%08x", deviceId,  
    21.                 identifier.name.string(), device->getSources());  
    22.     }  
    23.   
    24.     mDevices.add(deviceId, device); //添加这个input device  
    25.     bumpGenerationLocked();  
    26. }  

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

    [objc] view plain copy
     
    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.   
    9.     InputDevice* device = mDevices.valueAt(deviceIndex); //这里根据id 取出上面添加进去的inputdevice  
    10.     if (device->isIgnored()) {  
    11.         //ALOGD("Discarding event for ignored deviceId %d.", deviceId);  
    12.         return;  
    13.     }  
    14.   
    15.     device->process(rawEvents, count); //这里调用了一个process的函数  
    16. }  


    继续看InputDevice的process:

    [objc] view plain copy
     
    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.   
    15.         if (mDropUntilNextSync) {  
    16.             if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {  
    17.                 mDropUntilNextSync = false;  
    18. #if DEBUG_RAW_EVENTS  
    19.                 ALOGD("Recovered from input event buffer overrun.");  
    20. #endif  
    21.             } else {  
    22. #if DEBUG_RAW_EVENTS  
    23.                 ALOGD("Dropped input event while waiting for next input sync.");  
    24. #endif  
    25.             }  
    26.         } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) {  
    27.             ALOGI("Detected input event buffer overrun for device %s.", getName().string());  
    28.             mDropUntilNextSync = true;  
    29.             reset(rawEvent->when);  
    30.         } else {  
    31.             for (size_t i = 0; i < numMappers; i++) {  
    32.                 InputMapper* mapper = mMappers[i];  
    33.                 mapper->process(rawEvent);  //这里就是调用处理map的process 函数啦  
    34.             }  
    35.         }  
    36.     }  
    37. }  

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

    [objc] view plain copy
     
    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.   
    8.     /* The input device is an alpha-numeric keyboard (not just a dial pad). */  
    9.     INPUT_DEVICE_CLASS_ALPHAKEY      = 0x00000002,  
    10.   
    11.     /* The input device is a touchscreen or a touchpad (either single-touch or multi-touch). */  
    12.     INPUT_DEVICE_CLASS_TOUCH         = 0x00000004,  
    13.   
    14.     /* The input device is a cursor device such as a trackball or mouse. */  
    15.     INPUT_DEVICE_CLASS_CURSOR        = 0x00000008,  
    16.   
    17.     /* The input device is a multi-touch touchscreen. */  
    18.     INPUT_DEVICE_CLASS_TOUCH_MT      = 0x00000010,  
    19.   
    20.     /* The input device is a directional pad (implies keyboard, has DPAD keys). */  
    21.     INPUT_DEVICE_CLASS_DPAD          = 0x00000020,  
    22.   
    23.     /* The input device is a gamepad (implies keyboard, has BUTTON keys). */  
    24.     INPUT_DEVICE_CLASS_GAMEPAD       = 0x00000040,  
    25.   
    26.     /* The input device has switches. */  
    27.     INPUT_DEVICE_CLASS_SWITCH        = 0x00000080,  
    28.   
    29.     /* The input device is a joystick (implies gamepad, has joystick absolute axes). */  
    30.     INPUT_DEVICE_CLASS_JOYSTICK      = 0x00000100,  
    31.   
    32.     /* The input device has a vibrator (supports FF_RUMBLE). */  
    33.     INPUT_DEVICE_CLASS_VIBRATOR      = 0x00000200,  
    34.   
    35.     /* The input device is virtual (not a real device, not part of UI configuration). */  
    36.     INPUT_DEVICE_CLASS_VIRTUAL       = 0x40000000,  
    37.   
    38.     /* The input device is external (not built-in). */  
    39.     INPUT_DEVICE_CLASS_EXTERNAL      = 0x80000000,  
    40. };  

    那么接下来需要处理的就是 定义的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中的事件分发处理,后续分析~

  • 相关阅读:
    LeetCode_143. 重排链表
    LeetCode_844. 比较含退格的字符串
    LeetCode116. 填充每个节点的下一个右侧节点指针
    1002. 查找常用字符
    贝叶斯定理(Bayes' theorem)的理解笔记
    汉明码
    超级好用 音乐可视化软件-Specinker
    安卓安装aidlearning
    windows下gcc的安装和使用
    ubuntu桌面配置
  • 原文地址:https://www.cnblogs.com/zzb-Dream-90Time/p/7813231.html
Copyright © 2020-2023  润新知