android touch event delivery/inputflinger process analysis

Due to project needs, I need to understand the Android touch data transfer process. After reading the code, record the process for easy reference later.

This article is based on the Android11 ​​open source source code. All codes can be viewed and downloaded at the official address provided by aosp. The specific process of Android11 ​​may be somewhat inconsistent with other Android versions.

If there is something wrong in the article, students are welcome to point it out and discuss it together~

Analyze the touch data transmission mechanism of Android from 4 lines (4 directions). Along the way, we mainly focus on the route of touch data from the kernel to the app. We should open up this road without paying attention to the details and how the touch data is determined to be given to which device. activity (window, view),

Table of contents

ViewRootImpl registers InputChannel

InputFlinger reads touch data from kernel

InputFlinger sends touch data to View

ViewRootImpl receives touch data and gives it to the app


In one sentence, when the app adds a window to wms, wms creates a pair of sockets, encapsulated with InputChannel, one for the app and one for InputFlinger. Then InputFlinger sends the touch data to the app through this unix socket fd.

Take button as an example. The overall process is roughly as follows. After the app is started, when the window is added to wms on the ViewRootImpl side, an InputChannel will be created and brought with it. Wms will act as an intermediary and pass this InputChannel to InputFlinger. After clicking the button, After inputflinger reads the data from the kernel, it will give the data to ViewRootImpl through this InputChannel, and then give it to View through ViewPostImeInputStage, and then create a PerformClick to execute through View.post(), calling back the onClick() interface of the button. (How to know which activity, which window and which View is given will be clarified in the subsequent analysis process)

ViewRootImpl registers InputChannel

After app activity starts, PhoneWindow is created and then when adding window to wms, InputChannel is created in ViewRootImpl and passed as an output parameter. WMS creates 2 InputChannels through socket pair, one as client and one as server. The client passes the fd to ViewRootImpl. The InputChannel that comes over, the server passes to InputFlinger, InputFlinger creates a Connection and saves it, and then uses it to return the touch data to the app.

Look at the picture below:

Take a look at the code:

//frameworks/base/core/java/android/view/ViewRootImpl.java
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
           int userId) {
       //......
      
      //创建 InputChannel 
      inputChannel = new InputChannel();
      
      //在添加窗口的时候通过binder ipc一起给到wms
      //note1
      res = mWindowSession.addToDisplayAsUser(mWindow, mSeq, mWindowAttributes,
      getHostVisibility(), mDisplay.getDisplayId(), userId, mTmpFrame,
      mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
      mAttachInfo.mDisplayCutout, inputChannel,
      mTempInsets, mTempControls);
      
      //同时创建WindowInputEventReceiver用来接收touch数据(其实不只touch,
      //应该是包括按键,键盘啥的,我们目前先就只关注touch)
      //note2
      mInputEventReceiver = new WindowInputEventReceiver(inputChannel,
        Looper.myLooper());

      //......
      //创建用于处理input数据的InputStage,
      //note19
     mSyntheticInputStage = new SyntheticInputStage();
     InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage);
     InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage,
        "aq:native-post-ime:" + counterSuffix);
     InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage);
     InputStage imeStage = new ImeInputStage(earlyPostImeStage,
        "aq:ime:" + counterSuffix);
     InputStage viewPreImeStage = new ViewPreImeInputStage(imeStage);
     InputStage nativePreImeStage = new NativePreImeInputStage(viewPreImeStage,
        "aq:native-pre-ime:" + counterSuffix);
     mFirstInputStage = nativePreImeStage;
     mFirstPostImeInputStage = earlyPostImeStage;     
      
}

//frameworks/base/core/java/android/view/IWindowSession.aidl
int addToDisplayAsUser(IWindow window, int seq, in WindowManager.LayoutParams attrs,
            in int viewVisibility, in int layerStackId, in int userId,
            out Rect outFrame, out Rect outContentInsets, out Rect outStableInsets,
            out DisplayCutout.ParcelableWrapper displayCutout, out InputChannel outInputChannel,
            out InsetsState insetsState, out InsetsSourceControl[] activeControls);
//注意这边inputChannel是作为出参传进去的,后面在WindowState那边创建了socket pair后
//会重新给他赋值使其作为接收数据的客户端拥有可以接收touch数据的fd。
//当然这边是跨进程的然后中间是有binder做了处理简化了流程。

Follow mWindowSession.addToDisplayAsUser(),

The mWindowSession instance here is the Session object, which is obtained from wms on the WindowManagerGlobal side, and then passed over when creating ViewRootImpl. You can take a brief look~

//在这边赋值的
//frameworks/base/core/java/android/view/ViewRootImpl.java
public ViewRootImpl(Context context, Display display, IWindowSession session,
        boolean useSfChoreographer) {
//.......
    mWindowSession = session;
//.......       
}

//这边传过来的
public ViewRootImpl(Context context, Display display) {
        this(context, display, WindowManagerGlobal.getWindowSession(),
                false /* useSfChoreographer */);
}

//frameworks/base/core/java/android/view/WindowManagerGlobal.java
public void addView(View view, ViewGroup.LayoutParams params,
        Display display, Window parentWindow, int userId) {
 //......
 
     //创建ViewRootImpl
     root = new ViewRootImpl(view.getContext(), display);
 
 //.......       
 }
 
