Android InputFlinger simple analysis (mainly analysis Touch)

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);

Guess you like

Origin blog.csdn.net/bberdong/article/details/78626366