Android11 InputReader分析

InputReader线程主要负责读取输入数据,并把数据交给InputDispatcher线程。本文以多指触摸屏为例,梳理一下InputReader的流程。
InputReader线程主要完成以下工作:

  • 处理已有的输入设备
  • 处理新增或者移除的输入设备
  • 对输入设备产生的输入数据进行处理

InputReader线程启动后,调用loopOnce方法

//frameworks\native\services\inputflinger\reader\InputReader.cpp
void InputReader::loopOnce() {
    //省略

    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);//1

    { // acquire lock
        AutoMutex _l(mLock);
        mReaderIsAliveCondition.broadcast();

        if (count) {
            processEventsLocked(mEventBuffer, count);//2
        }

   		//省略
    } // release lock

    // Send out a message that the describes the changed input devices.
    if (inputDevicesChanged) {
        mPolicy->notifyInputDevicesChanged(inputDevices);
    }
    //省略
	mQueuedListener->flush();//3

注释1处获取数据,注释2处理数据,注释3处将数据交给InputDispatcher线程。这个loopOnce方法会被循环调用。接下来分开分析InputReader线程需要完成的工作。

处理已有的输入设备

首先是调用EventHub的getEvents方法

//frameworks\native\services\inputflinger\reader\EventHub.cpp
size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {

	RawEvent* event = buffer;
	
	for (;;) {
		//省略
		if (mNeedToScanDevices) {//默认为true
            mNeedToScanDevices = false;
            scanDevicesLocked();//1
            mNeedToSendFinishedDeviceScan = true;
        }
        while (mOpeningDevices != nullptr) {//2
            Device* device = mOpeningDevices;
            ALOGV("Reporting device opened: id=%d, name=%s\n", device->id, device->path.c_str());
            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;
            }
        }

        if (mNeedToSendFinishedDeviceScan) {//3
            mNeedToSendFinishedDeviceScan = false;
            event->when = now;
            event->type = FINISHED_DEVICE_SCAN;
            event += 1;
            if (--capacity == 0) {
                break;
            }
        }
		
		//省略

		if (event != buffer || awoken) {//4
            break;
        }
        //省略
	}

}

注释1处,调用scanDevicesLocked来扫描已存在的输入设备。注释2处对已经打开的输入设备,构造event,event的type为DEVICE_ADDED表示增加设备。注释3处表示扫描完成,增加一个type为FINISHED_DEVICE_SCAN的event。注释4处,此时event指向的地址不等于buffer,跳出循环,会回到InputReader的loopOnce方法,继续往下执行,调用processEventsLocked处理这些event。

先来看一下scanDevicesLocked方法

//frameworks\native\services\inputflinger\reader\EventHub.cpp
void EventHub::scanDevicesLocked() {
    status_t result = scanDirLocked(DEVICE_PATH);
  	//省略 
}

继续调用scanDirLocked处理,DEVICE_PATH为“dev/input”

//frameworks\native\services\inputflinger\reader\EventHub.cpp
status_t EventHub::scanDirLocked(const char* dirname) {
    char devname[PATH_MAX];
    char* filename;
    DIR* dir;
    struct dirent* de;
    dir = opendir(dirname);//1
    if (dir == nullptr) return -1;
    strcpy(devname, dirname);
    filename = devname + strlen(devname);
    *filename++ = '/';
    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);
        openDeviceLocked(devname);//2
    }
    closedir(dir);
    return 0;
}

注释1处打开dev/input这个目录,dev/input这个目录下代表的是一个个的输入设备,注释2处对目录下的每个设备,调用openDeviceLocked处理

//frameworks\native\services\inputflinger\reader\EventHub.cpp
status_t EventHub::openDeviceLocked(const char* devicePath) {

	int fd = open(devicePath, O_RDWR | O_CLOEXEC | O_NONBLOCK);//打开设备,得到fd
	
	//省略
	int32_t deviceId = mNextDeviceId++;
    Device* device = new Device(fd, deviceId, devicePath, identifier);//新建Device对象
    //省略
    if (test_bit(ABS_MT_POSITION_X, device->absBitmask) &&
        test_bit(ABS_MT_POSITION_Y, device->absBitmask)) {
        // Some joysticks such as the PS3 controller report axes that conflict
        // with the ABS_MT range.  Try to confirm that the device really is
        // a touch screen.
        if (test_bit(BTN_TOUCH, device->keyBitmask) || !haveGamepadButtons) {
            device->classes |= INPUT_DEVICE_CLASS_TOUCH | INPUT_DEVICE_CLASS_TOUCH_MT;//对于多指触摸
        }
	
	//省略
	if (registerDeviceForEpollLocked(device) != OK) {//1
        delete device;
        return -1;
    }

	addDeviceLocked(device);//2
}

新建Device对象后,对于多指触摸,设置其classes 为INPUT_DEVICE_CLASS_TOUCH 和INPUT_DEVICE_CLASS_TOUCH_MT。注释1处将打开的设备添加进epoll中监听。注释2处将创建的Device对象添加到mDevices集合中并设置mOpeningDevices
registerDeviceForEpollLocked最终调用:

//frameworks\native\services\inputflinger\reader\EventHub.cpp
status_t EventHub::registerFdForEpoll(int fd) {
    // TODO(b/121395353) - consider adding EPOLLRDHUP
    struct epoll_event eventItem = {};
    eventItem.events = EPOLLIN | EPOLLWAKEUP;
    eventItem.data.fd = fd;
    if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, &eventItem)) {
        ALOGE("Could not add fd to epoll instance: %s", strerror(errno));
        return -errno;
    }
    return OK;
}

