请选择 进入手机版 | 继续访问电脑版
MSIPO技术圈 首页 IT技术 查看内容

[Android 13]Input系列--EventHub获取事件

2023-07-13

hongxi.zhu 2023-7-12
Android T

从前面inputflinger的启动分析中,我们知道事件来源是在EventHub::getEvents, 所以我们重点看下这个方法的流程来了解事件是如何从驱动上报中获取的。

EventHub::getEvents

frameworks/native/services/inputflinger/reader/EventHub.cpp

size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
    ALOG_ASSERT(bufferSize >= 1);

    std::scoped_lock _l(mLock);
    //创建一个input_event数组,用于存放从epoll中读取到的input_events
    struct input_event readBuffer[bufferSize];
    //buffer是inputReader传入的RawEvent数组首地址,数组大小为256,将事件构造成RawEvent并装入后返回给inputReader
    //用这里把数组地址赋给event指针,后续使用这个指针操作这个数组
    RawEvent* event = buffer; //传入的RawEvent数组首地址
    //一次处理事件的最大容量为256个
    size_t capacity = bufferSize;
    bool awoken = false;
    for (;;) {
        nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);  //获取当前时间戳

		//处理有关设备状态变化的逻辑
        // Reopen input devices if needed.
        if (mNeedToReopenDevices) {
            mNeedToReopenDevices = false;

            ALOGI("Reopening all input devices due to a configuration change.");

            closeAllDevicesLocked();
            mNeedToScanDevices = true;
            break; // return to the caller before we actually rescan
        }

        // Report any devices that had last been added/removed.
        //当调用closeDeviceLocked时,就会把需要关闭的设备加入mClosingDevices,下一次循环到这里时就遍历这个列表处理
        for (auto it = mClosingDevices.begin(); it != mClosingDevices.end();) {
        	//移除一个设备就构建一个DEVICE_REMOVED类型的event并加入RawEvent数组中
            std::unique_ptr<Device> device = std::move(*it);
            ALOGV("Reporting device closed: id=%d, name=%s\n", device->id, device->path.c_str());
            event->when = now;
            event->deviceId = (device->id == mBuiltInKeyboardId)
                    ? ReservedInputDeviceId::BUILT_IN_KEYBOARD_ID
                    : device->id;
            event->type = DEVICE_REMOVED;
            event += 1;
            it = mClosingDevices.erase(it);  //从mClosingDevices中移除device
            mNeedToSendFinishedDeviceScan = true;
            if (--capacity == 0) {
                break;
            }
        }

		//当EventHub初始化时,mNeedToScanDevices = true, 所以首次进入需要scan输入设备
        if (mNeedToScanDevices) {
            mNeedToScanDevices = false;
            scanDevicesLocked();  //扫描设备"/dev/input"下的设备,例如event1、event2,这个方法很复杂,
            mNeedToSendFinishedDeviceScan = true;
        }
		//上一步进行了scan device的操作,现在mOpeningDevices是记录着获取到的Device
        while (!mOpeningDevices.empty()) {
        	//遍历取出mOpeningDevices中Device,构建RawEvent->DEVICE_ADDED事件,写入event缓冲区中
            std::unique_ptr<Device> device = std::move(*mOpeningDevices.rbegin());
            mOpeningDevices.pop_back();//把这个device对象从移除mOpeningDevices中
            ALOGV("Reporting device opened: id=%d, name=%s\n", device->id, device->path.c_str());
            //构建一个RawEvent时间,type = DEVICE_ADDED
            event->when = now;
            event->deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
            event->type = DEVICE_ADDED;
            event += 1; //RawEvent对象偏移 + 1(RawEvent数组中RawEvent数量)
		
			...
			//从已经处理的设备中mOpeningDevices中的device加入mDevices Map中,以device->id标记
            auto [dev_it, inserted] = mDevices.insert_or_assign(device->id, std::move(device));

            mNeedToSendFinishedDeviceScan = true;  //标记扫描完成,可以退出扫描状态(退出也要发退出事件)
            //如果RawEvent数组装满了,就跳出循环往下执行(需要等数组中数据分发释放后进入这里再处理)
            if (--capacity == 0) {  
                break;
            }
        }
		//如果扫描结束需要发一个mNeedToSendFinishedDeviceScan事件,将这个事件构造并写入event(RawEvent)数组中
        if (mNeedToSendFinishedDeviceScan) {
            mNeedToSendFinishedDeviceScan = false;
            event->when = now;
            event->type = FINISHED_DEVICE_SCAN;
            event += 1;  //RawEvent对象偏移 + 1(RawEvent数组中RawEvent数量)
            if (--capacity == 0) {
                break;
            }
        }

        // Grab the next input event.
        //从epoll中下一个输入事件
        bool deviceChanged = false;  //这个变量标记当前设备是否有变化(拔插、配置改变等)
        //mPendingEventCount指epoll中返回的事件(在epoll event数组中)的数量
        //mPendingEventIndex指要处理的epoll事件在epoll返回列表中的下标
        //循环处理epoll返回列表中的epoll事件
        while (mPendingEventIndex < mPendingEventCount) {
            const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];
            if (eventItem.data.fd == mINotifyFd) {
                if (eventItem.events & EPOLLIN) {
                    mPendingINotify = true;
                } else {
                    ALOGW("Received unexpected epoll event 0x%08x for INotify.", eventItem.events);
                }
                continue;
            }

            if (eventItem.data.fd == mWakeReadPipeFd) {
                if (eventItem.events & EPOLLIN) {
                    ALOGV("awoken after wake()");
                    awoken = true;
                    char wakeReadBuffer[16];
                    ssize_t nRead;
                    do {
                        nRead = read(mWakeReadPipeFd, wakeReadBuffer, sizeof(wakeReadBuffer));
                    } while ((nRead == -1 && errno == EINTR) || nRead == sizeof(wakeReadBuffer));
                } else {
                    ALOGW("Received unexpected epoll event 0x%08x for wake read pipe.",
                          eventItem.events);
                }
                continue;
            }

            //如果非mINotifyFd和非mWakeReadPipeFd,则是底层输入驱动上报的输入事件,那么通过fd获取这个事件对应的Device
            Device* device = getDeviceByFdLocked(eventItem.data.fd);
            
 			...
            // This must be an input event
            //如果是个epoll读事件
            if (eventItem.events & EPOLLIN) {
            	//通过read方法获取读缓冲区大小和数据。写入readBuffer,读取size为256个input_event
                int32_t readSize =
                        read(device->fd, readBuffer, sizeof(struct input_event) * capacity);
                        
                if (readSize == 0 || (readSize < 0 && errno == ENODEV)) {
                    // Device was removed before INotify noticed.
                    //如果读取的size <= 0 且返回异常可能是设备已经被移除了,只是INotify还没通知,
                    //那么就标记这个设备状态改变,并移除这个设备
                    deviceChanged = true;  //标记这个设备状态改变
                    closeDeviceLocked(*device);  //移除这个设备
                } else if (readSize < 0) {
					...
                } else if ((readSize % sizeof(struct input_event)) != 0) {
					...
                } else { //正常读到数据了
                	//(特殊)如果读到的device是内置键盘,name就设置它的device->id = 0
                    int32_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
					//计算这次读到的epoll读事件中的readBuffer中包含的input_event数量
                    size_t count = size_t(readSize) / sizeof(struct input_event);
                    //从readBuffer循环取出读到的的input_event对象
                    //构造RawEvent对象写入RawEvent数组中,指针依次往后偏移
                    for (size_t i = 0; i < count; i++) {
                        struct input_event& iev = readBuffer[i];
                        event->when = processEventTimestamp(iev);
                        event->readTime = systemTime(SYSTEM_TIME_MONOTONIC);
                        event->deviceId = deviceId;
                        event->type = iev.type;
                        event->code = iev.code;
                        event->value = iev.value;
                        event += 1;
                        capacity -= 1;
                    }
                    //如果写满了RawEvent数组
                    if (capacity == 0) {
                        // The result buffer is full.  Reset the pending event index
                        // so we will try to read the device again on the next iteration.
                        // 如果RawEvent数组写满了,就把mPendingEventIndex - 1,(因为下次循环开始会加一,提前减一这样处理的就还是当前这个epoll事件)
                        // 说明我们本次epoll读事件我们没有处理完,下一个循环还要继续处理这个epoll事件
                        mPendingEventIndex -= 1;
                        break;
                    }
                }
            } else if (eventItem.events & EPOLLHUP) {  //如果是hang-up事件说明设备拔出,就移除这个设备,通知设备状态变化
                ALOGI("Removing device %s due to epoll hang-up event.",
                      device->identifier.name.c_str());
                deviceChanged = true;
                closeDeviceLocked(*device);
            } else {  //收到异常的epoll事件,不处理
                ALOGW("Received unexpected epoll event 0x%08x for device %s.", eventItem.events,
                      device->identifier.name.c_str());
            }
        }

        // readNotify() will modify the list of devices so this must be done after
        // processing all other events to ensure that we read all remaining events
        // before closing the devices.
        //当处理完一次一次epoll_wait返回列表中所有epoll事件后,检测下是否有底层设备变化(mPendingINotify = true)
        //如果有就通知设备状态改变
        if (mPendingINotify && mPendingEventIndex >= mPendingEventCount) {
            mPendingINotify = false;
            readNotifyLocked();  //通过read去读取INotify fd返回的事件,判断设备状态,是需要重新获取设备还是移除设备
            deviceChanged = true;  //标记设备状态改变,
        }

        // Report added or removed devices immediately.
        // 如果有设备状态改变(新增或者移除)需要马上到下一个循环处理
        if (deviceChanged) {event
            continue;
        }

        // Return now if we have collected any events or if we were explicitly awoken.
        //1.如果其他地方调用了`mEventHub->wake()`则会唤醒阻塞在epoll_wait()中的inputReader线程,下一次循环时然后从这里跳出getEvents方法,往下执行loopOnce,处理输入事件
        //2. 或者RawEvent数组中有数据则跳出getEvents方法,往下执行loopOnce,处理输入事件
        if (event != buffer || awoken) {
            break;
        }

		//如果RawEvent数组为空且没有inputReader线程没有被外部唤醒,则下面就准备开始获取下一次epoll事件(进入阻塞等待)
        mPendingEventIndex = 0; //准备进入下一次事件接收前,重置mPendingEventIndex下标

        mLock.unlock(); // release lock before poll
		//进入epoll_wait阻塞等待驱动上报事件
        int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
		//从epoll_wait中唤醒
		//也许是外部调用mEventHub->wake()唤醒
		//或者内核通知事件上报唤醒
		//或者是超时退出休眠
		
        mLock.lock(); // reacquire lock after poll

        if (pollResult == 0) {
            // Timed out.
            // 超时退出的情况
            mPendingEventCount = 0;
            break;
        }

        if (pollResult < 0) {
            // An error occurred.
            mPendingEventCount = 0;

            // Sleep after errors to avoid locking up the system.
            // Hopefully the error is transient.
            if (errno != EINTR) {
                ALOGW("poll failed (errno=%d)\n", errno);
                usleep(100000);
            }
        } else {
            // Some events occurred.
            // 获取到epoll事件,将事件数量赋给mPendingEventCount
            mPendingEventCount = size_t(pollResult);
        }
    }

    // All done, return the number of events we read.
    // 处理结束,退出循环将事件返回到inputReader的loopOnce中处理
    return event - buffer;
}