//这边通过binder ipc从wms那边去获取的,再接着看下wms实现
//frameworks/base/core/java/android/view/WindowManagerGlobal.java
public static IWindowSession getWindowSession() {
    //......
    //从wms那边拿
    sWindowSession = windowManager.openSession(......);
    return sWindowSession;
}

//wms直接new了一个Session对象返回
//frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
public IWindowSession openSession(IWindowSessionCallback callback) {
    return new Session(this, callback);
}


Okay, confirm that mWindowSession is the Session, then go back to mWindowSession.addToDisplayAsUser() in note1, and continue to look at the registration of InputChanne. In fact, it goes to wms through binder ipc.

//frameworks/base/services/core/java/com/android/server/wm/Session.java
public int addToDisplayAsUser(IWindow window, int seq, WindowManager.LayoutParams attrs,
        int viewVisibility, int displayId, int userId, Rect outFrame,
        Rect outContentInsets, Rect outStableInsets,
        DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
        InsetsState outInsetsState, InsetsSourceControl[] outActiveControls) {
    return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outFrame,
            outContentInsets, outStableInsets, outDisplayCutout, outInputChannel,
            outInsetsState, outActiveControls, userId);

Directly call the addWindow() interface of wms



//frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
public int addWindow(Session session, IWindow client, int seq,
        LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame,
        Rect outContentInsets, Rect outStableInsets,
        DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
        InsetsState outInsetsState, InsetsSourceControl[] outActiveControls,
        int requestUserId) {
//......        
    //给WindowState了
    win.openInputChannel(outInputChannel);
//......
}

//接着往WindowState看
//frameworks/base/services/core/java/com/android/server/wm/WindowState.java
void openInputChannel(InputChannel outInputChannel) {
//......
    //创建一对InputChannle 
    InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
    //作为server和client
    mInputChannel = inputChannels[0];
    mClientChannel = inputChannels[1];
    //server端InputFlinger那边
    //这边wms、InputManagerService、InputFlinger 共进程,直接调用后,
    //InputManagerService也是直接调用给到InputFlinger
    mWmService.mInputManager.registerInputChannel(mInputChannel);    
    
    //......
    //client 赋值给出参然后返回到ViewRootImpl作为接收touch数据客户端
    mClientChannel.transferTo(outInputChannel);    
//......
}

In fact, two c++ InputChannels that can communicate are created here, respectively as client and server for communication. They contain two fds created by socket pair respectively. As the client's InputChannel, they are assigned to the java InputChannel passed by ViewRootClient. Do The InputChannel of the server is called to InputFlinger through an in-process function (not binder), and is used to receive and send touch data respectively.

Then look at mWmService.mInputManager.registerInputChannel(),

//frameworks/base/services/core/java/com/android/server/input/InputManagerService.java
public void registerInputChannel(InputChannel inputChannel) {
    if (inputChannel == null) {
        throw new IllegalArgumentException("inputChannel must not be null.");
    }
    nativeRegisterInputChannel(mPtr, inputChannel);
    
 }
 

//frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
static void nativeRegisterInputChannel(JNIEnv* env, jclass /* clazz */, jlong ptr,
                                       jobject inputChannelObj) {
//......
 status_t status = im->registerInputChannel(env, inputChannel);
//......
}

status_t NativeInputManager::registerInputChannel(JNIEnv* /* env */,
                                                  const sp<InputChannel>& inputChannel) {
    ATRACE_CALL();
    return mInputManager->getDispatcher()->registerInputChannel(inputChannel);
}

//到InputDispatcher那边了
//frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel) {
#if DEBUG_REGISTRATION
    ALOGD("channel '%s' ~ registerInputChannel", inputChannel->getName().c_str());
#endif

    { // acquire lock
        std::scoped_lock _l(mLock);
        //查重
        sp<Connection> existingConnection = getConnectionLocked(inputChannel->getConnectionToken());
        if (existingConnection != nullptr) {
            ALOGW("Attempted to register already registered input channel '%s'",
                  inputChannel->getName().c_str());
            return BAD_VALUE;
        }
        
        //这边根据 channel 创建connection ,后面用它来发数据给app
        //note13
        sp<Connection> connection = new Connection(inputChannel, false /*monitor*/, mIdGenerator);

        int fd = inputChannel->getFd();
        //这边注册上,其实就是把fd和connection做为一个键值对保存起来而已,
        //后面发数据的时候从这把connection取出来发数据
        mConnectionsByFd[fd] = connection;
        mInputChannelsByToken[inputChannel->getConnectionToken()] = inputChannel;
        
        //
        mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
    } // release lock

    // Wake the looper because some connections have changed.
    mLooper->wake();
    return OK;
}

Okay, after looking at it all the way, the so-called registration is to save the connection and the corresponding socket fd on the InputFlinger's InputDispatcher (note3) side, and put it in the hash map.

Next, let’s take a look at the process of InputFlinger reading data from the kernel~

InputFlinger reads touch data from kernel

