Android11 InputReader分析

news2024/12/28 18:32:28

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.coloradmin.cn/o/1663761.html

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

相关文章

K8s 多租户管理

一、K8s 多租户管理 多租户是指在同一集群中隔离多个用户或团队&#xff0c;以避免他们之间的资源冲突和误操作。在K8s中&#xff0c;多租户管理的核心目标是在保证安全性的同时&#xff0c;提高资源利用率和运营效率。 在K8s中&#xff0c;该操作可以通过命名空间&#xff0…

代码随想录算法训练营第六十三天| LeetCode84. 柱状图中最大的矩形

一、LeetCode 84. 柱状图中最大的矩形 题目链接/文章讲解/代码讲解&#xff1a;https://programmercarl.com/0084.%E6%9F%B1%E7%8A%B6%E5%9B%BE%E4%B8%AD%E6%9C%80%E5%A4%A7%E7%9A%84%E7%9F%A9%E5%BD%A2.html 状态&#xff1a;已解决 1.思路 这道题跟上道接雨水的题基本上是反…

掌握这些服务器知识点,你可以在领导面前吹一波了!

如何保证服务器可以支持百万用户访问&#xff1f;服务器品牌有哪些&#xff1f;如何选购服务器&#xff1f;对于这些问题&#xff0c;今天我们就一起来看下关于服务器的相关知识。 假如你开发了一个网站或者一个app把他放到服务器上&#xff0c;之后你把它发布到了网上&#x…

机器学习面试篇

如何理解机器学习数据集的概念 数据集是机器学习的基础&#xff0c;它包括了用于训练和测试模型所需的数据。数据集通常以矩阵的形式存在&#xff0c;其中每一行代表一个样本&#xff08;或实例&#xff09;&#xff0c;每一列代表一个特征&#xff08;或属性&#xff09;。…

云效 Pipeline as Code 来了!这些场景,用好它效率翻倍!

从可视化编排到支持 YAML 编排 云效流水线 Flow 是开箱即用的企业级持续集成和持续交付工具&#xff0c;支持丰富的代码源、构建、自动化测试工具、多种部署类型和部署方式&#xff0c;与阿里云深度集成&#xff0c;还提供多种企业级特性&#xff0c;助力企业高效完成从开发到…

亚信安全发布《2024年第一季度网络安全威胁报告》

亚信安全2024年第一季度网络安全威胁报告 一季度威胁概览 《亚信安全2024年第一季度网络安全威胁报告》的发布旨在从一个全面的视角解析当前的网络安全威胁环境。此报告通过详尽梳理和总结2024年第一季度的网络攻击威胁&#xff0c;目的是提供一个准确和直观的终端威胁感知。…

企业设置,支持自定义短信签名

05/08 主要更新模块概览 自动换行 启动封面 使用统计 短信签名 01表单管理 1.1 【表单外链】- 查询外链支持多个外链 说明&#xff1a; 表单查询外链原仅支持一个&#xff0c;现支持增加多个外链功能&…

Ring-Switch Field-Switch

参考文献&#xff1a; [GHPS12] Gentry C, Halevi S, Peikert C, et al. Ring switching in BGV-style homomorphic encryption[C]//International Conference on Security and Cryptography for Networks. Berlin, Heidelberg: Springer Berlin Heidelberg, 2012: 19-37.[GHP…

【git】通过JetNrains IDE对git的操作

应该适用于所有jetbrains产品。 一、拉取(pull)代码 上方工具栏-Git-克隆。然后填写git地址与本地存放地址。 二、搁置 修改代码后搁置代码(不提交,但是也不撤销已修改的代码,把它暂存起来)。 界面的左上角。1->2->3。完事就可以写换到其他分支肆意妄为^^。 三…

【JAVA |开篇】JAVA入门及JDK环境配置

目录 一、JIAVA语言 二、Java开发环境安装 三、初识Java的main方法 四、注释 一、JIAVA语言 Java 是一种优秀的程序设计语言 &#xff0c;它具有令人赏心悦目的语法和易于理解的语义 Write once, Run anywhere&#xff08;这句话体现了JAVA语言的核心&#xff0c;一次运行 任…

