Analysis of android touch event delivery process

Due to the needs of the project, you need to understand the data transfer process of android touch. After reading the code, record the process for later reference.

This article is based on the open source code of Android 11. All codes can be viewed and downloaded at the address officially provided by aosp. The specific process of Android 11 may be a little inconsistent with other Android versions,

If there is anything wrong in the article, you are welcome to point out that you can discuss and communicate together

Analyze the touch data transmission mechanism of android from four lines (four directions). Along the way, we mainly focus on the route from the kernel to the app. We open up this road without paying attention to the details, and how the touch data determines which activity (window, view) to give,

  • ViewRootImpl register InputChannel
  • InputFlinger reads touch data from the kernel
  • InputFlinger sends touch data to View
  • ViewRootImpl receives the touch data and sends it to the app (such as onclick() of the callback button)

Take the button as an example. The overall process is that when the app is up, it will add a window to wms at the ViewRootImpl side, and create an InputChannel to bring it along. wms will pass the InputChannel to inputflinger as an intermediary. After clicking the button, the inputlinker will read the data from the kernel and send the data to ViewRootImpl through the InputChannel, Then, send it to the View through ViewPostImeInputStage, and create a PerformClick Post () to execute, callback the onClick() interface of the button. (how to know which activity, which window and which View to give is sorted out in the next analysis process)

ViewRootImpl register InputChannel

After the app activity is up, when the PhoneWindow is created and the window is added to the wms, the InputChannel is created and transmitted together. The wms transmits the InputChannel to the inputflinger. The inputflinger creates a Connection to save it, and then uses it to return the touch data to the app. Look at the code below:

//frameworks/base/core/java/android/view/ViewRootImpl.java
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
           int userId) {
       //......
      
      //Create InputChannel 
      inputChannel = new InputChannel();
      
      //When adding a window, it is sent to wms through binder ipc
      //note1
      res = mWindowSession.addToDisplayAsUser(mWindow, mSeq, mWindowAttributes,
      getHostVisibility(), mDisplay.getDisplayId(), userId, mTmpFrame,
      mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
      mAttachInfo.mDisplayCutout, inputChannel,
      mTempInsets, mTempControls);
      
      //At the same time, a WindowInputEventReceiver is created to receive touch data (in fact, it is not only touch,
      //It should include buttons and keyboards. We only focus on touch at present)
      //note2
      mInputEventReceiver = new WindowInputEventReceiver(inputChannel,
        Looper.myLooper());

      //......
      //Create an InputStage for processing input data,
      //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);
//Note that the inputChannel here is passed in as an out parameter. Later, after the socket pair is created in WindowState
//It will be re assigned a value so that as the client receiving data, it has fd that can receive touch data.
//Of course, there are cross processes here, and then there is a binder in the middle, which simplifies the process.

Follow mwindowsession addToDisplayAsUser(),

The mWindowSession instance here is a Session object, which is obtained from wms at WindowManagerGlobal, and then transmitted when creating ViewRootImpl. You can have a simple look at ~

//Assigned here
//frameworks/base/core/java/android/view/ViewRootImpl.java
public ViewRootImpl(Context context, Display display, IWindowSession session,
        boolean useSfChoreographer) {
//.......
    mWindowSession = session;
//.......       
}

//It's coming from here
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) {
 //......
 
     //Create ViewRootImpl
     root = new ViewRootImpl(view.getContext(), display);
 
 //.......       
 }
 
//This is obtained from wms through binder ipc, and then look at the wms implementation
//frameworks/base/core/java/android/view/WindowManagerGlobal.java
public static IWindowSession getWindowSession() {
    //......
    //Take it from wms
    sWindowSession = windowManager.openSession(......);
    return sWindowSession;
}

//wms returns a Session object directly
//frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
public IWindowSession openSession(IWindowSessionCallback callback) {
    return new Session(this, callback);
}


OK, confirm that mWindowSession is Session, and then return to mWindowSession in note1 Addtodisplayasuser(), continue to see the registration of inputchannel, which actually 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) {
//......        
    //To WindowState
    win.openInputChannel(outInputChannel);
//......
}