I won’t go into the process of how to start InputFlinger and use epoll to read data from /dev/input/eventX. Interested students can take a look at it themselves (frameworks/native/services/inputflinger). Let’s start with reading the data from the kernel. Please note that the inputflinger mentioned here refers to the inputflinger binder service. Currently, in Android 11, it runs on the system server and is in the same process as the InputManagerService (but the communication between the two still uses binder). The InputManagerService uses jni to create the InputManager object and then registers it as InputFlinger. service, so the actual implementation of InputFlinger here (Android11) is actually the c++ InputManager class, which may be different from other Android versions.

The data is read by a thread on the InputReader side. Alas, obsessive-compulsive disorder makes me feel a little awkward if I don't start from where the source of the process comes from. Otherwise, let's talk about how the data-reading thread starts from the source.

//system server 创建InputManagerService对象
//frameworks/base/services/java/com/android/server/SystemServer.java
private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
//.......
    inputManager = new InputManagerService(context);    
//.......
}

//构造函数里边,用jni调native init 接口
//frameworks/base/services/core/java/com/android/server/input/InputManagerService.java
public InputManagerService(Context context) {
//.......
    mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());
//.......
}
//创建NativeInputManager对象
//frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
static jlong nativeInit(JNIEnv* env, jclass /* clazz */, jobject serviceObj, jobject contextObj,
                        jobject messageQueueObj) {
 //......                       
      NativeInputManager* im =
            new NativeInputManager(contextObj, serviceObj, messageQueue->getLooper());   
 //......                       
 }
 
 //创建InputMnager对象并注册成为inputflinger binder service
 NativeInputManager::NativeInputManager(jobject contextObj, jobject serviceObj,
                                       const sp<Looper>& looper)
      : mLooper(looper), mInteractive(true) {
 //......
    mInputManager = new InputManager(this, this);
    defaultServiceManager()->addService(String16("inputflinger"), mInputManager, false);
 //......     
 }
 
 
//创建完InputManagerService 后,
//system server 接着调用InputManagerService 的start接口    
//frameworks/base/services/java/com/android/server/SystemServer.java
private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
//.......
    inputManager = new InputManagerService(context); 
    
//......
    inputManager.start();
//.......
}

//又走到native那去了
//frameworks/base/services/core/java/com/android/server/input/InputManagerService.java
public void start() {
//......
    nativeStart(mPtr);
//......
}
//调用inputmanager 的start接口
//frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
static void nativeStart(JNIEnv* env, jclass /* clazz */, jlong ptr) {
    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);

    status_t result = im->getInputManager()->start();
}


//frameworks/native/services/inputflinger/InputManager.cpp
status_t InputManager::start() {
//......
    //这边去起从InputReader读数据的线程在这里咱们先忽略,
    //后面再回过头来看 note12
    mDispatcher->start();
    
    //这边去起从kernel读数据线程,接着看
    result = mReader->start();
 //......
}

//frameworks/native/services/inputflinger/reader/InputReader.cpp
//note4
status_t InputReader::start() {
    mThread = std::make_unique<InputThread>(
            "InputReader", [this]() { loopOnce(); }, [this]() { mEventHub->wake(); });
}

void InputReader::loopOnce() {
//......
    //这边把touch数据从kernel读出来(通过/dev/input/eventX)
    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
//......
}

Okay, now that the process of how to read data has been cleared up, let’s see how to send the data to the app~

InputFlinger sends touch data to View

After the data is read out, it is sent to ViewRootImpl through InputChannel. Let’s see what the process is like~

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

    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
    
    //处理数据
    processEventsLocked(mEventBuffer, count);
//......
}

Look at the picture below:

The overall process is probably: When InputDispatcher is initialized, it will create an InputDispatcher thread to continuously read data from a queue called InputDispatcher::mInboundQueue. If there is no data, it will sleep.

After the InputReader thread on note4 reads the touch data from the kernel, it goes through a series of processing (we will not look at the processing for now, just get the process through), puts the touch event into the InputDispatcher::mInboundQueue queue, and then wakes up the InputDispatcher thread. Read the event from the queue, then select which target View (we will focus on this later, let’s not look at it now), and then use the InputChannel registered previously for the View to send the event to ViewRootImpl through the unix socket.

Next, from the code point of view, first look at the process of InputReader reading data from the kernel, and then look at the process of InputDispatcher getting data.

InputReader:

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

    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
    
    //处理数据
    processEventsLocked(mEventBuffer, count);
    
//.......
    
    //note10
    mQueuedListener->flush();
//......
}
//其实上面那2处做的事情,前者是将数据处理后放入QueuedInputListener::mArgsQueue
//后者是将数据出队后继续处理,咱们先看下前者

void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {
//......
   //数据继续给进去
   processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
//......
}

void InputReader::processEventsForDeviceLocked(int32_t eventHubId, const RawEvent* rawEvents,
                                               size_t count) {
 //......
    //走到InputDevice那边了 
    device->process(rawEvents, count);                                            
}

//frameworks/native/services/inputflinger/reader/InputDevice.cpp
void InputDevice::process(const RawEvent* rawEvents, size_t count) {
//......
  //调用每个mapper的process,这边我通过打callstack得到调用的是 MultiTouchInputMapper,
  //TouchInputMapper这边有好几个,具体什么情况走哪个还没有去理清
  for_each_mapper_in_subdevice(rawEvent->deviceId, [rawEvent](InputMapper& mapper) {
             mapper.process(rawEvent);
   });
//......
}