EventHub::scanDevicesLocked()

void EventHub::scanDevicesLocked() {
    status_t result;
    std::error_code errorCode;

    if (std::filesystem::exists(DEVICE_INPUT_PATH, errorCode)) {
        result = scanDirLocked(DEVICE_INPUT_PATH);
        
    } else {
		...
    }
	...
}

status_t EventHub::scanDirLocked(const std::string& dirname) {
	//遍历 /dev/input/event* 路径,打开这些设备并获取相关设备信息
    for (const auto& entry : std::filesystem::directory_iterator(dirname)) {
        openDeviceLocked(entry.path());
    }
    return 0;
}

EventHub::openDeviceLocked

这个方法很长,主要作用就是打开/dev/input/eventX设备节点,用返回的fd通过ioctl向驱动获取输入设备device相关信息。

void EventHub::openDeviceLocked(const std::string& devicePath) {
	//如果目标路径是当前已存在的设备(之前扫描过的设备)的,就不再扫描这个路径了,避免出现重复设备
    for (const auto& [deviceId, device] : mDevices) {
        if (device->path == devicePath) {
            return; // device was already registered
        }
    }

    char buffer[80];

    ALOGV("Opening device: %s", devicePath.c_str());
	//通过open打开设备节点,返回该设备节点的fd
    int fd = open(devicePath.c_str(), O_RDWR | O_CLOEXEC | O_NONBLOCK);
    
    InputDeviceIdentifier identifier;  //一个硬件设备的结构体在用户空间中描述, 包括name、vendor、product、descriptor等

    // Get device name.
    //获取设备 device name
    if (ioctl(fd, EVIOCGNAME(sizeof(buffer) - 1), &buffer) < 1) {
        ALOGE("Could not get device name for %s: %s", devicePath.c_str(), strerror(errno));
    } else {
        buffer[sizeof(buffer) - 1] = '\0';
        identifier.name = buffer;
    }

    // Check to see if the device is on our excluded list
    //通过device name检测下这个设备是不是在排除名单,如果是就忽略这个设备
    for (size_t i = 0; i < mExcludedDevices.size(); i++) {
        const std::string& item = mExcludedDevices[i];
        if (identifier.name == item) {
            ALOGI("ignoring event id %s driver %s\n", devicePath.c_str(), item.c_str());
            close(fd);
            return;
        }
    }

    // Get device driver version.
    //获取设备驱动版本
    int driverVersion;
    if (ioctl(fd, EVIOCGVERSION, &driverVersion)) {
    }

    // Get device identifier.
    //获取设备的identifier,是设备在内核空间的描述
    //内核描述为input_id结构体,内容为:bustype、product、product、version
    struct input_id inputId;
    if (ioctl(fd, EVIOCGID, &inputId)) {
    }
    identifier.bus = inputId.bustype;
    identifier.product = inputId.product;
    identifier.vendor = inputId.product;
    identifier.version = inputId.version;

    // Get device physical location.
    //获取设备的物理位置(物理拓扑中的位置)
    if (ioctl(fd, EVIOCGPHYS(sizeof(buffer) - 1), &buffer) < 1) {
        // fprintf(stderr, "could not get location for %s, %s\n", devicePath, strerror(errno));
    } else {
        buffer[sizeof(buffer) - 1] = '\0';
        identifier.location = buffer;
    }

    // Get device unique id.
    //获取设备的unique id(一般的设备这个字段都是没有的,为空)
    if (ioctl(fd, EVIOCGUNIQ(sizeof(buffer) - 1), &buffer) < 1) {
        // fprintf(stderr, "could not get idstring for %s, %s\n", devicePath, strerror(errno));
    } else {
        buffer[sizeof(buffer) - 1] = '\0';
        identifier.uniqueId = buffer;
    }

    // Fill in the descriptor.
    // 获取设备的descriptor,这个字段很重要,它是用于标识这个设备的标识符,无论重启、拔插、升级都不会变
    //根据unique_id、vendor_id、product_id、随机数组合后sha1转化生成,赋值给identifier.descriptor
    assignDescriptorLocked(identifier);

    // Allocate device.  (The device object takes ownership of the fd at this point.)
    //创建Device结构体,用于描述当前从驱动获取到的这个输入设备,将前面获取的设备fd、设备节点路径devicePath、设备硬件描述identifier赋给这个Device,
    //同时还有deviceId,这个id并不是驱动传上来的,而是我们每次通过ioctl获取到新设备时计数 + 1
    int32_t deviceId = mNextDeviceId++;
    std::unique_ptr<Device> device = std::make_unique<Device>(fd, deviceId, devicePath, identifier);

	//
上一篇:Git配置下一篇:Lua学习

相关阅读

手机版|MSIPO技术圈 皖ICP备19022944号-2

Copyright © 2023, msipo.com

返回顶部