Android触摸事件的传递(三)--输入系统EventHub

了解更多,移步Android触摸事件传递机制系列详解
EventHub的作用是将来源不同的各种信息,转化成为一种类型的信息,然后将这些信息提交到上层,给上层做处理

1 EventHub初始化

  • EventHub对象是在NativeInputManager构造函数中创建的.
  • EventHub构造函数中都做了些什么
  1. 初始化一些成员变量
  2. 创建epoll对象,EPOLL_SIZE_HINT = 8代表最大监听数为8.
  3. 创建inotify对象,监听/dev/input下设备节点的增删。
  4. 将mINotifyFd添加到epoll中,作为一个监控对象。
  5. 创建管道,将管道读取端的可读事件添加到epoll中。使epoll_wait()返回,唤醒InputReader线程。
EventHub::EventHub(void) :
        mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD), mNextDeviceId(1),
        mOpeningDevices(0), mClosingDevices(0),
        mNeedToSendFinishedDeviceScan(false),
        mNeedToReopenDevices(false)//不重新打开设备, mNeedToScanDevices(true),
        mPendingEventCount(0), mPendingEventIndex(0), mPendingINotify(false) {
    acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
    //创建epoll对象,mEpollFd为epoll对象的描述符
    mEpollFd = epoll_create(EPOLL_SIZE_HINT);
    LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance.  errno=%d", errno);
    //创建inotify对象,mINotifyFd为inotify对象的描述符
    mINotifyFd = inotify_init();
    //DEVICE_PATH值为"/dev/input",监听该目录下的设备节点创建与删除操作。通过read函数读取事件。
    int result = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);
    LOG_ALWAYS_FATAL_IF(result < 0, "Could not register INotify for %s.  errno=%d",
            DEVICE_PATH, errno);
 
    struct epoll_event eventItem;
    memset(&eventItem, 0, sizeof(eventItem));
    eventItem.events = EPOLLIN;    //监听可读事件
    eventItem.data.u32 = EPOLL_ID_INOTIFY;
    //EPOLL_CTL_ADD表示增加事件
    //epoll_ctl将事件监听添加到epoll对象中去。
    result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);
    LOG_ALWAYS_FATAL_IF(result != 0, "Could not add INotify to epoll instance.  errno=%d", errno);
 
    int wakeFds[2];
    result = pipe(wakeFds);
    LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe.  errno=%d", errno);
 
    mWakeReadPipeFd = wakeFds[0];
    mWakeWritePipeFd = wakeFds[1];
 
    result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);
    LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake read pipe non-blocking.  errno=%d",
            errno);
 
    result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK);
    LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake write pipe non-blocking.  errno=%d",
            errno);
 
    eventItem.data.u32 = EPOLL_ID_WAKE;
    result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem);
    LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake read pipe to epoll instance.  errno=%d",
            errno);
}

2 EventHub::getEvents()

  • EventHub的主要工作都是在getEvents函数中,InputReaderThread通过循环调用EventHub的getEvents()函数获取输入事件。getEvents中做了些什么,现在看一看。

  • 通过epoll_wait,得到相应设备文件发生变化的事件

  • 根据事件类型获取相应设备id

  • 从相应设备文件中读取内容,对读出内容进行判断,判断是否有错误,是否可用

  • 判断无误,对事件内容进行包装,加入到返回事件中

  • getEvents()是阻塞的,只有当有事件或者被wake才会被唤醒向下执行。