//接着看
//frameworks/native/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
void MultiTouchInputMapper::process(const RawEvent* rawEvent) {
    //调用父类的
    TouchInputMapper::process(rawEvent);

    mMultiTouchMotionAccumulator.process(rawEvent);
}

//frameworks/native/services/inputflinger/reader/mapper/TouchInputMapper.cpp
void TouchInputMapper::process(const RawEvent* rawEvent) {
//.......
  sync(rawEvent->when);
//.......
}

void TouchInputMapper::sync(nsecs_t when) {
//.......
    //顾名思义,看起来是继续处理裸数据
    processRawTouches(false /*timeout*/);
//.......
}

void TouchInputMapper::processRawTouches(bool timeout) {
//......
   //不知为何函数名字要带cook,不过走这没错啦我从callstack看出来的
   cookAndDispatch(mCurrentRawState.when);
//......
}

void TouchInputMapper::cookAndDispatch(nsecs_t when) {
//......
    //走了这里
    dispatchTouches(when, policyFlags);
//.......
}

void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) {
//.......
 dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_DOWN, 0, 0,
                metaState, buttonState, 0,
                mCurrentCookedState.cookedPointerData.pointerProperties,
                mCurrentCookedState.cookedPointerData.pointerCoords,
                mCurrentCookedState.cookedPointerData.idToIndex, dispatchedIdBits,
                downId, mOrientedXPrecision, mOrientedYPrecision, mDownTime);    
//.......    
}

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) {
                                      
 //......                                     
  //这边取出listenner然后调用其 notifyMotion  
  //note5                       
  getListener()->notifyMotion(&args);                                    
//......                                      
}

Among them, the listener side refers to InputReader::mQueuedListener. If it is what it is, interested students can look at the following code snippet. Those who are not interested can skip it and look at the next code snippet.

//本代码端理一下为何上边getListenner拿到的是InputReader::mQueuedListener
//请注意前面note5是在MuitiTouchInputMapper对象里边的,


//frameworks/native/services/inputflinger/reader/InputReader.cpp
InputReader::InputReader(std::shared_ptr<EventHubInterface> eventHub,
                         const sp<InputReaderPolicyInterface>& policy,
                         const sp<InputListenerInterface>& listener)
      : mContext(this), //InputRead在构造的时候,顺便构造其mContext,把自己地址存进mContext
        mEventHub(eventHub),
        mPolicy(policy),
        mGlobalMetaState(0),
        mGeneration(1),
        mNextInputDeviceId(END_RESERVED_ID),
        mDisableVirtualKeysTimeout(LLONG_MIN),
        mNextTimeout(LLONG_MAX),
        mConfigurationChangesToRefresh(0) {
    mQueuedListener = new QueuedInputListener(listener);

    { // acquire lock
        AutoMutex _l(mLock);

        refreshConfigurationLocked(0);
        updateGlobalMetaStateLocked();
    } // release lock
}

//这边mContext为InputReader::ContextImpl类型,看下构造函数
InputReader::ContextImpl::ContextImpl(InputReader* reader)
      : mReader(reader), mIdGenerator(IdGenerator::Source::INPUT_READER) {}
//所以InputReader::mContex为InputReader::ContextImpl类型,
//其mReader 里边存了InputReader note6    


std::shared_ptr<InputDevice> InputReader::createDeviceLocked(
        int32_t eventHubId, const InputDeviceIdentifier& identifier) {
//......
  //这边创建InputDevice,把mContext,也就是InputReader::ContextImpl传进去

  device = std::make_shared<InputDevice>(&mContext, deviceId, bumpGenerationLocked(),
                                               identifier);     
//......        
}

//frameworks/native/services/inputflinger/reader/InputDevice.cpp
InputDevice::InputDevice(InputReaderContext* context, int32_t id, int32_t generation,
                         const InputDeviceIdentifier& identifier)
      : mContext(context),
      //InputReader::ContextImpl存在InputDevice的mContext里边  note7
        mId(id),
        mGeneration(generation),
        mControllerNumber(0),
        mIdentifier(identifier),
        mClasses(0),
        mSources(0),
        mIsExternal(false),
        mHasMic(false),
        mDropUntilNextSync(false) {}
        
//而InputDevice addEventHubDevice里创建了前面note5的MultiTouchInputMapper
void InputDevice::addEventHubDevice(int32_t eventHubId, bool populateMappers) {
//.......
    //InputDevice把自己的地址给了InputDeviceContext,
    //做为其mDevice成员的值,有取出InputReader做为其mContext的值
    std::unique_ptr<InputDeviceContext> contextPtr(new InputDeviceContext(*this, eventHubId));
//......
    //contextPtr的地址在InputMapper的mDeviceContext也保留了一份
    mappers.push_back(std::make_unique<MultiTouchInputMapper>(*contextPtr));
//.......
}

InputDeviceContext::InputDeviceContext(InputDevice& device, int32_t eventHubId)
      : mDevice(device),
      //根据note7  所以这边存的是InputReader::ContextImpl note8
        mContext(device.getContext()),
        mEventHub(device.getContext()->getEventHub()),
        mId(eventHubId),
        mDeviceId(device.getId()) {}