addDeviceLocked:

//frameworks\native\services\inputflinger\reader\EventHub.cpp
void EventHub::addDeviceLocked(Device* device) {
    mDevices.add(device->id, device);
    device->next = mOpeningDevices;
    mOpeningDevices = device;
}

scanDevicesLocked就是扫描dev/input/这个目录并打开里面的设备节点,添加到epoll中监测(后续某个设备有输入数据时,epoll_wait会返回)。对于每个设备节点都会新建device,设置其classes,然后添加到mDevices集合中。
前面提到过,scanDevicesLocked执行完成后,就是构建event,然后getEvents方法返回,回到InputReader的loopOnce方法,继续执行processEventsLocked处理这些event

//frameworks\native\services\inputflinger\reader\InputReader.cpp
void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {
    for (const RawEvent* rawEvent = rawEvents; count;) {
        int32_t type = rawEvent->type;
        size_t batchSize = 1;
        if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) {//type 为DEVICE_ADDED或者FINISHED_DEVICE_SCAN,不走这个分支
            //省略
        } else {
            switch (rawEvent->type) {
                case EventHubInterface::DEVICE_ADDED:
                    addDeviceLocked(rawEvent->when, rawEvent->deviceId);//1
                    break;
                case EventHubInterface::DEVICE_REMOVED:
                    removeDeviceLocked(rawEvent->when, rawEvent->deviceId);
                    break;
                case EventHubInterface::FINISHED_DEVICE_SCAN:
                    handleConfigurationChangedLocked(rawEvent->when);
                    break;
                default:
                    ALOG_ASSERT(false); // can't happen
                    break;
            }
        }
        count -= batchSize;
        rawEvent += batchSize;
    }
}

对于type为DEVICE_ADDED的event,调用addDeviceLocked处理

//frameworks\native\services\inputflinger\reader\InputReader.cpp
void InputReader::addDeviceLocked(nsecs_t when, int32_t eventHubId) {
    if (mDevices.find(eventHubId) != mDevices.end()) {
        ALOGW("Ignoring spurious device added event for eventHubId %d.", eventHubId);
        return;
    }

    InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(eventHubId);
    std::shared_ptr<InputDevice> device = createDeviceLocked(eventHubId, identifier);
	//省略
}

继续调用createDeviceLocked创建InputDevice对象

//frameworks\native\services\inputflinger\reader\InputReader.cpp
std::shared_ptr<InputDevice> InputReader::createDeviceLocked(
        int32_t eventHubId, const InputDeviceIdentifier& identifier) {
    auto deviceIt = std::find_if(mDevices.begin(), mDevices.end(), [identifier](auto& devicePair) {
        return devicePair.second->getDescriptor().size() && identifier.descriptor.size() &&
                devicePair.second->getDescriptor() == identifier.descriptor;
    });

    std::shared_ptr<InputDevice> device;
    if (deviceIt != mDevices.end()) {
        device = deviceIt->second;
    } else {
        int32_t deviceId = (eventHubId < END_RESERVED_ID) ? eventHubId : nextInputDeviceIdLocked();
        device = std::make_shared<InputDevice>(&mContext, deviceId, bumpGenerationLocked(),
                                               identifier);//1
    }
    device->addEventHubDevice(eventHubId);//2
    return device;
}

注释1处创建InputDevice,注释2处调用其addEventHubDevice方法

//frameworks\native\services\inputflinger\reader\InputDevice.cpp
void InputDevice::addEventHubDevice(int32_t eventHubId, bool populateMappers) {
	//省略
	// Touchscreens and touchpad devices.
    if (classes & INPUT_DEVICE_CLASS_TOUCH_MT) {
        mappers.push_back(std::make_unique<MultiTouchInputMapper>(*contextPtr));//1
    } else if (classes & INPUT_DEVICE_CLASS_TOUCH) {
        mappers.push_back(std::make_unique<SingleTouchInputMapper>(*contextPtr));
    }
	//省略
	// insert the context into the devices set
    mDevices.insert({eventHubId, std::make_pair(std::move(contextPtr), std::move(mappers))});//2
}

注释1处,对于多指触摸,设置其mapper为MultiTouchInputMapper,注释2处添加到mDevices集合中。
对于开机已存在的输入设备已经处理完了。主要是扫描并打开这些设备,添加到epoll中,监听这些设备有无事件发生,然后创建InputDevice,根据不同的设备设置其mapper。

处理新增或者移除的输入设备
在平时没有事件时,在EventHub方法中

//frameworks\native\services\inputflinger\reader\EventHub.cpp
size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
	if (event != buffer || awoken) {//没有事件这个不会返回,函数继续往下执行
            break;
     }
	int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
	//省略
}

通过epoll_wait等待事件,而在Android11 InputManagerService启动流程分析 一文中提到,EventHub在初始化的时候,初始化inotify来监听dev/input目录,并使用epoll监听这个inotify。
假设现在有设备新增,则这个epoll_wait会返回,getEvents继续往下执行

