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 lockAutoMutex _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) {//默认为truemNeedToScanDevices = false;scanDevicesLocked();//1mNeedToSendFinishedDeviceScan = true;}while (mOpeningDevices != nullptr) {//2Device* 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) {//3mNeedToSendFinishedDeviceScan = false;event->when = now;event->type = FINISHED_DEVICE_SCAN;event += 1;if (--capacity == 0) {break;}}//省略if (event != buffer || awoken) {//4break;}//省略}}

注释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);//1if (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) {//1delete 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 EPOLLRDHUPstruct 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);//1break;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 happenbreak;}}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);//2return 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 setmDevices.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();//1deviceChanged = 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 eventif (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);//取出InputDevicestd::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) {//这个值一般为trueif (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的尾部插入一个空的RawStateRawState* next = &mRawStatesPending.back();//将next指向mRawStatesPending的最后一个next->clear();next->when = when;//省略// Sync touchsyncTouch(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);//遍历取出Slotif (!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();//1if (mDeviceMode == DEVICE_MODE_POINTER) {//省略}else{if (!mCurrentMotionAborted) {dispatchButtonRelease(when, policyFlags);dispatchHoverExit(when, policyFlags);dispatchTouches(when, policyFlags);//2dispatchHoverEnterAndMove(when, policyFlags);dispatchButtonPress(when, policyFlags);}}//省略// Copy current touch to last touch in preparation for the next cycle.mLastRawState.copyFrom(mCurrentRawState);//将这次的数据拷贝到mLastRawStatemLastCookedState.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));//1getListener()->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);//1delete 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);//1bool needWake;{ // acquire lockmLock.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);//2needWake = enqueueInboundEventLocked(newEntry);//3mLock.unlock();} // release lockif (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.mzph.cn/news/833661.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数字工厂管理系统如何实现生产过程透明化

随着科技的飞速发展&#xff0c;数字化转型已成为制造业不可逆转的趋势。数字工厂管理系统作为实现生产自动化、智能化的重要工具&#xff0c;其在提升生产效率、降低运营成本、优化资源配置等方面的作用日益凸显。其中&#xff0c;实现生产过程的透明化是数字工厂管理系统的重…

一文彻底学会Vue3路由:全面讲解路由流程、路由模式、传参等——全栈开发之路--前端篇(7)路由详解

全栈开发一条龙——前端篇 第一篇&#xff1a;框架确定、ide设置与项目创建 第二篇&#xff1a;介绍项目文件意义、组件结构与导入以及setup的引入。 第三篇&#xff1a;setup语法&#xff0c;设置响应式数据。 第四篇&#xff1a;数据绑定、计算属性和watch监视 第五篇 : 组件…

数据中台:企业数字化转型的驱动力量_光点科技

在当今数字化快速发展的时代&#xff0c;企业正积极寻求转型升级的新路径。在这个过程中&#xff0c;数据中台以其独特的功能和价值&#xff0c;逐渐成为了企业数字化转型的关键驱动力。本文将深入探讨数据中台的角色、架构及其在企业中的应用&#xff0c;以期为企业的数字化转…

01-基本概念- 索引,文档和 REST API

# kibana_sample_data_ecommerce 为es 索引#查看索引相关信息 GET kibana_sample_data_ecommerce#查看索引的文档总数 GET kibana_sample_data_ecommerce/_count#查看前10条文档&#xff0c;了解文档格式 POST kibana_sample_data_ecommerce/_search { }#_cat indices API #查看…

论文查重率高,有什么办法降重吗?

现在大部分学校已经进入到论文查重降重的阶段了。如果查重率居高不下&#xff0c;延毕的威胁可能就在眼前。对于即将告别校园的学子们&#xff0c;这无疑是个噩梦。四年磨一剑&#xff0c;谁也不想在最后关头功亏一篑。 查重率过高&#xff0c;无非以下两种原因。要么是作为“…

CRM(客户关系管理系统)

商机流程 为什么选择简道云CRM&#xff1f; 行业痛点 很多客户有复杂的订单成本计算方式&#xff0c;复杂多变的审批流程&#xff0c;个性化/流程化的数据结构&#xff0c;没有自定义能力就很难满足。 解决方案 在CRM套件的基础上自定义编辑/搭建了适合公司业务的CRMERP 两…

PCIE协议-2-事务层规范

1.事务层概述 从高层次上看&#xff0c;事务层的关键方面包括&#xff1a; 一个流水线化的全分割事务协议用于区分事务层数据包&#xff08;TLPs&#xff09;的排序和处理要求的机制基于信用量的流控制可选支持数据中毒和端到端数据完整性检测 事务层包含以下内容&#xff1…

Axure RP 9:卓越的交互式产品原型设计工具

Axure RP 9&#xff0c;作为一款备受欢迎的交互式产品原型设计工具&#xff0c;已经在全球范围内赢得了众多设计师和开发者的青睐。这款软件凭借其强大的功能和出色的用户体验&#xff0c;成为了产品原型设计领域的佼佼者。 Axure RP 9支持Mac和Windows两大操作系统&#xff0…