//Then look at WindowState
//frameworks/base/services/core/java/com/android/server/wm/WindowState.java
void openInputChannel(InputChannel outInputChannel) {
//......
    //Create a pair of inputchanles 
    InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
    //As server and client
    mInputChannel = inputChannels[0];
    mClientChannel = inputChannels[1];
    //InputFlinger on the server side
    //There are wms, InputManagerService and InputFlinger processes here. After direct call,
    //InputManagerService is also called directly to InputFlinger
    mWmService.mInputManager.registerInputChannel(mInputChannel);    
    
    //......
    //The client assignment gives the parameter, and then returns to ViewRootImpl as the client receiving touch data
    mClientChannel.transferTo(outInputChannel);    
//......
}

In fact, two C + + inputchannels that can communicate are created here, which are respectively used as the communication client and server. They contain two fd created by socket pair, which are assigned as the client's InputChannel to the java InputChannel transmitted by ViewRootClient, As a server, InputChannel is called to InputFlinger through in-process function (non binder) 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);
}

//To the 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);
        //Check duplicate
        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;
        }
        
        //Here, create a connection according to the channel, and then use it to send data to the app
        //note13
        sp<Connection> connection = new Connection(inputChannel, false /*monitor*/, mIdGenerator);

        int fd = inputChannel->getFd();
        //The registration here actually saves fd and connection as a key value pair,
        //When sending data later, take out the connection and send data
        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;
}

Well, all the way down, the so-called registration is to save the connection and the corresponding socket fd in the InputDispatcher(note3) of InputFlinger and put them in the hash map.

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

InputFlinger reads touch data from the kernel

Let's not talk about how inputflinger starts the process of reading data from / dev/input/eventX using epoll. Interested students can see for themselves (frameworks / native / services / inputlinker). Let's start with reading data from the kernel. Please note that the inputlinker here refers to the inputlinker binder service. At present, it runs on the system server in Android 11, It shares the same process with InputManagerService (but the communication between them still uses binder). InputManagerService creates the InputManager object with jni and registers it as InputFlinger service. Therefore, the actual implementation of inputflinger here (Android11) is actually c++ InputManager class, which may be different from other Android versions.

The data is read by a thread on the InputReader side. Alas, OCD makes me feel a little awkward from where the process source comes from. Otherwise, I'd better talk about how the data reading thread gets up from the source

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

//Inside the constructor, call the native init interface with jni
//frameworks/base/services/core/java/com/android/server/input/InputManagerService.java
public InputManagerService(Context context) {
//.......
    mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());
//.......
}
//Create NativeInputManager object
//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());   
 //......                       
 }
 
 //Create an InputMnager object and register it as an inputlinker 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);
 //......     
 }
 
 
//After creating InputManagerService,
//system server then calls the start interface of InputManagerService    
//frameworks/base/services/java/com/android/server/SystemServer.java
private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
//.......
    inputManager = new InputManagerService(context); 
    
//......
    inputManager.start();
//.......
}

//Went to native again
//frameworks/base/services/core/java/com/android/server/input/InputManagerService.java
public void start() {
//......
    nativeStart(mPtr);
//......
}
//Call the start interface of inputmanager
//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() {
//......
    //Here, let's ignore the thread reading data from InputReader,
    //I'll look back at note12 later
    mDispatcher->start();
    
    //Let's start with the thread reading data from the kernel, and then look at it
    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() {
//......
    //Here, read the touch data from the kernel (via / dev/input/eventX)
    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
//......
}

Well, the process of how to read data has been opened. Next, let's see how to send data to the app~

InputFlinger sends touch data to View

After reading the data, send it to ViewRootImpl through InputChannel. Let's see how the process is

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

    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
    
    //Processing data
    processEventsLocked(mEventBuffer, count);
//......
}

//TODO has to draw a picture and fill it up later

