【android 9】【input】【7.发送按键事件1——InputReader线程】

系列文章目录

本人系列文章-优快云博客


目录

系列文章目录

1.简介

1.1发送流程介绍

1.2 时序图

2.普通按键消息发送部分源码分析

 2.1 设备的监听

 2.2 inputreader线程阻塞等待事件发生

 2.3 按键事件的产生

 2.4 EventHub::getEvents

2.5 InputReader::loopOnce

 2.6 processEventsLocked

 2.7 processEventsForDeviceLocked

 2.8 InputDevice::process

 2.9 KeyboardInputMapper::process

2.10 KeyboardInputMapper::process(扫描消息)

 2.11 KeyboardInputMapper::process(按键按下)

 2.12 KeyboardInputMapper::processKey

2.13 QueuedInputListener::notifyKey

 2.14 KeyboardInputMapper::process(同步消息)

 2.15 KeyboardInputMapper::process(扫描事件)

 2.16 KeyboardInputMapper::process(按键抬起事件)

2.17 KeyboardInputMapper::process(同步消息)

 2.18 QueuedInputListener::flush

 2.19 NotifyKeyArgs::notify

 2.20 InputDispatcher::notifyKey

 2.21 NativeInputManager::interceptKeyBeforeQueueing

2.22 IMS中的interceptKeyBeforeQueueing

 2.23 WMS中的interceptKeyBeforeQueueing

2.24 PhoneWindowManager中的interceptKeyBeforeQueueing

 2.25 NativeInputManager::handleInterceptActions

 2.26 InputDispatcher::enqueueInboundEventLocked

 2.27 NotifyKeyArgs::notify(抬起)


1.简介

从之前的篇幅我们知道了,事件分为设备增删事件和原始输入事件,而原始输入事件主要有两种,一种是key按键事件的派发,一种是触摸事件的派发。本篇区别于其他作者,会不放过任何一个函数,进行系统完整的分析。此篇主要分析一个按键事件从驱动上报到应用监听获取的全流程。

1.1发送流程介绍

1.input启动后,完成对设备的加载后,当无事件产生时,inputreader线程便阻塞在epoll_wait等待有消息的产生。

2.当物理按键按下和抬起的时候,即按键事件便产生了。内核会上报按下和抬起的消息给到input系统。

3.inputreader线程中的不再阻塞,然后从notifyfd中读取原始的input_event输入事件。

 4.对原始的input_event事件,进行加工成RawEvent事件,然后调用对应的KeyboardInputMapper进行处理。

5.KeyboardInputMapper会将RawEvent事件再加工成NotifyKeyArgs事件,然后插入到mArgsQueue队列中。

6.然后会调用InputDispatcher的notifyKey,将NotifyKeyArgs事件再加工成KeyEntry事件,放入mInboundqueue队列队尾,然后唤醒InputDispatcher分发线程,进行按键事件的分发。

1.2 时序图

  为了完整的画出所有时序,较为模糊,读者可保存照片到本地放大观看。


2.普通按键消息发送部分源码分析

为了便于读者清晰的了解发送流程,此章节会删除不执行的代码,便于理解。

 2.1 设备的监听

要想知道某设备的事件发生,首先,便是要监听设备。在启动篇中,我们知道会注册设备到epoll中完成对设备的监听。

下面是前文监听设备节点的代码

status_t EventHub::registerDeviceForEpollLocked(Device* device) {
    struct epoll_event eventItem;
    memset(&eventItem, 0, sizeof(eventItem));
    eventItem.events = EPOLLIN;//监听的文件描述符是否有可读事件

    eventItem.data.u32 = device->id;//事件触发时会返回eventItem.data数据
    if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, device->fd, &eventItem)) {//监听device->fd对应的设备是否有可读事件。
	//如果有事件可读,则返回eventItem.data
        
    }
    return OK;
}

 2.2 inputreader线程阻塞等待事件发生