//frameworks\native\services\inputflinger\reader\EventHub.cpp
size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
	
	int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);

    if (pollResult < 0) {
          
     } else {
     	// Some events occurred.
		 mPendingEventCount = size_t(pollResult);
	}
}

设置了mPendingEventCount ,继续执行下一次循环

//frameworks\native\services\inputflinger\reader\EventHub.cpp
size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
	//省略
	while (mPendingEventIndex < mPendingEventCount) {//这时候,这个条件满足
            const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];
            if (eventItem.data.fd == mINotifyFd) {//有设备增加或删除
                if (eventItem.events & EPOLLIN) {
                    mPendingINotify = true;//设置mPendingINotify 为ture
                } else {
                    ALOGW("Received unexpected epoll event 0x%08x for INotify.", eventItem.events);
                }
                continue;//跳出循环
            }
            //省略
     }
	// 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.
  	if (mPendingINotify && mPendingEventIndex >= mPendingEventCount) {//满足条件
            mPendingINotify = false;
            readNotifyLocked();//1
            deviceChanged = true;
 	}
}

有设备增加或删除时,调用注释1处的readNotifyLocked处理

status_t EventHub::readNotifyLocked() {
    int res;
    char event_buf[512];
    int event_size;
    int event_pos = 0;
    struct inotify_event* event;

    ALOGV("EventHub::readNotify nfd: %d\n", mINotifyFd);
    res = read(mINotifyFd, event_buf, sizeof(event_buf));//读取数据
    
    while (res >= (int)sizeof(*event)) {
        event = (struct inotify_event*)(event_buf + event_pos);
        if (event->len) {
            if (event->wd == mInputWd) {
                std::string filename = StringPrintf("%s/%s", DEVICE_PATH, event->name);
                if (event->mask & IN_CREATE) {
                    openDeviceLocked(filename.c_str());//1
                } else {
                    ALOGI("Removing device '%s' due to inotify event\n", filename.c_str());
                    closeDeviceByPathLocked(filename.c_str());
                }
            } else if (event->wd == mVideoWd) {
               //省略
            } else {
                LOG_ALWAYS_FATAL("Unexpected inotify event, wd = %i", event->wd);
            }
        }
        event_size = sizeof(*event) + event->len;
        res -= event_size;
        event_pos += event_size;
    }
    return 0;
}

注释1处也是调用openDeviceLocked来处理,openDeviceLocked前面分析过。

关于设备的处理,可以用一张图来总结

在这里插入图片描述
对输入设备产生的输入数据进行处理

和监听输入设备的添加一样,当有输入数据来的时候,getEvents方法中,epoll_wait会返回,设置mPendingEventCount的值,然后进入getEvents下次循环

//frameworks\native\services\inputflinger\reader\EventHub.cpp
size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
	//省略
	for (;;) {
		//省略
		while (mPendingEventIndex < mPendingEventCount) {//前面设置了mPendingEventCount,满足条件
			const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];
			//省略
			Device* device = getDeviceByFdLocked(eventItem.data.fd);//根据产生事件设备的fd,找到device 
			//省略
			// This must be an input event
            if (eventItem.events & EPOLLIN) {
            	int32_t readSize = read(device->fd, readBuffer, sizeof(struct input_event) * capacity);//读取数据
                	//省略
                    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++) {
                        struct input_event& iev = readBuffer[i];
                        event->when = processEventTimestamp(iev);
                        event->deviceId = deviceId;
                        event->type = iev.type;
                        event->code = iev.code;
                        event->value = iev.value;
                        event += 1;
                        capacity -= 1;
                    }
                    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.
                        mPendingEventIndex -= 1;
                        break;
       			//省略
		}
		
		// Return now if we have collected any events or if we were explicitly awoken.
        if (event != buffer || awoken) {//跳出循环
            break;
        }
	}
}

对于设备产生的输入数据,也是构造RawEvent,只不过这些event的type为驱动上报的type,如:ABS_MT_POSITION_X 。
同样,跳出getEvents循环后,调用processEventsLocked来处理这些RawEvent

//frameworks\native\services\inputflinger\reader\InputReader.cpp
void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {
    for (const RawEvent* rawEvent = rawEvents; count;) {
        int32_t type = rawEvent->type;
        size_t batchSize = 1;
        if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) {//输入事件的type肯定远小于0x10000000,满足条件
            int32_t deviceId = rawEvent->deviceId;
            while (batchSize < count) {
                if (rawEvent[batchSize].type >= EventHubInterface::FIRST_SYNTHETIC_EVENT ||
                    rawEvent[batchSize].deviceId != deviceId) {
                    break;
                }
                batchSize += 1;
            }
            
            processEventsForDeviceLocked(deviceId, rawEvent, batchSize);//1

	//省略

对于输入事件,继续调用注释1处的processEventsForDeviceLocked处理

//frameworks\native\services\inputflinger\reader\InputReader.cpp
void InputReader::processEventsForDeviceLocked(int32_t eventHubId, const RawEvent* rawEvents,
                                               size_t count) {
    auto deviceIt = mDevices.find(eventHubId);//取出InputDevice
   
    std::shared_ptr<InputDevice>& device = deviceIt->second;
    if (device->isIgnored()) {//如果这个设备没有设置过mappers数组的话,就忽略这个设备
        // ALOGD("Discarding event for ignored deviceId %d.", deviceId);
        return;
    }

    device->process(rawEvents, count);//1
}

找到InputDevice之后,继续调用其process进行处理

//frameworks\native\services\inputflinger\reader\InputDevice.cpp
void InputDevice::process(const RawEvent* rawEvents, size_t count) {
   
    for (const RawEvent* rawEvent = rawEvents; count != 0; rawEvent++) {
        if (mDropUntilNextSync) {
            	//省略
            } else {
				//省略
            }
        } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) {
            //省略
        } else {
            for_each_mapper_in_subdevice(rawEvent->deviceId, [rawEvent](InputMapper& mapper) {
                mapper.process(rawEvent);
            });
        }
        --count;
    }
}