The overall process is as follows: during the initialization of InputDispatch, an InputDispatcher thread will be created to constantly 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, After a series of processing (we won't see what has been processed, just get through the process), put the touch event into the InputDispatcher::mInboundQueue queue, wake up the InputDispatcher thread to read the event from the queue, and then select the target View (let's focus on this), and then use the InputChannel registered in the previous View to send the event to ViewRootImpl through unix socket.

Next, from the code, 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);
    
    //Processing data
    processEventsLocked(mEventBuffer, count);
    
//.......
    
    //note10
    mQueuedListener->flush();
//......
}
//In fact, the above two things are done. The former is to put the data into QueuedInputListener::mArgsQueue after processing
//The latter is to continue processing the data after leaving the team. Let's take a look at the former first

void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {
//......
   //Continue to feed the data
   processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
//......
}

void InputReader::processEventsForDeviceLocked(int32_t eventHubId, const RawEvent* rawEvents,
                                               size_t count) {
 //......
    //It's over to InputDevice 
    device->process(rawEvents, count);                                            
}

//frameworks/native/services/inputflinger/reader/InputDevice.cpp
void InputDevice::process(const RawEvent* rawEvents, size_t count) {
//......
  //Call the process of each mapper. Here, I call MultiTouchInputMapper by calling callstack,
  //There are several TouchInputMapper, but we haven't sorted out which one to go
  for_each_mapper_in_subdevice(rawEvent->deviceId, [rawEvent](InputMapper& mapper) {
             mapper.process(rawEvent);
   });
//......
}

//Keep looking
//frameworks/native/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
void MultiTouchInputMapper::process(const RawEvent* rawEvent) {
    //Calling the parent class
    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) {
//.......
    //As the name suggests, it seems to continue to process raw data
    processRawTouches(false /*timeout*/);
//.......
}

void TouchInputMapper::processRawTouches(bool timeout) {
//......
   //I don't know why the function name should bring a cook, but there's nothing wrong with it. I saw it from callstack
   cookAndDispatch(mCurrentRawState.when);
//......
}

void TouchInputMapper::cookAndDispatch(nsecs_t when) {
//......
    //Come here
    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) {
                                      
 //......                                     
  //This way takes out listenner and calls its notifyMotion.  
  //note5                       
  getListener()->notifyMotion(&args);                                    
//......                                      
}

Among them, listener refers to inputreader:: mqueedlistener. If it is, interested students can see the following code segment, and not interested students can skip to the next code segment,

//This code explains why the upper getlistener gets inputreader:: mqueedlistener
//Note that note5 is inside the MuitiTouchInputMapper object,


//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 constructs its mContext and saves its address in the 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
}

//The mContext here is of InputReader::ContextImpl type. See the constructor
InputReader::ContextImpl::ContextImpl(InputReader* reader)
      : mReader(reader), mIdGenerator(IdGenerator::Source::INPUT_READER) {}
//So InputReader::mContex is of InputReader::ContextImpl type,
//InputReader note6 is stored in its mReader    


std::shared_ptr<InputDevice> InputReader::createDeviceLocked(
        int32_t eventHubId, const InputDeviceIdentifier& identifier) {
//......
  //Create an InputDevice here and transfer the mContext, that is, 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 exists in note7 in the mContext of InputDevice
        mId(id),
        mGeneration(generation),
        mControllerNumber(0),
        mIdentifier(identifier),
        mClasses(0),
        mSources(0),
        mIsExternal(false),
        mHasMic(false),
        mDropUntilNextSync(false) {}
        
//The InputDevice addEventHubDevice creates the MultiTouchInputMapper in note5 above
void InputDevice::addEventHubDevice(int32_t eventHubId, bool populateMappers) {
//.......
    //InputDevice gives its address to InputDeviceContext,
    //As the value of its mDevice member, take InputReader as the value of its mContext
    std::unique_ptr<InputDeviceContext> contextPtr(new InputDeviceContext(*this, eventHubId));
//......
    //The address of contextPtr is also reserved in the mDeviceContext of InputMapper
    mappers.push_back(std::make_unique<MultiTouchInputMapper>(*contextPtr));
//.......
}

InputDeviceContext::InputDeviceContext(InputDevice& device, int32_t eventHubId)
      : mDevice(device),
      //According to note7, InputReader::ContextImpl note8 is stored here
        mContext(device.getContext()),
        mEventHub(device.getContext()->getEventHub()),
        mId(eventHubId),
        mDeviceId(device.getId()) {}


//The address of contextPtr is also reserved in the mDeviceContext of InputMapper,
//That is, mDeviceContext points to the InputDeviceContext object note9
//MultiTouchInputMapper, TouchInputMapper and InputMapper are the inheritance relationship of grandson, father and grandfather
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) {}
      