size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
    AutoMutex _l(mLock); //加锁

    struct input_event readBuffer[bufferSize];
    RawEvent* event = buffer; //原始事件
    size_t capacity = bufferSize; //容量大小为256
    bool awoken = false;
    for (;;) {
        nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
        ...

        if (mNeedToScanDevices) {
            mNeedToScanDevices = false;
            scanDevicesLocked(); //扫描设备【见小节2.2】
            mNeedToSendFinishedDeviceScan = true;
        }

        while (mOpeningDevices != NULL) {
            Device* device = mOpeningDevices;
            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;
            }
        }
        ...

        bool deviceChanged = false;
      while (mPendingEventIndex < mPendingEventCount) {
         //从mPendingEventItems读取事件项
         const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];
        .....
       //获取设备ID所对应的device
       ssize_t deviceIndex = mDevices.indexOfKey(eventItem.data.u32);
     if (eventItem.events & EPOLLIN) {
         //从设备不断读取事件,放入到readBuffer
         int32_t readSize = read(device->fd, readBuffer,
                        sizeof(struct input_event) * capacity);
        if (readSize == 0 || (readSize < 0 && errno == ENODEV)) {
           // 设备被移除,关闭设备
           deviceChanged = true;
           closeDeviceLocked(device);
         } else if (readSize < 0) {
             //无法获得事件
             if (errno != EAGAIN && errno != EINTR) {
                 ALOGW("could not get event (errno=%d)", errno);
             }
         } else if ((readSize % sizeof(struct input_event)) != 0) {
            //获得事件的大小非事件类型整数倍
            ALOGE("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++) {
              //获取readBuffer的数据
               struct input_event& iev = readBuffer[i];
               if (iev.type == EV_MSC) {
                 if (iev.code == MSC_ANDROID_TIME_SEC) {
                     device->timestampOverrideSec = iev.value;
                     continue;
                  } else if (iev.code == MSC_ANDROID_TIME_USEC) {
                     device->timestampOverrideUsec = iev.value;
                     continue;
                  }
               }
              //事件时间相关计算,时间的错误可能会导致ANR和一些bug。这里采取一系列的防范 
            //将input_event信息, 封装成RawEvent
               .........
             event->deviceId = deviceId;
             event->type = iev.type;
             event->code = iev.code;
             event->value = iev.value;
             event += 1;
             capacity -= 1;
          }
        if (capacity == 0) {
          //每到我们计算完一个事件,capacity就会减1,如果为0。则表示  结果缓冲区已经满了,
      //需要重置开始读取时间的索引值,来读取下一个事件迭代                    
           mPendingEventIndex -= 1;
           break;
      }
 } 
    //表明读到事件了,跳出循环
    if (event != buffer || awoken) {
            break;
     }
     mPendingEventIndex = 0;
     //等待input事件的到来
     int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
       if (pollResult == 0) {
          mPendingEventCount = 0;
          break;
       }
      //判断是否有事件发生
       if (pollResult < 0) {
          mPendingEventCount = 0;
        } else {
            //产生的事件的数目
          mPendingEventCount = size_t(pollResult);
        }
    }
    //返回所读取的事件个数
    return event - buffer;
}

EventHub采用INotify + epoll机制实现监听目录/dev/input下的设备节点,经过EventHub将input_event结构体 + deviceId 转换成RawEvent结构体,如下:

3 RawEvent

[-> InputEventReader.h]

struct input_event {
 struct timeval time; //事件发生的时间点
 __u16 type;
 __u16 code;
 __s32 value;
};

struct RawEvent {
    nsecs_t when; //事件发生的时间店
    int32_t deviceId; //产生事件的设备Id
    int32_t type; // 事件类型
    int32_t code;
    int32_t value;
};

此处事件类型:

  • DEVICE_ADDED(添加)
  • DEVICE_REMOVED(删除)
  • FINISHED_DEVICE_SCAN(扫描完成)
  • type<FIRST_SYNTHETIC_EVENT(其他事件)
    getEvents()已完成转换事件转换工作,

4 设备扫描

4.1 scanDevicesLocked

void EventHub::scanDevicesLocked() {
    //此处DEVICE_PATH="/dev/input"【见小节2.3】
    status_t res = scanDirLocked(DEVICE_PATH);
    ...
}

4.2 scanDirLocked