找出InputDevice对应的mapper(之前在处理输入设备的时候,设置过mappers集合),调用这些mapper的process方法。对于多指触摸屏,mapper为MultiTouchInputMapper

//frameworks\native\services\inputflinger\reader\mapper\MultiTouchInputMapper.cpp
void MultiTouchInputMapper::process(const RawEvent* rawEvent) {
    TouchInputMapper::process(rawEvent);//1,处理type为EV_SYN,code为SYN_REPORT的事件

    mMultiTouchMotionAccumulator.process(rawEvent);
}

EV_SYN同步事件是在一个event结束时驱动上报的。比如下面的触摸协议:

EV_ABS       ABS_MT_SLOT          00000000        
EV_ABS       ABS_MT_TRACKING_ID   00000016        
EV_KEY       BTN_TOUCH            DOWN           
EV_KEY       BTN_TOOL_FINGER      DOWN                
EV_ABS       ABS_MT_POSITION_X    0000011a       
EV_ABS       ABS_MT_POSITION_Y    00000475       
EV_ABS       ABS_MT_TOUCH_MAJOR   00000003      
EV_SYN       SYN_REPORT           00000000    

对于其它类型的数据,调用mMultiTouchMotionAccumulator的process处理,先来看一下这个方法

//frameworks\native\services\inputflinger\reader\mapper\MultiTouchInputMapper.cpp
void MultiTouchMotionAccumulator::process(const RawEvent* rawEvent) {
    if (rawEvent->type == EV_ABS) {//处理type为EV_ABS的数据
        bool newSlot = false;
        if (mUsingSlotsProtocol) {//这个值一般为true
            if (rawEvent->code == ABS_MT_SLOT) {
                mCurrentSlot = rawEvent->value;
                newSlot = true;
            }
        } else if (mCurrentSlot < 0) {
            mCurrentSlot = 0;
        }

        if (mCurrentSlot < 0 || size_t(mCurrentSlot) >= mSlotCount) {
			//省略
        } else {
            Slot* slot = &mSlots[mCurrentSlot];

            switch (rawEvent->code) {
                case ABS_MT_POSITION_X:
                    slot->mInUse = true;
                    slot->mAbsMTPositionX = rawEvent->value;
                    break;
                case ABS_MT_POSITION_Y:
                    slot->mInUse = true;
                    slot->mAbsMTPositionY = rawEvent->value;
                    break;
                case ABS_MT_TOUCH_MAJOR:
                    slot->mInUse = true;
                    slot->mAbsMTTouchMajor = rawEvent->value;
                    break;
                case ABS_MT_TOUCH_MINOR:
                    slot->mInUse = true;
                    slot->mAbsMTTouchMinor = rawEvent->value;
                    slot->mHaveAbsMTTouchMinor = true;
                    break;
                case ABS_MT_WIDTH_MAJOR:
                    slot->mInUse = true;
                    slot->mAbsMTWidthMajor = rawEvent->value;
                    break;
                case ABS_MT_WIDTH_MINOR:
                    slot->mInUse = true;
                    slot->mAbsMTWidthMinor = rawEvent->value;
                    slot->mHaveAbsMTWidthMinor = true;
                    break;
                case ABS_MT_ORIENTATION:
                    slot->mInUse = true;
                    slot->mAbsMTOrientation = rawEvent->value;
                    break;
                case ABS_MT_TRACKING_ID:
                    if (mUsingSlotsProtocol && rawEvent->value < 0) {
                        // The slot is no longer in use but it retains its previous contents,
                        // which may be reused for subsequent touches.
                        slot->mInUse = false;
                    } else {
                        slot->mInUse = true;
                        slot->mAbsMTTrackingId = rawEvent->value;
                    }
                    break;
                case ABS_MT_PRESSURE:
                    slot->mInUse = true;
                    slot->mAbsMTPressure = rawEvent->value;
                    break;
                case ABS_MT_DISTANCE:
                    slot->mInUse = true;
                    slot->mAbsMTDistance = rawEvent->value;
                    break;
                case ABS_MT_TOOL_TYPE:
                    slot->mInUse = true;
                    slot->mAbsMTToolType = rawEvent->value;
                    slot->mHaveAbsMTToolType = true;
                    break;
            }
        }
    } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_MT_REPORT) {//code一般为
        // MultiTouch Sync: The driver has returned all data for *one* of the pointers.
        mCurrentSlot += 1;
    }
}

可以看出该方法,就是处理type为EV_ABS的数据,填充slot。对于code为ABS_MT_SLOT,则认为是一个新手指,新建slot。经过这个方法的处理,数据都放入mSlots里面了。
最后上报的是EV_SYN同步事件,回到MultiTouchInputMapper的process方法,对于同步事件,调用TouchInputMapper的process处理