茅台申购,多平台签到与通知 | 使用极空间NAS部署一个神级脚本『DailyCheckIn』

茅台申购&#xff0c;多平台签到与通知 | 使用极空间NAS部署一个神级脚本『DailyCheckIn』 哈喽小伙伴们好&#xff0c;我是Stark-C~&#xff0c;今天为大家分享一个极空间上非常实用且好玩的项目。 小伙伴们都知道&#xff0c;目前很多平台为了促进用户的活跃度和黏性&#…

LeetCode--所有质数、质数对

1.0 Q: 输出 100 以内所有质数 1.1 /* 第一层循环控制检查到哪个数* 第二层通过遍历除以每个比他小的数的方式,检查每个数是不是质数* 由于要遍历检查,设置一个标记,只要任意一次循环可以整除,我们就设置该标记为不是质数 */boolean isPrime true;for (int i 2; i < 100…

如何彻底将CAD或者Cadence卸载干净

最近因为升级软件需要先彻底删除这两个软件&#xff0c;发现无论如何都不能卸载干净&#xff0c;于是乎找到这样一个软件帮助卸载或查找剩余的软件残留&#xff1a; 官网&#xff1a;https://geekuninstaller.com 支持软件和 UWP 应用的卸载&#xff0c;查看软件注册表和安装目…

[leetcode] 68. 文本左右对齐

文章目录 题目描述解题方法贪心java代码复杂度分析 题目描述 给定一个单词数组 words 和一个长度 maxWidth &#xff0c;重新排版单词&#xff0c;使其成为每行恰好有 maxWidth 个字符&#xff0c;且左右两端对齐的文本。 你应该使用 “贪心算法” 来放置给定的单词&#xff…

The 2024 International Collegiate Programming Contest in Hubei Province, China

目录 H. Genshin Impact Startup Forbidden III K. Points on the Number Axis B 估计还会补D&#xff0c;I H. Genshin Impact Startup Forbidden III 对于一个有鱼的池塘&#xff0c;有周围与自己本身五个关键位置可以捕获当前位位置的鱼。把这些位置存储到 map中。用四进制…

深入理解京东商品搜索API(jd.item_search)返回值结构

京东商品搜索API&#xff08;jd.item_search&#xff09;的返回值结构通常是一个复杂的JSON对象&#xff0c;它包含了关于搜索结果的详细信息。为了深入理解这个返回值结构&#xff0c;我们可以将其大致分解为几个主要部分。 status: code: 响应状态码&#xff0c;通常200表示成…

新品 | 功率放大器高压放大测试器HAP-4001

400V功率放大器是一台非常实用的讯号放大器&#xff0c;它具有体积小、重量轻及操作方便&#xff0c;较高的电压输出(400Vp-p)以及较大的输出电流&#xff08;2.8Ap-p&#xff09;等优点。 HAP-4001连续输出直流电流量最大值达到0.5 A&#xff0c;电压放大增益40倍&#xff0c…

OSI七层模型

ISO为了更好的使网络应用更为普及&#xff0c;推出了OSI参考模型。 &#xff08;1&#xff09;应用层 OSI参考模型中最靠近用户的一层&#xff0c;是为计算机用户提供应用接口&#xff0c;也为用户直接提供各种网络服务。我们常见应用层的网络服务协议有&#xff1a;HTTP&…

Echarts柱状图横坐标不显示

本人遇到的问题&#xff1a;折线图横坐标可以正常显示 柱状图接收一样的数据在横坐标却显示不了 1.在前端打印是否能够正常接收数据、数据类型是否有误以及数据是否有内容 console.log(typeof optionbar.xAxis.data)console.log(optionbar.xAxis.data) 2.如上确定能够接收到数…

netty 高性能架构设计--零拷贝

文章目录 前言一、直接内存1.1 什么是直接内存1.2 代码实现1.3 使用直接内存的优缺点 二、netty 零拷贝设计2.1 netty 直接内存2.2 netty 内存池 三、零拷贝的两种方式 前言 本篇从源码层面剖析 netty 高性能架构设计之零拷贝&#xff0c;并且扩展讲述零拷贝的两种实现方式。 …

有没有不使用技术分析工具的?众汇10年交易经验发现还真有

不知道各位投资者有没有遇见过不使用技术分析工具的投资者&#xff0c;众汇用自己的10年外汇交易经验保证&#xff0c;别不信还真有&#xff0c;并且不在少数。 其实道理很简单&#xff0c;这些投资者不相信技术分析工具的效率!在这些投资者看来技术分析工具通常比较滞后、需要…

前置知识储备

基本认知 什么是模式 在一定环境中解决一些问题的方案&#xff08;通俗来说&#xff1a;特定环境中用固定的套路解决问题&#xff09; 什么是设计模式 设计模式是一套反复被人使用&#xff0c;多数人知晓的&#xff0c;经过分类编目的代码设计经验的总结 设计模式最终的目…