status_t EventHub::scanDirLocked(const char *dirname)
{
    char devname[PATH_MAX];
    char *filename;
    DIR *dir;
    struct dirent *de;
    dir = opendir(dirname);

    strcpy(devname, dirname);
    filename = devname + strlen(devname);
    *filename++ = '/';
    //读取/dev/input/目录下所有的设备节点
    while((de = readdir(dir))) {
        if(de->d_name[0] == '.' &&
           (de->d_name[1] == '\0' ||
            (de->d_name[1] == '.' && de->d_name[2] == '\0')))
            continue;
        strcpy(filename, de->d_name);
        //打开相应的设备节点【2.2.3】
        openDeviceLocked(devname);
    }
    closedir(dir);
    return 0;
}

4.3 openDeviceLocked

status_t EventHub::openDeviceLocked(const char *devicePath) {
    char buffer[80];
    //打开设备文件
    int fd = open(devicePath, O_RDWR | O_CLOEXEC);
    InputDeviceIdentifier identifier;
    //获取设备名
    if(ioctl(fd, EVIOCGNAME(sizeof(buffer) - 1), &buffer) < 1){
    } else {
        buffer[sizeof(buffer) - 1] = '\0';
        identifier.name.setTo(buffer);
    }

    identifier.bus = inputId.bustype;
    identifier.product = inputId.product;
    identifier.vendor = inputId.vendor;
    identifier.version = inputId.version;

    //获取设备物理地址
    if(ioctl(fd, EVIOCGPHYS(sizeof(buffer) - 1), &buffer) < 1) {
    } else {
        buffer[sizeof(buffer) - 1] = '\0';
        identifier.location.setTo(buffer);
    }

    //获取设备唯一ID
    if(ioctl(fd, EVIOCGUNIQ(sizeof(buffer) - 1), &buffer) < 1) {
    } else {
        buffer[sizeof(buffer) - 1] = '\0';
        identifier.uniqueId.setTo(buffer);
    }
    //将identifier信息填充到fd
    assignDescriptorLocked(identifier);
    //设置fd为非阻塞方式
    fcntl(fd, F_SETFL, O_NONBLOCK);

    //获取设备ID,分配设备对象内存
    int32_t deviceId = mNextDeviceId++;
    Device* device = new Device(fd, deviceId, String8(devicePath), identifier);
    ...

    //注册epoll
    struct epoll_event eventItem;
    memset(&eventItem, 0, sizeof(eventItem));
    eventItem.events = EPOLLIN;
    if (mUsingEpollWakeup) {
        eventItem.events |= EPOLLWAKEUP;
    }
    eventItem.data.u32 = deviceId;
    if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, &eventItem)) {
        delete device; //添加失败则删除该设备
        return -1;
    }
    ...
    
    addDeviceLocked(device);
}

4.4 addDeviceLocked

void EventHub::addDeviceLocked(Device* device) {
    mDevices.add(device->id, device); //添加到mDevices队列
    device->next = mOpeningDevices;
    mOpeningDevices = device;
}

介绍了EventHub从设备节点获取事件的流程,当收到事件后接下里便开始处理事件。

参考

Android 输入系统(二)EventHub
Android系统源码分析-事件收集
Input系统—InputReader线程

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 序言 最近在看Android触摸屏事件相关的源码,为了对整个事件体系的了解,所以对事件相关,从事件的产生,写入设备...
    Jensen95阅读 4,093评论 0 3
  • 前言 之前项目中涉及到对于Android事件的转发处理相关的需求,因此对于这部分的系统源码进行了分析,从写入设备文...
    Jensen95阅读 4,028评论 0 1
  • 必备的理论基础 1.操作系统作用: 隐藏丑陋复杂的硬件接口,提供良好的抽象接口。 管理调度进程,并将多个进程对硬件...
    drfung阅读 8,989评论 0 5
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 13,902评论 1 32
  • 暑假去银泰城溜冰。由于个子高,又没有任何轮滑经验,当我第一次穿上锋利的冰刀鞋站在光滑的冰面上时,霎时体验到了“如履...
    一条后知后觉的鱼阅读 3,483评论 0 0

友情链接更多精彩内容