//Well, we can go back to getListener of note5
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(); }

//According to note9, go to the InputDeviceContext and look
//framworks/native/services/inputflinger/reader/include/InputDevice.h
inline InputReaderContext* getContext() { return mContext; }
//According to note8, the mContext here is the InputReader::ContextImpl passed in when the InputDevice was constructed earlier

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

OK, make sure that what you get here is inputreader:: mqueedlistener. Ha ha, you can skip the above paragraph and continue to look at the following paragraph

//Let's go back to 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 the queue called mArgsQueue (implemented by vector). Since you have joined the queue, you must have left the queue. However, how can you read the former in InputReader::loopOnce() at the beginning of this section, and then look at the latter to see what the data has done after leaving the queue, 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++) {
    //Take out each data and call its notify interface,
        NotifyArgs* args = mArgsQueue[i];
        args->notify(mInnerListener);
        delete args;
    }
    mArgsQueue.clear();
}
//note11
//The mInnerListener here is InputClassifier,
// The input dispatcher is installed in the mListener in the InputClassifier
//This part is simpler. Interested students can follow it from the following place
//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);
}

//All right, let's get down to business and see how the data is transmitted,
//frameworks/native/services/inputflinger/InputListener.cpp
void NotifyMotionArgs::notify(const sp<InputListenerInterface>& listener) const {
    listener->notifyMotion(this);
}

//Went to 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);
}

//The InputClassifier::mListener here is the InputDispatcher. Let's continue

//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);
        //Data queue
        needWake = enqueueInboundEventLocked(newEntry);    
        
        if (needWake) {
            //Wake up the thread reading data and dequeue the data
            mLooper->wake();
        }
//......
}

//Keep looking
bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) {
//......
      //The data is in the team again here
      mInboundQueue.push_back(entry);   
//......
}

OK, now we see the data entering the InputDispatcher::mInboundQueue queue again. Let's go and see where to get it out of the queue

//From note12, let's go on,
//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;
}

//A thread is created to run the dispatchOnce() function,
//Go to sleep when there is no event. When there is an event, wake up by the previous team joining event and start working
void InputDispatcher::dispatchOnce() {
//.......
     dispatchOnceInnerLocked(&nextWakeupTime);
//.......
}

//Keep looking
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 will choose which View to send the touch data to, and then load it into inputTargets
   //How to choose the specific one will be studied separately later
   injectionResult =
         findTouchedWindowTargetsLocked(currentTime, *entry, inputTargets, nextWakeupTime,
                                               &conflictingPointerActions); 
//......   
   //After selecting View, continue to send data
   dispatchEventLocked(currentTime, entry, inputTargets);                                        
//......                                           
}

void InputDispatcher::dispatchEventLocked(nsecs_t currentTime, EventEntry* eventEntry,
                                          const std::vector<InputTarget>& inputTargets) {
//.......
    //Take out the connection registered in note13 according to the inputTarget, which contains the InputChannel used to communicate with the View
    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) {
 //.......                                                
     //Go in here and look
     enqueueDispatchEntriesLocked(currentTime, connection, splitMotionEntry, inputTarget);                                                
//......                                                 
}