//frameworks\native\services\inputflinger\reader\mapper\TouchInputMapper.cpp
void TouchInputMapper::process(const RawEvent* rawEvent) {
    //省略

    if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
        sync(rawEvent->when);
    }
}

继续调用sync处理

//frameworks\native\services\inputflinger\reader\mapper\TouchInputMapper.cpp
void TouchInputMapper::sync(nsecs_t when) {
    const RawState* last =
            mRawStatesPending.empty() ? &mCurrentRawState : &mRawStatesPending.back();

    // Push a new state.
    mRawStatesPending.emplace_back();//往mRawStatesPending的尾部插入一个空的RawState
	
    RawState* next = &mRawStatesPending.back();//将next指向mRawStatesPending的最后一个
    next->clear();
    next->when = when;

   //省略

    // Sync touch
    syncTouch(when, next);//1

    // Assign pointer ids.
    if (!mHavePointerIds) {
        assignPointerIds(last, next);
    }
	
    processRawTouches(false /*timeout*/);//2
}

注释1处填充next(next为RawState对象),即填充mRawStatesPending,注释2处开始处理数据。先来看一下syncTouch方法

//frameworks\native\services\inputflinger\reader\mapper\MultiTouchInputMapper.cpp
void MultiTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) {
    size_t inCount = mMultiTouchMotionAccumulator.getSlotCount();
    size_t outCount = 0;
    BitSet32 newPointerIdBits;
    mHavePointerIds = true;

    for (size_t inIndex = 0; inIndex < inCount; inIndex++) {
        const MultiTouchMotionAccumulator::Slot* inSlot =
                mMultiTouchMotionAccumulator.getSlot(inIndex);//遍历取出Slot
        if (!inSlot->isInUse()) {
            continue;
        }
        //省略
        /*根据slot的数据,开始填充RawState*/
        RawPointerData::Pointer& outPointer = outState->rawPointerData.pointers[outCount];
        outPointer.x = inSlot->getX();
        outPointer.y = inSlot->getY();
        outPointer.pressure = inSlot->getPressure();
        outPointer.touchMajor = inSlot->getTouchMajor();
        outPointer.touchMinor = inSlot->getTouchMinor();
        outPointer.toolMajor = inSlot->getToolMajor();
        outPointer.toolMinor = inSlot->getToolMinor();
        outPointer.orientation = inSlot->getOrientation();
        outPointer.distance = inSlot->getDistance();
        outPointer.tiltX = 0;
        outPointer.tiltY = 0;

        outPointer.toolType = inSlot->getToolType();
        //省略
        /*开始处理id和index*/
        // Assign pointer id using tracking id if available.
        if (mHavePointerIds) {
            int32_t trackingId = inSlot->getTrackingId();
            int32_t id = -1;
            if (trackingId >= 0) {
                for (BitSet32 idBits(mPointerIdBits); !idBits.isEmpty();) {
                    uint32_t n = idBits.clearFirstMarkedBit();
                    if (mPointerTrackingIdMap[n] == trackingId) {
                        id = n;
                    }
                }

                if (id < 0 && !mPointerIdBits.isFull()) {
                    id = mPointerIdBits.markFirstUnmarkedBit();
                    mPointerTrackingIdMap[id] = trackingId;
                }
            }
            if (id < 0) {
                mHavePointerIds = false;
                outState->rawPointerData.clearIdBits();
                newPointerIdBits.clear();
            } else {
                outPointer.id = id;
                outState->rawPointerData.idToIndex[id] = outCount;
                outState->rawPointerData.markIdBit(id, isHovering);
                newPointerIdBits.markBit(id);
            }
        }
        outCount += 1;
    }

    outState->rawPointerData.pointerCount = outCount;
    mPointerIdBits = newPointerIdBits;

    mMultiTouchMotionAccumulator.finishSync();
}

在syncTouch方法中,除了填充RawState,还有对id和index的处理,可以看出id和index不一定相等。在多指的开发中注意,只有id才能代表一个手指,需要根据id,从idToIndex中得到index,从而找到对应的触摸信息。

继续回到sync方法,syncTouch处理完成后,调用processRawTouches继续处理数据

//frameworks\native\services\inputflinger\reader\mapper\TouchInputMapper.cpp
void TouchInputMapper::processRawTouches(bool timeout) {
    //省略
    const size_t N = mRawStatesPending.size();//前面已经填充过mRawStatesPending了,这里取出来
    size_t count;
    for (count = 0; count < N; count++) {
        const RawState& next = mRawStatesPending[count];//遍历取出RawState
		//省略
        mCurrentRawState.copyFrom(next);//将数据拷贝到mCurrentRawState中
        if (mCurrentRawState.when < mLastRawState.when) {
            mCurrentRawState.when = mLastRawState.when;
        }
        cookAndDispatch(mCurrentRawState.when);//1
    }
    if (count != 0) {
        mRawStatesPending.erase(mRawStatesPending.begin(), mRawStatesPending.begin() + count);//处理完之后要擦除
    }
   //省略
    
}

注释1处调用cookAndDispatch方法来处理以及分发数据