//contextPtr的地址在InputMapper的mDeviceContext也保留了一份,
//也就是mDeviceContext指向的是InputDeviceContext对象  note9
//MultiTouchInputMapper、TouchInputMapper、InputMapper三者为孙、父、爷爷的继承关系
MultiTouchInputMapper::MultiTouchInputMapper(InputDeviceContext& deviceContext)
      : TouchInputMapper(deviceContext) {}

TouchInputMapper::TouchInputMapper(InputDeviceContext& deviceContext)
      : InputMapper(deviceContext),
        mSource(0),
        mDeviceMode(DEVICE_MODE_DISABLED),
        mRawSurfaceWidth(-1),
        mRawSurfaceHeight(-1),
        mSurfaceLeft(0),
        mSurfaceTop(0),
        mPhysicalWidth(-1),
        mPhysicalHeight(-1),
        mPhysicalLeft(0),
        mPhysicalTop(0),
        mSurfaceOrientation(DISPLAY_ORIENTATION_0) {}      
 
 InputMapper::InputMapper(InputDeviceContext& deviceContext) : mDeviceContext(deviceContext) {}
      
//好了,咱们可以再回头来看note5的getListener了
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) {
                                      
 //......                                     
  //note5                       
  getListener()->notifyMotion(&args);                                    
//......                                      
}

//frameworks/native/services/inputflinger/reader/mapper/InputMapper.h
inline InputListenerInterface* getListener() { return getContext()->getListener(); }

inline InputReaderContext* getContext() { return mDeviceContext.getContext(); }

//根据note9,走到InputDeviceContext里边,接着看
//framworks/native/services/inputflinger/reader/include/InputDevice.h
inline InputReaderContext* getContext() { return mContext; }
//根据note8,这边mContext就是前面构造InputDevice时传进来的InputReader::ContextImpl呀~

//接着看
//frameworks/native/services/inputflinger/reader/InputReader.cpp
InputListenerInterface* InputReader::ContextImpl::getListener() {
    return mReader->mQueuedListener.get();
}

Okay, let’s confirm that what we got here is InputReader::mQueuedListener. It’s a bit convoluted. In fact, you can skip the above paragraph and continue to the following paragraph.

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

Here, put the touch data into this queue called mArgsQueue (implemented with vector). Since there is entry into the queue, there must be dequeue. But at this point, how can we put the former in InputReader::loopOnce() at the beginning of this section? After reading it, let’s look at the latter to see what was done after the data was dequeued. Let’s look at it from note10.

//frameworks/native/services/inputflinger/include/InputListener.h
void QueuedInputListener::flush() {
    size_t count = mArgsQueue.size();
    for (size_t i = 0; i < count; i++) {
    //把每个数据取出来,调用其notify接口,
        NotifyArgs* args = mArgsQueue[i];
        args->notify(mInnerListener);
        delete args;
    }
    mArgsQueue.clear();
}
//note11
//其中,这边mInnerListener 是InputClassifier,
// 而InputClassifier里边mListener装着InputDispatcher
//这部分简单一些,感兴趣的同学可以自己从下面这个地方开始跟一下~
//frameworks/native/services/inputflinger/InputManager.cpp
InputManager::InputManager(
        const sp<InputReaderPolicyInterface>& readerPolicy,
        const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
    mDispatcher = createInputDispatcher(dispatcherPolicy);
    mClassifier = new InputClassifier(mDispatcher);
    mReader = createInputReader(readerPolicy, mClassifier);
}

//好了言归正传接着看数据怎么传的,
//frameworks/native/services/inputflinger/InputListener.cpp
void NotifyMotionArgs::notify(const sp<InputListenerInterface>& listener) const {
    listener->notifyMotion(this);
}

//走到InputClassifier那去了
//frameworks/native/services/inputflinger/InputClassifier.cpp
void InputClassifier::notifyMotion(const NotifyMotionArgs* args) {
    std::scoped_lock lock(mLock);
    // MotionClassifier is only used for touch events, for now
    const bool sendToMotionClassifier = mMotionClassifier && isTouchEvent(*args);
    if (!sendToMotionClassifier) {
        mListener->notifyMotion(args);
        return;
    }

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

//而这边InputClassifier::mListener 就是InputDispatcher,接着看

//frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
void InputDispatcher::notifyMotion(const NotifyMotionArgs* 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);
        //数据入队
        needWake = enqueueInboundEventLocked(newEntry);    
        
        if (needWake) {
            //唤醒读数据的线程将数据出队
            mLooper->wake();
        }
//......
}

//接着看
bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) {
//......
      //数据在这又入队了
      mInboundQueue.push_back(entry);   
//......
}

Okay, now we see data entering the InputDispatcher::mInboundQueue queue. Let’s see where it is dequeued.

//从note12咱们接着看,
//frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
status_t InputDispatcher::start() {
    if (mThread) {
        return ALREADY_EXISTS;
    }
    mThread = std::make_unique<InputThread>(
            "InputDispatcher", [this]() { dispatchOnce(); }, [this]() { mLooper->wake(); });
    return OK;
}

//创建了一个线程去跑dispatchOnce()函数,
//没事件时睡下去,有事件时被前面的入队事件唤醒后开始工作
void InputDispatcher::dispatchOnce() {
//.......
     dispatchOnceInnerLocked(&nextWakeupTime);
//.......
}