void InputReader::loopOnce() {
    int32_t oldGeneration;
    int32_t timeoutMillis;
    bool inputDevicesChanged = false;
    Vector<InputDeviceInfo> inputDevices;
    { 
 
        oldGeneration = mGeneration;//新的值
        timeoutMillis = -1;
 
    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);//通过EventHub的getEvents函数获取事件,
	//并存放在mEventBuffer中。
	//参数分析:timeoutMillis=-1,代表等待超时的时间,感觉如果数字为0代表立即执行
	//mEventBuffer是一个存放从eventhub中读取的rawevent结构体类型的数组,源代码是:RawEvent mEventBuffer[EVENT_BUFFER_SIZE];
	//EVENT_BUFFER_SIZE值是256,代表最大可以读取256个原始事件,查看833行-840行,RawEvent结构体如下
.......

 即:此时阻塞在getEvents函数的epoll_wait中。

//第一次for循环,会阻塞在epoll_wait中,等待设备增加事件的到来。
 
size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
    ALOG_ASSERT(bufferSize >= 1);
 
 
 
    struct input_event readBuffer[bufferSize];//bufferSize值为传入的256
 
    RawEvent* event = buffer;//event指针指向传入的buffer首地址,每存入一个事件,event指针向后移动一个元素
    size_t capacity = bufferSize;//capacity表示buffer中剩余端元素数量,capacity为0,表示buffer已满
    bool awoken = false;
    for (;;) {
        nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);//获取当前时间
 
 
        mPendingEventIndex = 0;//此时mPendingEventItems数组中未处理的事件已经处理完了,将mPendingEventIndex设置为0
 
 
        int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);//此时读线程会阻塞在此处,等待有消息的事件发生
		
	......

 2.3 按键事件的产生

首先从本系列的input第一篇可知,一次按键按下和抬起的过程中,会产生六条数据包,一个按键按下到抬起,发了6包数据,依次是:

扫描事件、KEY事件、同步事件、扫描事件、KEY事件、同步事件。

比如按下A按键,然后抬起。则内核上报的数据为:

按下:
/dev/input/event4: EV_MSC MSC_SCAN 00090001//扫描事件,EV_MSC代表是杂项事件,MSC_SCAN代表是杂项事件中的扫描事件,Value代表值是00090001,说明扫描到BTN_GAMEPAD这个按键有变化
/dev/input/event4: EV_KEY BTN_GAMEPAD 00000001 //KEY事件,EV_KEY代表这是按键类型事件,BTN_GAMEPAD代表手柄注册中的按键是BTN_GAMEPAD,00000001表示按下
/dev/input/event4: EV_SYN SYN_REPORT 00000000 //同步事件,标识一个独立的事件的发生,它与另一个同步包夹中间的一组的打印就是一个事件的发生
抬起:
/dev/input/event4: EV_MSC MSC_SCAN 00090001 //扫描事件
/dev/input/event4: EV_KEY BTN_GAMEPAD 00000000 //KEY事件,00000000代表抬起
/dev/input/event4: EV_SYN SYN_REPORT 00000000 //同步事件

 然后便是按键事件的处理

 2.4 EventHub::getEvents

主要作用是:

1.epoll_wait不再阻塞,从发生事件的fd中读取按键原始事件并加工成RawEvent类型的消息。

回到2.2 inputreader线程阻塞的地方,因为对设备的fd进行了epoll监听其是否有可读事件,所以当有事件时,第一次for循环中epoll_wait不再阻塞,向下执行。