//frameworks\native\services\inputflinger\reader\mapper\TouchInputMapper.cpp
void TouchInputMapper::cookAndDispatch(nsecs_t when) {
	mCurrentCookedState.clear();
	//省略
	cookPointerData();//1
	if (mDeviceMode == DEVICE_MODE_POINTER) {
		//省略
	}else{
		if (!mCurrentMotionAborted) {
            dispatchButtonRelease(when, policyFlags);
            dispatchHoverExit(when, policyFlags);
            dispatchTouches(when, policyFlags);//2
            dispatchHoverEnterAndMove(when, policyFlags);
            dispatchButtonPress(when, policyFlags);
        }
	}
	//省略
	// Copy current touch to last touch in preparation for the next cycle.
    mLastRawState.copyFrom(mCurrentRawState);//将这次的数据拷贝到mLastRawState
    mLastCookedState.copyFrom(mCurrentCookedState);//拷贝数据
}

注释1处cookPointerData方法比较长,主要是对数据进行加工,加工后的数据放入mCurrentCookedState中。为什么要进行加工呢?因为当前的数据,比如X,Y坐标还是针对触摸屏的,我们要加工成显示屏上对应的坐标
注释2处开始分发数据了,主要是根据不同的情况调用dispatchMotion,分发AMOTION_EVENT_ACTION_MOVE,AMOTION_EVENT_ACTION_POINTER_UP和 AMOTION_EVENT_ACTION_POINTER_DOWN 的 action
接着直接来看下dispatchMotion

//frameworks\native\services\inputflinger\reader\mapper\TouchInputMapper.cpp
void TouchInputMapper::dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32_t source,
                                      int32_t action, int32_t actionButton, int32_t flags,
                                      int32_t metaState, int32_t buttonState, int32_t edgeFlags,
                                      const PointerProperties* properties,
                                      const PointerCoords* coords, const uint32_t* idToIndex,
                                      BitSet32 idBits, int32_t changedId, float xPrecision,
                                      float yPrecision, nsecs_t downTime) {
    PointerCoords pointerCoords[MAX_POINTERS];
    PointerProperties pointerProperties[MAX_POINTERS];
    uint32_t pointerCount = 0;
    while (!idBits.isEmpty()) {
        uint32_t id = idBits.clearFirstMarkedBit();
        uint32_t index = idToIndex[id];
        pointerProperties[pointerCount].copyFrom(properties[index]);
        pointerCoords[pointerCount].copyFrom(coords[index]);//拷贝,coords包含了坐标信息等数据

        if (changedId >= 0 && id == uint32_t(changedId)) {
            action |= pointerCount << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
        }

        pointerCount += 1;
    }

    ALOG_ASSERT(pointerCount != 0);

    if (changedId >= 0 && pointerCount == 1) {
    	/*可以看出,如果是第一根手指,这里做了处理,变成AMOTION_EVENT_ACTION_DOWN和AMOTION_EVENT_ACTION_UP*/
        if (action == AMOTION_EVENT_ACTION_POINTER_DOWN) {
            action = AMOTION_EVENT_ACTION_DOWN;
        } else if (action == AMOTION_EVENT_ACTION_POINTER_UP) {
            action = AMOTION_EVENT_ACTION_UP;
        } else {
            // Can't happen.
            ALOG_ASSERT(false);
        }
    }
    //省略
    NotifyMotionArgs args(getContext()->getNextId(), when, deviceId, source, displayId, policyFlags,
                          action, actionButton, flags, metaState, buttonState,
                          MotionClassification::NONE, edgeFlags, pointerCount, pointerProperties,
                          pointerCoords, xPrecision, yPrecision, xCursorPosition, yCursorPosition,
                          downTime, std::move(frames));//1
    getListener()->notifyMotion(&args);//2
}

注释1处又将数据构造成了一个NotifyMotionArgs 对象。注释2处这个getListener()返回的是什么呢?返回的是一个QueuedInputListener对象。具体参考这张简易的类图
在这里插入图片描述

调用QueuedInputListener的notifyMotion方法

//frameworks\native\services\inputflinger\InputListener.cpp
void QueuedInputListener::notifyMotion(const NotifyMotionArgs* args) {
    traceEvent(__func__, args->id);
    mArgsQueue.push_back(new NotifyMotionArgs(*args));
}

这里只是将数据放入了队列。在InputReader线程中,最后会调用flush方法,来处理这个队列

//frameworks\native\services\inputflinger\InputListener.cpp
void QueuedInputListener::flush() {
    size_t count = mArgsQueue.size();
    for (size_t i = 0; i < count; i++) {
        NotifyArgs* args = mArgsQueue[i];
        args->notify(mInnerListener);//1
        delete args;
    }
    mArgsQueue.clear();
}

遍历队列,注释1处调用NotifyMotionArgs的notify方法,注意传入的参数为mInnerListener,mInnerListener指向的是一个InputClassifier对象(看上图)

//frameworks\native\services\inputflinger\InputListener.cpp
void NotifyMotionArgs::notify(const sp<InputListenerInterface>& listener) const {
    listener->notifyMotion(this);
}

直接调用InputClassifier的notifyMotion方法

//frameworks\native\services\inputflinger\InputClassifier.cpp
void InputClassifier::notifyMotion(const NotifyMotionArgs* args) {
    std::scoped_lock lock(mLock);
    //省略

    NotifyMotionArgs newArgs(*args);
    newArgs.classification = mMotionClassifier->classify(newArgs);
    mListener->notifyMotion(&newArgs);//mListener是InputDispatcher
}