//接着看
void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
//.......
    done = dispatchMotionLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
//......
}

bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, MotionEntry* entry,
                                           DropReason* dropReason, nsecs_t* nextWakeupTime) {
//.......                                           
   //TODO 这边会去选出到底是要把touch数据给到哪个View,选完装到inputTargets里边
   //具体咋选的后面再单独研究下
   injectionResult =
         findTouchedWindowTargetsLocked(currentTime, *entry, inputTargets, nextWakeupTime,
                                               &conflictingPointerActions); 
//......   
   //选完View后,接着继续发数据
   dispatchEventLocked(currentTime, entry, inputTargets);                                        
//......                                           
}

void InputDispatcher::dispatchEventLocked(nsecs_t currentTime, EventEntry* eventEntry,
                                          const std::vector<InputTarget>& inputTargets) {
//.......
    //根据inputTarget取出前面note13 注册的connection,其中里边包含用来和View通信的InputChannel
    sp<Connection> connection =
           getConnectionLocked(inputTarget.inputChannel->getConnectionToken());
           
    prepareDispatchCycleLocked(currentTime, connection, eventEntry, inputTarget);
                                           
//.......                                          
}

void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
                                                 const sp<Connection>& connection,
                                                 EventEntry* eventEntry,
                                                 const InputTarget& inputTarget) {
 //.......                                                
     //从这进去,接着看
     enqueueDispatchEntriesLocked(currentTime, connection, splitMotionEntry, inputTarget);                                                
//......                                                 
}

void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime,
                                                   const sp<Connection>& connection,
                                                   EventEntry* eventEntry,
                                                   const InputTarget& inputTarget) {
 //.......                                                  
     //从这进去                                          
     startDispatchCycleLocked(currentTime, connection);                                                  
}

void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
                                               const sp<Connection>& connection) {
//......                                               
    status =connection->inputPublisher.publishKeyEvent(dispatchEntry->seq, dispatchEntry->resolvedEventId,
                                                 keyEntry->deviceId, keyEntry->source,
                                                 keyEntry->displayId, std::move(hmac),
                                                 dispatchEntry->resolvedAction,
                                                 dispatchEntry->resolvedFlags, keyEntry->keyCode,
                                                 keyEntry->scanCode, keyEntry->metaState,
                                                 keyEntry->repeatCount, keyEntry->downTime,
                                                 keyEntry->eventTime);
//....
}

//走到InputPublisher去了,看下
//frameworks/native/libs/input/InputTransport.cpp
status_t InputPublisher::publishKeyEvent(uint32_t seq, int32_t eventId, int32_t deviceId,
                                         int32_t source, int32_t displayId,
                                         std::array<uint8_t, 32> hmac, int32_t action,
                                         int32_t flags, int32_t keyCode, int32_t scanCode,
                                         int32_t metaState, int32_t repeatCount, nsecs_t downTime,
                                         nsecs_t eventTime) {
//.......                                         
     //好了到底了,这边通过InputChannel用unix socket把数据发给View
     return mChannel->sendMessage(&msg);
}                                      

//至于这把mChannel是怎么来的为何是前面app通过wms传过来的InputChannel,
//咱们从note13  再看下
//frameworks/native/services/inputflinger/dispatcher/Connection.cpp
Connection::Connection(const sp<InputChannel>& inputChannel, bool monitor,
                       const IdGenerator& idGenerator)
      : status(STATUS_NORMAL),
        inputChannel(inputChannel),
        monitor(monitor),
        inputPublisher(inputChannel),
        inputState(idGenerator) {}

//frameworks/native/libs/input/InputTransport.cpp
InputPublisher::InputPublisher(const sp<InputChannel>& channel) :
        mChannel(channel) {
}

//这样子就清楚啦~

Okay, let's see this first when InputFlinger reads data from the kernel and sends it to the View (there is also a TODO left in it, which is sent to that View and then viewed separately). The main work of this part of the process is concentrated on InputReader, InputDisapter, and InputDevice.

Next, let’s take a look at how ViewRootImpl uses InputChannel to give a specific View after receiving touch data~

ViewRootImpl receives touch data and gives it to the app

Let's take a specific example here, such as how onClick() of this button is called after clicking a button.

Let’s do a callstack first:

at com.example.test4.MainActivity$5.onClick(MainActivity.java:192)
at android.view.View.performClick(View.java:7513)
at com.google.android.material.button.MaterialButton.performClick(MaterialButton.java:992)
at android.view.View.performClickInternal(View.java:7490)
at android.view.View.access$3600(View.java:821)
at android.view.View$PerformClick.run(View.java:28564)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:232)
at android.app.ActivityThread.main(ActivityThread.java:8151)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:959)

Here’s another picture:

The overall process is roughly that after ViewRootImpl uses WindowInputEventReceiver to receive the touch data, it loads the data into the queue, then reads the data out of the queue, creates a Runable task, puts the data in and then calls back the button's onClick().

Let's start from the place where InputChannel receives data. Continue with note 2. InputChannel is created during setView. After assigning the fd value of unix socketpair to wms, it will be given as a parameter to create the WindowInputEventReceiver object.