//第一次死循环,当按键按下产生按键按下的消息。epoll_wait不再阻塞,向下执行。
size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
    

    struct input_event readBuffer[bufferSize];//bufferSize值为传入的256

    RawEvent* event = buffer;//event指针指向传入的buffer首地址,每存入一个事件,event指针向后移动一个元素
    size_t capacity = bufferSize;//capacity表示buffer中剩余端元素数量,capacity为0,表示buffer已满
    bool awoken = false;
    for (;;) {
        nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);//获取当前时间

        mPendingEventIndex = 0;//此时mPendingEventItems数组中未处理的事件已经处理完了,将mPendingEventIndex设置为0

        int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);//此时读线程会阻塞在此处,等待有消息的事件发生
		
		

        ///if (pollResult < 0) 
		{
        } else {
            // 发生了某些事件
            mPendingEventCount = size_t(pollResult);
        }
    }

    /// 此时并没有返回而是开启了下一次死循环。
    ///return event - buffer;
}

 第二次for循环,会从设备的fd中读取原始事件,并将消息加工成RawEvent类型。

//第二次for死循环
//假设只有一个设备,一个按下,一个抬起,相当于六条消息,
//依次为扫描事件、key按下事件、同步事件、扫描事件、key抬起事件、同步事件。
size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
	size_t capacity = bufferSize;//capacity表示buffer中剩余端元素数量,capacity为0,表示buffer已满
	RawEvent* event = buffer;
    for (;;) {
        nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);//获取当前时间



        //如果存在未处理的事件,则从mPendingEventItems数组中取出事件
        bool deviceChanged = false;
        while (mPendingEventIndex < mPendingEventCount) {//此时会执行,此时mPendingEventIndex=0,mPendingEventCount为设备有变化的数量
		//此时相当于mPendingEventCount =1,
            const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];



            ssize_t deviceIndex = mDevices.indexOfKey(eventItem.data.u32);//获取事件的data.u32字段,此字段存储的是device-id,
			//然后判断此事件发生的设备是否在mDevices中能查找到对应的deviceid
            if (deviceIndex < 0) {//在已经打开的设备中,没找到此事件对应的设备
                ALOGW("Received unexpected epoll event 0x%08x for unknown device id %d.",
                        eventItem.events, eventItem.data.u32);
                continue;
            }

            Device* device = mDevices.valueAt(deviceIndex);//通过deviceid,找到对应的device
            if (eventItem.events & EPOLLIN) {//现在处理的是input设备产生的原始事件
                int32_t readSize = read(device->fd, readBuffer,sizeof(struct input_event) * capacity);//从input设备中,读取event事件,存入readBuffer数组中,从input设备中,读取event事件,存入readBuffer数组中,
                if (readSize == 0 || (readSize < 0 && errno == ENODEV)) {//如果满足此条件,代表在通知INotify之前,设备已被移除。
                }
				else
				{
                    int32_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;//判断是否是内置键盘,给devcie-id赋值

                    size_t count = size_t(readSize) / sizeof(struct input_event);//计算事件的数量,此时等于6
                    for (size_t i = 0; i < count; i++) {
                        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;
                        capacity -= 1;
                    }
                    if (capacity == 0) {//buffer数组已满,重置mPendingEventIndex,
					//注意,此时有三个数组,分别是
					//1.存储epoll_event结构体的mPendingEventItems数组,epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];
					//2.存储input_event结构体的readbuffer数组,input_event readBuffer[bufferSize],
					//3.存储RawEvent类型的buffer数组,RawEvent* event = buffer
					//流程如下:
					//当设备有可读事件时,会返回一个epoll_event结构体,存储mPendingEventItems数组中,然后从mPendingEventItems数组中,
					//取出这个epoll_event结构体,函数进行判断epoll_event结构体的eventItem.events = EPOLLIN,此字段代表监听的fd有可读事件,
					//然后,通过read(fd,readbuffer)函数,去读取此设备中的多个input_event结构体类型的事件,
					//存储在readbuffer数组中,这是第二个数组,然后循环取出input_event类型的数据,
					//将其进行加工成rawinput类型,存储在存储RawEvent类型的buffer数组中。
					
					//所以此处的mPendingEventIndex含义减去一的含义是:
					//比如:buffer数组的容量是200,一个eventI
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值