调用InputDispatcher的notifyMotion方法

//frameworks\native\services\inputflinger\dispatcher\InputDispatcher.cpp
void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) {
	//省略
	mPolicy->interceptMotionBeforeQueueing(args->displayId, args->eventTime, /*byref*/ policyFlags);//1
	bool needWake;
    { // acquire lock
        mLock.lock();
        if (shouldSendMotionToInputFilterLocked(args)) {//可以设置拦截器直接处理
			//省略
		}
		// Just enqueue a new motion event.
        MotionEntry* newEntry =
                new MotionEntry(args->id, args->eventTime, args->deviceId, args->source,
                                args->displayId, policyFlags, args->action, args->actionButton,
                                args->flags, args->metaState, args->buttonState,
                                args->classification, args->edgeFlags, args->xPrecision,
                                args->yPrecision, args->xCursorPosition, args->yCursorPosition,
                                args->downTime, args->pointerCount, args->pointerProperties,
                                args->pointerCoords, 0, 0);//2

        needWake = enqueueInboundEventLocked(newEntry);//3
        mLock.unlock();
    } // release lock

    if (needWake) {
        mLooper->wake();//4
    }
}

注释1处通过JNI调用到Java层的interceptMotionBeforeQueueingNonInteractive方法,注释2处又将数据封装成了MotionEntry对象,注释3处入“iq”队列,注释4处虽然这个是在InputDispatcher中,但实际上还是在InputReader线程中调用的,所以需要唤醒InputDispatcher线程。

//frameworks\native\services\inputflinger\dispatcher\InputDispatcher.cpp
bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) {
    bool needWake = mInboundQueue.empty();
    mInboundQueue.push_back(entry);//放入iq队列
    traceInboundQueueLengthLocked();//这就是为什么可以在trace中看到iq的原因
	//省略

    return needWake;
}

到这里,InputReader的工作就完成了,接下来就是InputDispatcher线程的处理了。对于多指触摸,总结下InputReader处理数据的流程
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/602808.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【数据结构与算法】力扣 226. 翻转二叉树

题目描述 给你一棵二叉树的根节点 root &#xff0c;翻转这棵二叉树&#xff0c;并返回其根节点。 示例 1&#xff1a; 输入&#xff1a; root [4,2,7,1,3,6,9] 输出&#xff1a; [4,7,2,9,6,3,1]示例 2&#xff1a; 输入&#xff1a; root [2,1,3] 输出&#xff1a; [2,3,1…

【算法刷题 | 贪心算法09】4.30(单调递增的数字)

文章目录 16.单调递增的数字16.1题目16.2解法&#xff1a;贪心16.2.1贪心思路16.2.2代码实现 16.单调递增的数字 16.1题目 当且仅当每个相邻位数上的数字 x 和 y 满足 x < y 时&#xff0c;我们称这个整数是单调递增的。 给定一个整数 n &#xff0c;返回 小于或等于 n 的…

项目1:STM32+DHT11+FreeRTOS+emwin+LCD

【屏幕显示DHT11数据】 面向对象的思想编写硬件驱动程序&#xff0c;DHT11采集环境中的温湿度数据。使用FreeRTOS提供的任务间通信、同步、互斥&#xff0c;将DHT11的数据传递给显示任务。显示任务中&#xff0c;使用emWin中间件&#xff0c;制作屏幕的各种界面&#xff0c;并将…

程序员必备的8款工具软件,第5款简直绝了!

没错&#xff0c;今天又送福利了&#xff01;来给大家推荐一波好用的软件~ 都说程序员的电脑上有各种各样的软件工具、编辑器、插件等等&#xff0c;不同岗位的程序员使用的工具也不同。 今天就给你分享8款程序员必备的工具软件&#xff0c;看看是不是你常用的&#xff01; …

【最经典的79个】软件测试面试题(内含答案)备战“金三银四”

001.软件的生命周期(prdctrm) 计划阶段(planning)-〉需求分析(requirement)-〉设计阶段(design)-〉编码(coding)->测试(testing)->运行与维护(running maintrnacne) 测试用例 用例编号 测试项目 测试标题 重要级别 预置条件 输入数据 执行步骤 预期结果 0002.问&…

锂电池管理芯片厂商拓品微电子授权世强硬创代理,产品涵盖充电/升压等系列

为进一步扩大自身品牌在国内的知名度&#xff0c;给更多硬科技企业提供锂电池管理芯片产品&#xff0c;南京拓品微电子有限公司&#xff08;下称“拓品微电子”&#xff0c;英文名&#xff1a;TOP POWER ASIC&#xff09;与拥有超30年历史的分销企业世强先进&#xff08;科技&a…

618热门好物大盘点,省心购物指南快看过来!

在618购物节即将拉开帷幕之际&#xff0c;整个互联网仿佛都弥漫着一种节日的热闹与期待。各大品牌纷纷亮出他们的杀手锏&#xff0c;推出了一系列诱人的优惠活动和特色产品&#xff0c;让人眼花缭乱&#xff0c;心动不已。如果你此刻正犹豫着该把哪一件宝贝收入囊中&#xff0c…

【JavaScript】原型