//framworks/base/core/java/android/view/ViewRootImpl.java
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView, int userId) {
//......    
    //note14
    mInputEventReceiver = new WindowInputEventReceiver(inputChannel, Looper.myLooper());
//......
}
//而在ViewRootImpl构造时(setView之前),就会创建一个线程,使用mInputEventReceiver来接收数据并处理
//这操作有点小骚,其实此时mInputEventReceiver为空,所以它搞了个判空操作

final class ConsumeBatchedInputImmediatelyRunnable implements Runnable {
    @Override
    public void run() {
        mConsumeBatchedInputImmediatelyScheduled = false;
        doConsumeBatchedInput(-1);
    }

final ConsumeBatchedInputImmediatelyRunnable mConsumeBatchedInputImmediatelyRunnable = 
    new ConsumeBatchedInputImmediatelyRunnable();
    
boolean doConsumeBatchedInput(long frameTimeNanos) {
    final boolean consumedBatches;
    if (mInputEventReceiver != null) {
        //把数据读出来
        //note16
        consumedBatches = mInputEventReceiver.consumeBatchedInputEvents(frameTimeNanos);
    } else {
        consumedBatches = false;
    }
    //这边去处理数据
    //note18
    doProcessInputEvents();
    return consumedBatches;
}

Among them, the above mInputEventReceiver.consumeBatchedInputEvents() will use the inputChannel to read the data in the native layer, and then call the dispatchInputEvent interface of the java layer from the native to give the data again.

And doProcessInputEvents() then processes the data sent from native and sends it to View.

Let’s first look at the consumeBatchedInputEvents() process:

 //从note14开始看
 
//framworks/base/core/java/android/view/ViewRootImpl.java
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView, int userId) {
//......    
    //inputChannel 做为入参给进去
    mInputEventReceiver = new WindowInputEventReceiver(inputChannel, Looper.myLooper());
//......
}

final class WindowInputEventReceiver extends InputEventReceiver
public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
      //再给到父类InputEventReceiver
     super(inputChannel, looper);
}

//frameworks/base/core/java/android/view/InputEventReceiver.java
public InputEventReceiver(InputChannel inputChannel, Looper looper) {
//......
    //自己保留一份引用
    mInputChannel = inputChannel;
    //再给到native层
    mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),
        inputChannel, mMessageQueue);   
//......    
}

//frameworks/base/core/jni/android_view_InputEventReceiver.cpp
static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,
        jobject inputChannelObj, jobject messageQueueObj) {
//......
    sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
            inputChannelObj);
//......
   //创建一个NativeInputEventReceiver对象,inputChannel作为入参带进去         
    sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,
            receiverWeak, inputChannel, messageQueue);            
//......        
}

NativeInputEventReceiver::NativeInputEventReceiver(JNIEnv* env,
        jobject receiverWeak, const sp<InputChannel>& inputChannel,
        const sp<MessageQueue>& messageQueue) :
        mReceiverWeakGlobal(env->NewGlobalRef(receiverWeak)),
        //保存一份对inputChannel的指针引用
        //note15
        mInputConsumer(inputChannel), mMessageQueue(messageQueue),
        mBatchedInputEventPending(false), mFdEvents(0) {
//......
}

Okay, ViewRootImpl's inputChannel retains a copy in NativeInputEventReceiver's mInputConsumer.

ok, then look at note16

public final boolean consumeBatchedInputEvents(long frameTimeNanos) {
    if (mReceiverPtr == 0) {
        Log.w(TAG, "Attempted to consume batched input events but the input event "
                + "receiver has already been disposed.");
    } else {
        //走到native层
        return nativeConsumeBatchedInputEvents(mReceiverPtr, frameTimeNanos);
    }
    return false;
}
//frameworks/base/core/jni/android_view_InputEventReceiver.cpp
static jboolean nativeConsumeBatchedInputEvents(JNIEnv* env, jclass clazz, jlong receiverPtr,
        jlong frameTimeNanos) {
     //根据note15,这边调用InputChannel把来自InputFlinger的touch数据读出来
    status_t status = receiver->consumeEvents(env, true /*consumeBatches*/, frameTimeNanos,
            &consumedBatch);

    //......
}

status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
        bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) {
 //......       
         //用channel去把数据读出来
        status_t status = mInputConsumer.consume(&mInputEventFactory,
                consumeBatches, frameTime, &seq, &inputEvent,
                &motionEventType, &touchMoveNum, &flag);    
 //......       
 }

//frameworks/native/libs/input/InputTransport.cpp
status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consumeBatches,
                                nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent,
                                int* motionEventType, int* touchMoveNumber, bool* flag) {
 //......                     
    //inputChannel接收数据          
    status_t result = mChannel->receiveMessage(&mMsg);  
 //......
     //根据读到的数据做个转换
     inputEventObj = android_view_MotionEvent_obtainAsCopy(env, motionEvent);  
 //......  
     //调用java接口 dispatchInputEvent,把inputEventObj 传过去
    env->CallVoidMethod(receiverObj.get(),
        gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);    
 //......            
 }
 
 

Return to the java layer and continue reading

//frameworks/base/core/java/android/view/InputEventReceiver.java
private void dispatchInputEvent(int seq, InputEvent event) {
//......
    //注意此时对象是WindowInputEventReceiver,
    //所以是走到WindowInputEventReceiver::onInputEvent(),把touch数据继续给进去
    onInputEvent(event);
//......
}

