Android InputFlinger simple analysis (mainly analysis Touch)
First, it has a service, InputManagerService.
InputManagerService starts
startOtherServices@SystemServer
...
inputManager = new InputManagerService(context);
wm = WindowManagerService.main(context, inputManager,
mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
!mFirstBoot, mOnlyCore);
...
ServiceManager.addService(Context.INPUT_SERVICE, inputManager);
...
inputManager.setWindowManagerCallbacks(wm.getInputMonitor());
inputManager.start();
...
Take a look at the constructor:
public InputManagerService(Context context) {
...
mPtr = nativeInit(this, mContext,mHandler.getLooper().getQueue());
...
}
This mPrt is then used as the first parameter of other native functions. First look at nativeInit
(位置base/services/core/jni/com_android_server_input_InputManagerService.cpp)
static jlong nativeInit(JNIEnv* env, jclass clazz,
jobject serviceObj, jobject contextObj, jobject messageQueueObj) {
sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
...
NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,messageQueue->getLooper());
im->incStrong(0);
return reinterpret_cast<jlong>(im);
}
A simple understanding is to obtain the message queue of the java layer, then construct a NativeInputManager object, then return to the java layer, and assign it to mPtr.
Previously, startOtherServices@SystemServer also called
inputManager.start();
start()@InputManagerService
...
nativeStart(mPtr);
...
nativeStart:
static void nativeStart(JNIEnv* env, jclass clazz, jlong ptr) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
status_t result = im->getInputManager()->start();
...
}
That is, call the start function with the new NativeInputManager object in nativeInit.
So what is NativeInputManager?
inline sp<InputManager> getInputManager() const { return mInputManager; }
Just go directly to the InputManager to call the start function.
Next, our working directory is concentrated in
frameworks/native/services/inputflinger
Start with InputManager::start
...
status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
...
result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
...
The run functions of mDispatcherThread and mReaderThread will call the threadLoop function (it is common sense).
bool InputDispatcherThread::threadLoop() {
mDispatcher->dispatchOnce();
return true;
}
bool InputReaderThread::threadLoop() {
mReader->loopOnce();
return true;
}
Judging from the name, one is dedicated to reading input, and the other is dedicated to distributing input. But why only run it once (loopOnce)?
Our main concern this time is the touch event, namely MotionEvent.
InputDispatcher.h
struct MotionEntry : EventEntry {
Motion is a type of Event!
virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args);
virtual void notifyKey(const NotifyKeyArgs* args);
virtual void notifyMotion(const NotifyMotionArgs* args);
virtual void notifySwitch(const NotifySwitchArgs* args);
virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args);
The functions of these functions should be similar, but the types of Events are different.
The call was not found in InputDispatcher.cpp. Um! yes! The search results are as follows:
InputReader.cpp:2551: getListener()->notifyMotion(&args);
...
getListener@InputReader:
class InputReader : public InputReaderInterface {
...
virtual InputListenerInterface* getListener();
InputDispatcher.h:
class InputDispatcherInterface : public virtual RefBase, public InputListenerInterface
...
class InputDispatcher : public InputDispatcherInterface
Looking back at the constructor of InputManager, you will understand
InputManager::InputManager
InputManager::InputManager(
const sp<EventHubInterface>& eventHub,
const sp<InputReaderPolicyInterface>& readerPolicy,
const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
mDispatcher = new InputDispatcher(dispatcherPolicy);
//注意第三个参数
mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
initialize();
}
The constructor of InputReader:
InputReader::InputReader(const sp<EventHubInterface>& eventHub,
const sp<InputReaderPolicyInterface>& policy,
//这个
const sp<InputListenerInterface>& listener) :
mContext(this), mEventHub(eventHub), mPolicy(policy),
mGlobalMetaState(0), mGeneration(1),
mDisableVirtualKeysTimeout(LLONG_MIN), mNextTimeout(LLONG_MAX),
mConfigurationChangesToRefresh(0) {
mQueuedListener = new QueuedInputListener(listener);
and
InputListenerInterface* InputReader::ContextImpl::getListener() {
return mReader->mQueuedListener.get();
}
In general, the call to the notifyMotion function comes from InputReader. After reading the input, notify!
InputReader Analysis
InputReader::loopOnce
//引出一个EventHub,getEvents也非常类似平时在adb shell中使用的命令
size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
...
processEventsLocked(mEventBuffer, count);
...
InputReader::processEventsLocked
void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {
for (const RawEvent* rawEvent = rawEvents; count;) {
...
//type不是
//DEVICE_ADDED,DEVICE_REMOVED,FINISHED_DEVICE_SCAN这三个
if(type < EventHubInterface::FIRST_SYNTHETIC_EVENT) {
...
processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
}
}
}
Let's take a look at a log that loads the Touch device at boot
V/EventHub( 1691): EventHub::readNotify nfd: 69
V/EventHub( 1691): Opening device: /dev/input/event7
V/EventHub( 1691): Created descriptor: raw=:0000:0000:name:Atmel maXTouch Touchscreen, cooked=37118057a971f33d58dfff27cf9e19bbe8914aca
V/EventHub( 1691): add device 7: /dev/input/event7
V/EventHub( 1691): bus: 0018
V/EventHub( 1691): vendor 0000
V/EventHub( 1691): product 0000
V/EventHub( 1691): version 0000
V/EventHub( 1691): name: "Atmel maXTouch Touchscreen"
V/EventHub( 1691): location: "i2c-5-004a/input0"
V/EventHub( 1691): unique id: ""
V/EventHub( 1691): descriptor: "37118057a971f33d58dfff27cf9e19bbe8914aca"
V/EventHub( 1691): driver: v1.0.1
D/EventHub( 1691): No input device configuration file found for device 'Atmel maXTouch Touchscreen'.
I/EventHub( 1691): New device: id=7, fd=213, path='/dev/input/event7', name='Atmel maXTouch Touchscreen', classes=0x14, configuration='', keyLayout='', keyCharacterMap='', builtinKeyboard=false, wakeMechanism=EPOLLWAKEUP, usingClockIoctl=true
V/EventHub( 1691): Reporting device opened: id=7, name=/dev/input/event7
V/InputReader( 1691): ZDQ after mEventHub->getEvents
//DEVICE_ADDED = 0x10000000
//这次调用来自mEventHub->getEvents
V/InputReader( 1691): ZDQ InputReader::processEventsLocked type = 10000000
//mode 1:DEVICE_MODE_DIRECT
I/InputReader( 1691): Device reconfigured: id=7, name='Atmel maXTouch Touchscreen', size 1920x720, orientation 0, mode 1, display id 0
I/InputReader( 1691): Device added: id=7, name='Atmel maXTouch Touchscreen', sources=0x00005002
//FINISHED_DEVICE_SCAN = 0x30000000
V/InputReader( 1691): ZDQ InputReader::processEventsLocked type = 30000000
Then, let's take a look at the log of a single touch event to warm up
getevent -lr /dev/input/event7
EV_ABS ABS_MT_TRACKING_ID 00000069
EV_ABS ABS_MT_POSITION_X 00000608
EV_ABS ABS_MT_POSITION_Y 00000075
EV_KEY BTN_TOUCH DOWN
EV_ABS ABS_X 00000608
EV_ABS ABS_Y 00000075
EV_SYN SYN_REPORT 00000000 rate 0
EV_ABS ABS_MT_TRACKING_ID ffffffff
EV_KEY BTN_TOUCH UP
EV_SYN SYN_REPORT 00000000 rate 13
The first column is rawEvent->type
The second column is rawEvent->code, which is parsed in the function MultiTouchMotionAccumulator::process
The third column is value
The whole event is parsed out as follows:
1.ABS_MT_TRACKING_ID = 0x00000069,坐标为(0x608,0x75)即
(1544,117)的一个DOWN事件
2.ABS_MT_TRACKING_ID = ffffffff
SYN_REPORT:似乎这个组合(EV_SYN,SYN_REPORT)之后就是下一个事件了
很多的process里头都是这么写的:
if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
sync(rawEvent->when);
}
InputReader::processEventsForDeviceLocked之后
1.InputDevice::process
Process all of the events in order for each mapper.
We cannot simply ask each mapper to process them in bulk because mappers may
have side-effects that must be interleaved. For example, joystick movement events and gamepad button presses are handled by different mappers but they should be dispatched in the order received.
for (size_t i = 0; i < numMappers; i++) {
InputMapper* mapper = mMappers[i];
mapper->process(rawEvent);
}
2.mapper->process
class TouchInputMapper : public InputMapper
class SingleTouchInputMapper : public TouchInputMapper
class MultiTouchInputMapper : public TouchInputMapper
MultiTouchInputMapper::process
void MultiTouchInputMapper::process(const RawEvent* rawEvent) {
TouchInputMapper::process(rawEvent);
mMultiTouchMotionAccumulator.process(rawEvent);
}
TouchInputMapper::process:
void TouchInputMapper::process(const RawEvent* rawEvent) {
//应该是给鼠标用的
//专门负责EV_KEY BTN_LEFT/BTN_MIDDLE/BTN_RIGHT...这种
mCursorButtonAccumulator.process(rawEvent);
//EV_REL REL_WHEEL/REL_HWHEEL
mCursorScrollAccumulator.process(rawEvent);
//专门负责EV_KEY BTN_TOUCH...
//触摸事件就这
//EV_KEY BTN_TOUCH的时候,就一件事:
//mBtnTouch = rawEvent->value;
mTouchButtonAccumulator.process(rawEvent);
//开始报点
if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
sync(rawEvent->when);
}
...
}
TouchInputMapper::sync
...
// Sync touch state.
bool havePointerIds = true;
//mCurrentRawPointerData会在这里被清掉
mCurrentRawPointerData.clear();
//纯虚函数
syncTouch(when, &havePointerIds);
...
//加工点的数据
cookPointerData();
Since syncTouch is a pure virtual function, the implementation of syncTouch is available in SingleTouchInputMapper and MultiTouchInputMapper.
How to call this place?
In the boot log I saw earlier
I/EventHub( 1691): New device: id=7, fd=213, path='/dev/input/event7', name='Atmel maXTouch Touchscreen', classes=0x14, configuration='', keyLayout='', keyCharacterMap='', builtinKeyboard=false, wakeMechanism=EPOLLWAKEUP, usingClockIoctl=true
You can see classes=0x14
EventHub.h
/* The input device is a touchscreen or a touchpad (either single-touch or multi-touch). */
INPUT_DEVICE_CLASS_TOUCH = 0x00000004,
/* The input device is a multi-touch touchscreen. */
INPUT_DEVICE_CLASS_TOUCH_MT = 0x00000010,
And CreateDeviceLocked is called in InputReader::addDeviceLocked
InputReader::createDeviceLocked:
// Touchscreens and touchpad devices.
if (classes & INPUT_DEVICE_CLASS_TOUCH_MT) {
device->addMapper(new MultiTouchInputMapper(device));
} else if (classes & INPUT_DEVICE_CLASS_TOUCH) {
device->addMapper(new SingleTouchInputMapper(device));
}
That is to say, in this case, the SingleTouchInputMapper class is basically useless.
Then the previous question is gone, the call is
//这个函数通过mMultiTouchMotionAccumulator(多点触控累加器?什么鬼?)给mCurrentRawPointerData赋了值!
void MultiTouchInputMapper::syncTouch(nsecs_t when, bool* outHavePointerIds) {
//Slot?槽?
size_t inCount = mMultiTouchMotionAccumulator.getSlotCount();//configure的时候赋值,这里是16
size_t outCount = 0;
for (size_t inIndex = 0; inIndex < inCount; inIndex++) {
const MultiTouchMotionAccumulator::Slot* inSlot =
mMultiTouchMotionAccumulator.getSlot(inIndex);
if (!inSlot->isInUse()) {
//16个slot中,not inUse的全在这continue!只有inUse的才会赋值给mCurrentRawPointerData
continue;
}
...
RawPointerData::Pointer& outPointer =
mCurrentRawPointerData.pointers[outCount];
outPointer.x = inSlot->getX();
outPointer.y = inSlot->getY();
...
outCount += 1;
}
...
}
How to judge inSlot->isInUse()?
void MultiTouchMotionAccumulator::process(const RawEvent* rawEvent) {
...
Slot* slot = &mSlots[mCurrentSlot];
case ABS_MT_POSITION_X:
slot->mInUse = true;
slot->mAbsMTPositionX = rawEvent->value;
break;
case ABS_MT_POSITION_Y:
slot->mInUse = true;
...
}
It can be said that it is mainly determined by mCurrentSlot! One of the 16 Slots is selected to become mCurrentSlot, and the corresponding slot becomes inUse!
So when to cancel mInUse? The driver reports such an event:
EV_ABS ABS_MT_TRACKING_ID ffffffff
do not believe you look
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不再inUse了,但是它保留它之前的(那些可以被随后的touches重新使用)内容
slot->mInUse = false;
} else {
slot->mInUse = true;
slot->mAbsMTTrackingId = rawEvent->value;
}
As mentioned earlier, the main body of assigning value to mCurrentRawPointerData is:
mMultiTouchMotionAccumulator.getSlot(inIndex);
and
inline const Slot* getSlot(size_t index) const { return &mSlots[index]; }
There is only one place for assignment:
void MultiTouchMotionAccumulator::process(const RawEvent* rawEvent) {
if (rawEvent->type == EV_ABS) {
...
switch (rawEvent->code) {
case ABS_MT_POSITION_X:
slot->mInUse = true;
//这么取值
//inline int32_t getX() const { return mAbsMTPositionX; }
slot->mAbsMTPositionX = 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;
...
}
} else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_MT_REPORT) {
// MultiTouch Sync: The driver has returned all data for *one* of the pointers.
mCurrentSlot += 1;
}
}
MultiTouchMotionAccumulator::process
if (rawEvent->type == EV_ABS) {
bool newSlot = false;
//log来看,这个值为true
if (mUsingSlotsProtocol) {
if (rawEvent->code == ABS_MT_SLOT) {
//底层会上报多个值,一跟手指0,两根0,1...
mCurrentSlot = rawEvent->value;
newSlot = true;
}
} else if (mCurrentSlot < 0) {
mCurrentSlot = 0;
}
//开始解析rawEvent->code
Slot* slot = &mSlots[mCurrentSlot];
switch (rawEvent->code) {
...
case ABS_MT_POSITION_X:
slot->mInUse = true;
slot->mAbsMTPositionX = rawEvent->value;
break;
...
}
mUsingSlotsProtocol is the B protocol here, for details, please refer to this article:
http://www.cnblogs.com/ljf181275034/articles/3343222.html
Then look at the cookPointerData of the processing point data mentioned earlier
void TouchInputMapper::cookPointerData() {
uint32_t currentPointerCount = mCurrentRawPointerData.pointerCount;
...
// Walk through the the active pointers and map device coordinates onto
// surface coordinates and adjust for display orientation.
for (uint32_t i = 0; i < currentPointerCount; i++) {
const RawPointerData::Pointer& in = mCurrentRawPointerData.pointers[i];
//接下来就是从in中去解析
//Size
//Pressure
//Tilt and Orientation
//Distance
//Coverage
//Adjust X, Y, and coverage coords for surface orientation.(根据surface orientation调整x,y坐标)
// Write output coords.
PointerCoords& out = mCurrentCookedPointerData.pointerCoords[i];
out.clear();
out.setAxisValue(AMOTION_EVENT_AXIS_X, x);
out.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
...
// Write output properties.
// Write id index.
}
...
}
cookPointerData processes mCurrentRawPointerData into mCurrentCookedPointerData!
After that is dispatchTouches(when, policyFlags)
void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) {
BitSet32 currentIdBits = mCurrentCookedPointerData.touchingIdBits;
BitSet32 lastIdBits = mLastCookedPointerData.touchingIdBits;
...
if (currentIdBits == lastIdBits) {
//没有pointer id变化,所以,这是一个move事件
//注意是所有点(xxxxBits用bit位包含所有点的id)
...
dispatchMotion(when, policyFlags, mSource,
AMOTION_EVENT_ACTION_MOVE, 0, metaState, buttonState,
AMOTION_EVENT_EDGE_FLAG_NONE,
mCurrentCookedPointerData.pointerProperties,
mCurrentCookedPointerData.pointerCoords,
mCurrentCookedPointerData.idToIndex,
currentIdBits, -1,
mOrientedXPrecision, mOrientedYPrecision, mDownTime);
...
} else {
//如果lastIdBits == currentIdBits,upIdBits就是0
//但是这个else是!=,所以这个结果就是把少掉的点(即已经up的点)找出来
BitSet32 upIdBits(lastIdBits.value & ~currentIdBits.value);
//同理,这个是多出来的点(down)
BitSet32 downIdBits(currentIdBits.value & ~lastIdBits.value);
//
BitSet32 moveIdBits(lastIdBits.value & currentIdBits.value);
//根据不同的情况,分别做
//AMOTION_EVENT_ACTION_POINTER_UP
//AMOTION_EVENT_ACTION_MOVE
//AMOTION_EVENT_ACTION_POINTER_DOWN
...
dispatchMotion...
}
TouchInputMapper::dispatchMotion:
...
//0x5(DOWN)的ACTION经过这处理,变成0x105
//同理,0x6变成0x106
if (changedId >= 0 && id == uint32_t(changedId)) {
action |= pointerCount << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
}
...
NotifyMotionArgs args(when, getDeviceId(), source, policyFlags,
action, flags, metaState, buttonState, edgeFlags,
mViewport.displayId, pointerCount, pointerProperties, pointerCoords,xPrecision, yPrecision, downTime);
getListener()->notifyMotion(&args);
...
Briefly summarize the whole process:
1.eventhub report:
(When multiple points) EV_ABS ABS_MT_SLOT 00000000
if (rawEvent->code == ABS_MT_SLOT) {
mCurrentSlot = rawEvent->value;//设置当前slot为0
newSlot = true;
}
EV_ABS ABS_MT_TRACKING_ID id
调用MultiTouchMotionAccumulator::process
mSlots[0]->mAbsMTTrackingId = id//Assign mSlots[mCurrentSlot]
2.EV_ABS ABS_MT_POSITION_X x
调用MultiTouchMotionAccumulator::process
mSlots[0]->mAbsMTPositionX = x//Assign mSlot[mCurrentSlot]s
3.EV_ABS ABS_MT_POSITION_Y y
调用MultiTouchMotionAccumulator::process
mSlots[0]->mAbsMTPositionY = y//Assign mSlots[mCurrentSlot]
4.EV_SYN SYN_REPORT
TouchInputMapper::process
TouchInputMapper::sync
(1)MultiTouchInputMapper::syncTouch
对mCurrentRawPointerData.pointers[0]进行赋值.
(2)MultiTouchInputMapper::cookPointerData();
加工mCurrentRawPointerData.pointers[0]数据
(3)dispatchTouches
dispatchMotion
getListener()->notifyMotion(&args);
5.EV_ABS ABS_MT_TRACKING_ID ffffffff
调用MultiTouchMotionAccumulator::process
mSlots[0]->mAbsMTTrackingId = ffffffff (out of range, so -1)
so here it is
//打日志得到的
//mUsingSlotsProtocol = 1,rawEvent->value = -1
if (mUsingSlotsProtocol && rawEvent->value < 0) {
slot->mInUse = false;
}
进而isInUse函数就会返回false啦!
6.EV_SYN SYN_REPORT 00000000
TouchInputMapper::process
TouchInputMapper::sync
(1)MultiTouchInputMapper::syncTouch
走入
if (!inSlot->isInUse()) {
continue;
}
(2)cookPointerData这里什么都没干
(3)dispatchTouches
dispatchMotion
getListener()->notifyMotion(&args);
InputDispatcher analysis
void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) {
...
needWake = enqueueInboundEventLocked(newEntry);//是否需要唤醒
...
if (needWake) {
//
mLooper->wake();//如果需要唤醒,唤醒之,具体一会儿说
}
...
}
Let's first look at the judgment of needWake
bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) {
...
//mInboundQueue(入站队列)为空的时候,需要唤醒
bool needWake = mInboundQueue.isEmpty();
mInboundQueue.enqueueAtTail(entry);
...
switch (entry->type) {
case EventEntry::TYPE_KEY: {
//notifyKey走这一支
}
...
case EventEntry::TYPE_MOTION: {
...
sp<InputWindowHandle> touchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y);
if (touchedWindowHandle != NULL
&& touchedWindowHandle->inputApplicationHandle
!= mInputTargetWaitApplicationHandle) {
// User touched a different application than the one we are waiting on.
// Flag the event, and start pruning the input queue.
mNextUnblockedEvent = motionEntry;
needWake = true;
}
...
}
...
}
A brief summary is that it needs to be woken up when it was sleeping and mInboundQueue was empty. It needs to wake up when the user touches another application.
As mentioned earlier, when you need to wake up, you will call mLooper->wake()
After this process, even guessing with Google, the conclusion is that it will be executed
bool InputDispatcherThread::threadLoop() {
mDispatcher->dispatchOnce();
return true;
}
void InputDispatcher::dispatchOnce() {
...
// Run a dispatch loop if there are no pending commands.
// The dispatch loop might enqueue commands to run afterwards.
if (!haveCommandsLocked()) {
dispatchOnceInnerLocked(&nextWakeupTime);
}
...
//总之参数就是一个很大很大的超时时间
mLooper->pollOnce(timeoutMillis);
}
void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
...
case EventEntry::TYPE_MOTION: {
done = dispatchMotionLocked(currentTime, typedEntry,
&dropReason, nextWakeupTime);
}
...
}
bool InputDispatcher::dispatchMotionLocked(
nsecs_t currentTime, MotionEntry* entry, DropReason* dropReason, nsecs_t* nextWakeupTime) {
...
bool isPointerEvent = entry->source & AINPUT_SOURCE_CLASS_POINTER;
// Identify targets.
Vector<InputTarget> inputTargets;
if (isPointerEvent) {
// Pointer event. (eg. touchscreen)
//找到目标窗口
//这个方法很复杂,先这样吧
injectionResult = findTouchedWindowTargetsLocked(currentTime,
entry, inputTargets, nextWakeupTime, &conflictingPointerActions);
}
...
//分发MotionEvent啦!
dispatchEventLocked(currentTime, entry, inputTargets);
}
void InputDispatcher::dispatchEventLocked(nsecs_t currentTime,
EventEntry* eventEntry, const Vector<InputTarget>& inputTargets) {
...
//戳用户Activity?暂时不知道是干嘛的
pokeUserActivityLocked(eventEntry);
//
for (size_t i = 0; i < inputTargets.size(); i++) {
const InputTarget& inputTarget = inputTargets.itemAt(i);
ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel);
if (connectionIndex >= 0) {
//mConnectionsByFd在InputDispatcher::registerInputChannel中被关联
//mConnectionsByFd.add(fd, connection);
sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget);
}
}
...
}
InputDispatcher::registerInputChannel related connection and fd will be discussed later, let's continue to look at it first
void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget) {
...
// Not splitting. Enqueue dispatch entries for the event as is.
enqueueDispatchEntriesLocked(currentTime, connection, eventEntry, inputTarget);
}
void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime,
const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget) {
//这时候已经变成outbound了
bool wasEmpty = connection->outboundQueue.isEmpty();
// Enqueue dispatch entries for the requested modes.
enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT);
...
// If the outbound queue was previously empty, start the dispatch cycle going.
//之前空,后来不空
if (wasEmpty && !connection->outboundQueue.isEmpty()) {
startDispatchCycleLocked(currentTime, connection);
}
}
void InputDispatcher::enqueueDispatchEntryLocked(
const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget,
int32_t dispatchMode) {
// This is a new event.
// Enqueue a new dispatch entry onto the outbound queue for this connection.
//构造一个dispatchEntry
DispatchEntry* dispatchEntry = new DispatchEntry(eventEntry, // increments ref
inputTargetFlags, inputTarget->xOffset, inputTarget->yOffset,
inputTarget->scaleFactor);
...
case EventEntry::TYPE_MOTION: {
//对dispatchEntry做一些处理
}
...
//将dispatchEntry加到outboundQueue的队尾
connection->outboundQueue.enqueueAtTail(dispatchEntry);
}
In enqueueDispatchEntriesLocked, it will be called when connection->outboundQueue is not empty
void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
const sp<Connection>& connection) {
while (connection->status == Connection::STATUS_NORMAL
&& !connection->outboundQueue.isEmpty()) {
...
case EventEntry::TYPE_KEY:
status = connection->inputPublisher.publishKeyEvent(...
...
// Publish the motion event.
case EventEntry::TYPE_MOTION:
status = connection->inputPublisher.publishMotionEvent(
...
//出站dispatchEntry
connection->outboundQueue.dequeue(dispatchEntry);
...
//将dispatchEntry加入connection->waitQueue
connection->waitQueue.enqueueAtTail(dispatchEntry);
...
}
}
publishKeyEvent and publishMotionEvent enter InputChannel
(located in frameworks/native/libs/input/InputTransport.cpp)!
The InputDispathcher part is finished!
java layer other
1.View implements
KeyEvent.Callback
onKeyDown
onKeyLongPress
onKeyUp
等接口
KeyEvent extends InputEvent
Keystroke input is a subset of all input! understandable
2.MotionEvent
(parameters of dispatchTouchEvent)
Details of the touch event
MotionEvent KeyEvent extends InputEvent
The native method that is basically called
3. Call stack
Java layer:
What did jni call up?:
ViewRootImpl.java:WindowInputEventReceiver:
onInputEvent
ViewRootImpl.java:enqueueInputEvent
ViewRootImpl.java:processPointerEvent
->View.java:dispatchPointerEvent
->View.java:dispathTouchEvent
->listenerInfo.mOnTouchListener.onTouch
InputEventReceiver.java
Functions like dispatchInputEvent() are Called from native code
Each touch will be called by the native layer, and then the event will be passed up, such as the down, move, and up events of a click event. I simply think this is one of the most important passes!
callback point:
android_view_InputEventReceiver.cpp:
NativeInputEventReceiver::consumeEvents
env->CallVoidMethod(receiverObj.get(),
gInputEventReceiverClassInfo.dispatchBatchedInputEventPending);