void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime,
                                                   const sp<Connection>& connection,
                                                   EventEntry* eventEntry,
                                                   const InputTarget& inputTarget) {
 //.......                                                  
     //Get in here                                          
     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);
//....
}

//Here we go. Have a look
//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) {
//.......                                         
     //OK, to the end, send the data to View through InputChannel using unix socket
     return mChannel->sendMessage(&msg);
}                                      

//As for how this mChannel came from, why is it the InputChannel transmitted from the previous app through wms,
//Let's take another look at 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) {
}

//So it's clear ~

Well, InputFlinger reads data from the kernel and sends it to the View. Let's see this first (there is a TODO left, which is sent to the View and then viewed separately). The main work of this part of the process is focused on InputReader, inputdisapper and InputDevice.

Next, let's take a look at how ViewRootImpl uses InputChannel to receive touch data and give specific View ~

ViewRootImpl receives the touch data and sends it to the app

Let's take a specific example to see how onClick() of a button is called after clicking on it.

Let's start with a callstack:

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)

Let's start from the place where InputChannel receives data, and continue with note2. When setView creates InputChannel, assign the fd value of unix socketpair to it when wms, and it will be given as the parameter to create 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());
//......
}
//When ViewRootImpl is constructed (before setView), a thread will be created to receive and process data using mInputEventReceiver
//This operation is a little small. In fact, the mInputEventReceiver is empty at this time, so it makes a null judgment operation

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) {
        //Read the data
        //note16
        consumedBatches = mInputEventReceiver.consumeBatchedInputEvents(frameTimeNanos);
    } else {
        consumedBatches = false;
    }
    //This way to process the data
    //note18
    doProcessInputEvents();
    return consumedBatches;
}

Where, the above minputeventreceiver Consumebatchedinputevents() will use inputChannel to read data in the native layer, and then call the dispatchInputEvent interface of the java layer from the native layer to send the data again,

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

Let's take a look at the consumeBatchedInputEvents() process first:

 //Start with note14
 
//framworks/base/core/java/android/view/ViewRootImpl.java
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView, int userId) {
//......    
    //inputChannel as input parameter
    mInputEventReceiver = new WindowInputEventReceiver(inputChannel, Looper.myLooper());
//......
}

final class WindowInputEventReceiver extends InputEventReceiver
public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
      //Then give it to the parent class InputEventReceiver
     super(inputChannel, looper);
}

//frameworks/base/core/java/android/view/InputEventReceiver.java
public InputEventReceiver(InputChannel inputChannel, Looper looper) {
//......
    //Keep a quote for yourself
    mInputChannel = inputChannel;
    //Then to the native layer
    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);
//......
   //Create a NativeInputEventReceiver object and bring in inputChannel as an input parameter         
    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)),
        //Save a pointer reference to inputChannel
        //note15
        mInputConsumer(inputChannel), mMessageQueue(messageQueue),
        mBatchedInputEventPending(false), mFdEvents(0) {
//......
}

OK, the inputChannel of ViewRootImpl keeps a copy in the inputconsumer of NativeInputEventReceiver,

ok, then watch 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 {
        //Go to the native layer
        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) {
     //According to note15, call InputChannel here to read out the touch data from InputFlinger
    status_t status = receiver->consumeEvents(env, true /*consumeBatches*/, frameTimeNanos,
            &consumedBatch);

    //......
}

status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
        bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) {
 //......       
         //Use channel to read the data
        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 receives data          
    status_t result = mChannel->receiveMessage(&mMsg);  
 //......
     //Make a conversion according to the read data
     inputEventObj = android_view_MotionEvent_obtainAsCopy(env, motionEvent);  
 //......  
     //Call the java interface dispatchInputEvent and pass the inputEventObj
    env->CallVoidMethod(receiverObj.get(),
        gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);    
 //......            
 }
 
 

Go back to the java layer and continue

//frameworks/base/core/java/android/view/InputEventReceiver.java
private void dispatchInputEvent(int seq, InputEvent event) {
//......
    //Note that the object is WindowInputEventReceiver,
    //So go to WindowInputEventReceiver::onInputEvent() and continue to feed the touch data
    onInputEvent(event);
//......
}