能恢复永久删除文件的十大数据恢复软件

当您不小心删除了重要数据&#xff0c;或者由于病毒攻击而丢失了重要数据时&#xff0c;请不要惊慌&#xff0c;我们已经为您准备好了。别无他处&#xff0c;这是您目前市场上最佳数据恢复软件列表的一站式目的地。 能恢复永久删除文件的十大数据恢复软件 1. 奇客数据恢复 这是…

怎么制作流程图?介绍制作方法

怎么制作流程图&#xff1f;在日常生活和工作中&#xff0c;流程图已经成为我们不可或缺的工具。无论是项目规划、流程优化&#xff0c;还是学习理解复杂系统&#xff0c;流程图都能帮助我们更直观地理解和表达信息。然而&#xff0c;很多人可能并不清楚&#xff0c;其实制作流…

Android 如何启用user版本的adb源码分析

通过adb shell中执行getprop persist.sys.usb.config&#xff0c;可以看到系统usb的相关选项&#xff0c;persist.sys.usb.config显示的就是当前系统关于usb选项的系统配置【RK3188Android4.4刚移植的例子】: 全编脚本中make命令会调用build/core/main.mk,在里面可以看到一段…

品鉴中的平衡之美:如何欣赏红酒的口感与风格和谐

品鉴云仓酒庄雷盛红酒的过程&#xff0c;是对其口感与风格和谐的追求和欣赏。平衡是红酒品质的重要标志之一&#xff0c;它体现在红酒的色泽、香气、口感和余味等多个方面。通过欣赏红酒的平衡之美&#xff0c;我们可以更好地领略其精妙之处&#xff0c;感受其带来的美妙滋味。…

sql-labs(11-20)

1.less-11 1.判断类型 根据测试在使用 " 不会报错&#xff0c; 会报错&#xff0c;所以他是字符型的并且被单引号闭合&#xff0c;而且只有用户 登陆成功才会显示数据。所以先尝试报错注入 2.爆数据库 and updatexml(2,concat(0x7e,(select database()),0x7e),2)-- 3.爆数…

C++笔试强训day16

目录 1.字符串替换 2.神奇数 3.DNA序列 1.字符串替换 链接 简单的遍历替换即可&#xff1a; class Solution { public:string formatString(string str, vector<char>& arg) {string ret;int k 0;for (int i 0; i < str.size(); i){if (str[i] %){ret arg…

表格内容高效拆分,自定义行数随心所欲,让数据处理更高效!

在信息化社会的今天&#xff0c;表格成为了我们处理数据、整理信息的重要工具。然而&#xff0c;当表格内容过于庞大时&#xff0c;如何高效地拆分表格内容成为了摆在我们面前的一大难题。传统的拆分方法往往耗时耗力&#xff0c;且难以满足我们个性化的需求。 首先&#xff0…

Q1季度空气净化器行业线上市场(京东天猫淘宝)销售数据分析

随着人们对健康生活方式的追求&#xff0c;以及消费升级的推动&#xff0c;空气净化器市场正在逐步恢复增长态势。 根据鲸参谋数据显示&#xff0c;今年Q1季度空气净化器在线上市场&#xff08;京东天猫淘宝&#xff09;综合销量超90万件&#xff0c;同比去年增长4%&#xff1…

【配置】IT-Tools部署

github地址 docker运行如下&#xff0c;记得打开云服务器的9090端口 docker run -d --name it-tools --restart unless-stopped -p 9090:80 corentinth/it-tools:latestip:9090查看&#xff0c;很香大部分工具都有

react18【系列实用教程】useEffect —— 副作用操作 (2024最新版)

什么是副作用操作&#xff1f; useEffect 用于编写由渲染本身引起的对接组件外部的操作&#xff08;官方称呼为&#xff1a;副作用操作&#xff09; 以下情况会触发页面渲染 初次加载页面&#xff08;组的挂载&#xff09;响应式变量发生变化&#xff0c;触发页面根据新值重新…