1. 什么是原型&#xff1f; 在 JavaScript 中&#xff0c;每个对象都有一个原型&#xff08;prototype&#xff09;&#xff0c;它是对象的一种特殊属性。原型对象包含了对象的属性和方法&#xff0c;当我们访问对象的属性或方法时&#xff0c;如果对象本身不存在这些属性或方…

电动车违规停放监测摄像机

随着电动车的普及和城市交通拥堵问题的加剧&#xff0c;电动车的停放管理也成为一个亟待解决的难题。为了维护城市交通秩序和提高停车效率&#xff0c;一种名为电动车违规停放监测摄像机应运而生&#xff0c;成为城市管理的利器。这种电动车违规停放监测摄像机&#xff0c;利用…

实战 | 实时手部关键点检测跟踪(附完整源码+代码详解)

《博主简介》 小伙伴们好&#xff0c;我是阿旭。专注于人工智能、AIGC、python、计算机视觉相关分享研究。 ✌更多学习资源&#xff0c;可关注公-仲-hao:【阿旭算法与机器学习】&#xff0c;共同学习交流~ &#x1f44d;感谢小伙伴们点赞、关注&#xff01; 《------往期经典推…

如何理解GTX发送通道的用户接口?(高速收发器二)

前文讲解了高速收发器的QPLL及CPLL和差分时钟相关内容&#xff0c;如下图所示。本文就打开高速收发器通道的内部结构&#xff0c;进行简要讲解。 图21GTXE2_CHANNEL原始拓扑 收发器的内部框图如下所示&#xff0c;上半部分是发送通道&#xff0c;下半部分是接收通道&#xff0c…

Matlab实现分段函数拟合(分段点未知)| 源码分享 | 视频教程 | 三种分段函数拟合方法

专栏导读 作者简介&#xff1a;工学博士&#xff0c;高级工程师&#xff0c;专注于工业软件算法研究本文已收录于专栏&#xff1a;《复杂函数拟合案例分享》本专栏旨在提供 1.以案例的形式讲解各类复杂函数拟合的程序实现方法&#xff0c;并提供所有案例完整源码&#xff1b;2.…

STM32 VS Code 扩展用户指南

系列文章目录 前言 一、视频教程快速入门 通过我们简单易学的视频教程&#xff0c;快速掌握新版本的使用方法&#xff1a; 二、功能描述 2.1 创建/导入项目 STM32 VS Code 扩展提供两种不同的项目创建选项&#xff1a; STM32CubeMX 项目&#xff1a; 这是一个依靠 CMake 作为…

(三十六)第 6 章 树和二叉树(二叉树的顺序存储表示实现)

1. 背景说明 2. 示例代码 1) errorRecord.h // 记录错误宏定义头文件#ifndef ERROR_RECORD_H #define ERROR_RECORD_H#include <stdio.h> #include <string.h> #include <stdint.h>// 从文件路径中提取文件名 #define FILE_NAME(X) strrchr(X, \\) ? str…

web前端学习笔记7-iconfont使用

7. iconfont的使用流程 字体图标使用较多的是阿里巴巴iconfont图标库,它是阿里巴巴体验团队推出的图标库和图标管理平台,提供了大量免费和可定制的矢量图标,以满足网页设计、平面设计、UI设计、应用程序开发和其他创意项目的需求。 官方网站:https://www.iconfont.cn/ 使用…

可视化数据报道:Kompas.ai如何用图表和动态效果讲述故事

在数字化时代&#xff0c;数据无处不在&#xff0c;而如何将这些数据转化为易于理解且吸引人的故事&#xff0c;成为信息传递的关键。数据可视化作为一种强有力的工具&#xff0c;能够帮助观众快速把握复杂信息的要点&#xff0c;增强记忆&#xff0c;并激发情感共鸣。本文将深…

【BUUCTF】[RoarCTF 2019]Easy Java1

工具&#xff1a;hackbar发包&#xff0c;bp抓包。 解题步骤&#xff1a;【该网站有时候send不了数据&#xff0c;只能销毁靶机重试】 这里的登录界面是个天坑【迷魂弹】 直接点击help&#xff0c;然后进行打开hackbar——通过post请求&#xff0c;再通过bp抓包&#xff0c;…

2. 外婆都称赞的基于Jaeger的Span模型改造

前言 我们的目标是基于Jaeger来实现分布式链路追踪的Java客户端工具包&#xff0c;实际就是一个Springboot的Starter&#xff0c;在1. 看完这篇文章我奶奶都懂Opentracing了一文中我们详细的学习了一下Opentracing的相关概念以及阅读了相关的源码&#xff0c;同时特别重要的是…

深度剖析Comate智能产品:科技巧思,实用至上

文章目录 Comate智能编码助手介绍Comate应用场景Comate语言与IDE支持 Comate安装步骤Comate智能编码使用体验代码推荐智能推荐生成单测注释解释注释生成智能问答 Comate实战演练总结 Comate智能编码助手介绍 市面上现在有很多智能代码助手&#xff0c;当时互联网头部大厂百度也…

2024年5月软考,别再傻傻啃书了!

备考2024年软考&#xff0c;不听课也不刷题&#xff0c;只是看教材的话&#xff0c;想要考试通过&#xff0c;几乎是不可能的&#xff0c;特别是基础比较薄弱的考生。 为什么只看教材通不过&#xff1f; 如果只是把教材从头到尾看一遍&#xff0c;毫无目的地看书&#xff0c;…