//Keep looking
//frameworks/base/core/java/android/view/ViewRootImpl.java
public void onInputEvent(InputEvent event) {
//.......
    //Here is a queue joining operation for touch data
     enqueueInputEvent(event, this, 0, true);
//......
}

void enqueueInputEvent(InputEvent event, InputEventReceiver receiver, int flags, boolean processImmediately) {
      //A QueuedInputEvent is created based on the touch data,
      //These data formats go around. At present, we can ignore them, k
      //You can first pay attention to the trend of the data. In fact, the most fundamental data I think is the book x,y + calibration parameters
     QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);
     //Here is a queue of input data, using mpendiginputeventhead and mpendiginputeventtail
     //To describe the queue header and queue tail;
     //If the queue is empty, construct the first element,
     //Otherwise, put it in the right tail
     //note17
     if (last == null) {
        mPendingInputEventHead = q;
        mPendingInputEventTail = q;
     } else {
        last.mNext = q;
        mPendingInputEventTail = q;
     }
     
//......
}

Well, after the touch data is read from the inputChannel, put it 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() {
    //Clean up the data in the queue
    while (mPendingInputEventHead != null) {
        //Fetch from queue header
        QueuedInputEvent q = mPendingInputEventHead;
//......
        //This is to send data
        deliverInputEvent(q);
    }

//.......
}

private void deliverInputEvent(QueuedInputEvent q) {
    //A stage is shown here. The general stage is like those created in note19 during setView,
    //Then give the touch data to each stage to see if it belongs to the data within its own management scope,
    //If so, handle it, otherwise it will be given to the next InputStage,
    InputStage stage;
    if (q.shouldSendToSynthesizer()) {
        stage = mSyntheticInputStage;
    } else {
        stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;
    }        
//.......
    //Here, in our click button example, the event is handled by ViewPostImeInputStage,
    //So let's look directly at the data processing function of ViewPostImeInputStage,
    //We don't see the data processing flow of InputStage one by one. I haven't figured it out myself
    //Interested students can see it for themselves
    stage.deliver(q);
}

public final void deliver(QueuedInputEvent q) {
    //If the data is not within the scope of their own processing, they will not be processed
    //To the next 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 {
        //Otherwise, deal with it yourself
            result = onProcess(q);
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
        apply(q, result);
    }
}

//Look at the onProcess interface of ViewPostImeInputStage
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 press event. We won't watch other event types first
            return processPointerEvent(q);
        } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
            return processTrackballEvent(q);
        } else {
            return processGenericMotionEvent(q);
        }
 }
 
 private int processPointerEvent(QueuedInputEvent q) {
     //Event has been converted
     final MotionEvent event = (MotionEvent)q.mEvent;
 //......
     //Here it is
     boolean handled = mView.dispatchPointerEvent(event);
 //......
 }
 
//Keep looking
//frameworks/base/core/java/android/view/View.java
public final boolean dispatchPointerEvent(MotionEvent event) { 
   if (event.isTouchEvent()) {
        //If it's a touch event, go here
        return dispatchTouchEvent(event);
    } else {
        return dispatchGenericMotionEvent(event);
    }
}

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

public boolean onTouchEvent(MotionEvent event) {
//......
    //Create a task below and post it to execute
    //(I'm not sure who it is for. Is it the system server wired process pool?)
   if (mPerformClick == null) {
       mPerformClick = new PerformClick();
   }
   if (!post(mPerformClick)) {
       performClickInternal();
   }
//...... 
} 

//Keep looking
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() {
 //......
     //This way, call the onClick interface of button
     li.mOnClickListener.onClick(this);
 //......
 }

Well, after analyzing the data to the app, there is still a problem: how to decide which activity, window and view to give to the InputFlinger, and then analyze it later.

//TODO has to fill in another flow chart

Keywords: Android

Added by dsds1121 on Mon, 03 Jan 2022 03:42:14 +0200