//接着看
//frameworks/base/core/java/android/view/ViewRootImpl.java
public void onInputEvent(InputEvent event) {
//.......
    //这边对touch数据做了一个入队的操作
     enqueueInputEvent(event, this, 0, true);
//......
}

void enqueueInputEvent(InputEvent event, InputEventReceiver receiver, int flags, boolean processImmediately) {
      //根据touch数据创建了一个QueuedInputEvent,
      //这些数据格式转来转去的目前咱们可以先不用管,k
      //可以先关注数据的走向,其实最根本的数据我觉得也就书x,y +校准参数
     QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);
     //这边有一个input数据的队列,用mPendingInputEventHead、mPendingInputEventTail
     //来描述队列头和队列尾部;
     //若队列为空构造第一个元素,
     //否则放入对尾部
     //note17
     if (last == null) {
        mPendingInputEventHead = q;
        mPendingInputEventTail = q;
     } else {
        last.mNext = q;
        mPendingInputEventTail = q;
     }
     
//......
}

Okay, after the touch data is read from the inputChannel, it is put into a queue constructed with mPendingInputEventHead and mPendingInputEventTail. Next, let's go back to note18 to see the data processing flow.

//frameworks/base/core/java/android/view/ViewRootImpl.java
void doProcessInputEvents() {
    //把队列里边的数据取干净
    while (mPendingInputEventHead != null) {
        //从队列头开始取
        QueuedInputEvent q = mPendingInputEventHead;
//......
        //这把去发送数据
        deliverInputEvent(q);
    }

//.......
}

private void deliverInputEvent(QueuedInputEvent q) {
    //这边去出一个stage,总的stage有如前面setView时创建的在note19那些,
    //然后把touch数据给到每个stage去看是否属于自己管理范围内的数据,
    //如果是的话就处理,否则给到下一个InputStage,
    InputStage stage;
    if (q.shouldSendToSynthesizer()) {
        stage = mSyntheticInputStage;
    } else {
        stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;
    }        
//.......
    //这边在咱们的点击button例子中,处理事件的是ViewPostImeInputStage,
    //所以咱们直接看下ViewPostImeInputStage的数据处理函数吧,
    //中间逐个InputStage处理数据流程咱们就不看了,我自己也还没理清楚
    //感兴趣的同学可以自己看下
    stage.deliver(q);
}

public final void deliver(QueuedInputEvent q) {
    //如果不是自己处理范围的数据就不处理
    //给下一个InputStage
    if ((q.mFlags & QueuedInputEvent.FLAG_FINISHED) != 0) {
        forward(q);
    } else if (shouldDropInputEvent(q)) {
        finish(q, false);
    } else {
        traceEvent(q, Trace.TRACE_TAG_VIEW);
        final int result;
        try {
        //否则自己处理
            result = onProcess(q);
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
        apply(q, result);
    }
}

//看ViewPostImeInputStage的onProcess接口
protected int onProcess(QueuedInputEvent q) {
    if (q.mEvent instanceof KeyEvent) {
        return processKeyEvent(q);
    } else {
        final int source = q.mEvent.getSource();
        if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
        // touch 按压事件 ,其他事件类型咱们先不看了
            return processPointerEvent(q);
        } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
            return processTrackballEvent(q);
        } else {
            return processGenericMotionEvent(q);
        }
 }
 
 private int processPointerEvent(QueuedInputEvent q) {
     //事件做了转换
     final MotionEvent event = (MotionEvent)q.mEvent;
 //......
     //给到view里边了
     boolean handled = mView.dispatchPointerEvent(event);
 //......
 }
 
//接着看
//frameworks/base/core/java/android/view/View.java
public final boolean dispatchPointerEvent(MotionEvent event) { 
   if (event.isTouchEvent()) {
        //如果是touch事件就走这
        return dispatchTouchEvent(event);
    } else {
        return dispatchGenericMotionEvent(event);
    }
}

public boolean dispatchTouchEvent(MotionEvent event) {
//......
     onTouchEvent(event)
//......
}

public boolean onTouchEvent(MotionEvent event) {
//......
    //底下创建一个任务然后post给主线程去执行
    
   if (mPerformClick == null) {
       mPerformClick = new PerformClick();
   }
   if (!post(mPerformClick)) {
       performClickInternal();
   }
//...... 
} 

//接着看
private final class PerformClick implements Runnable {
    @Override
    public void run() {
        recordGestureClassification(TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__SINGLE_TAP);
        performClickInternal();
    }
}

private boolean performClickInternal() {
    // Must notify autofill manager before performing the click actions to avoid scenarios wher
    // the app has a click listener that changes the state of views the autofill service might
    // be interested on.
    notifyAutofillManagerOnClick();
    return performClick();
}

 public boolean performClick() {
 //......
     //这边去调用button 的onClick接口
     li.mOnClickListener.onClick(this);
 //......
 }

Okay, now that we have finished analyzing the data to the app, the remaining question is how to decide which activity, window, and view to send to InputFlinger. We will analyze it later.

Guess you like

Origin blog.csdn.net/goodnight1994/article/details/119328739