InputManagerService source code analysis 3: event distribution

Keywords: Android

preface

android source code is obscure and difficult to read. I think there are three main points:

  1. The code contains many small internal logic, so it is difficult to grasp the key points in the reading process. This problem can be solved by streamlining the process and log analysis.
  2. Involving unknown areas of knowledge. As an application development, these knowledge points include JNI, Linux operating system API, OpenGL, Skia, etc. Some can be solved quickly by searching articles; Some involve professional fields, and non professional fields are not reachable.
  3. Is related to other complex source code reading. Take InputManagerService event distribution to obtain the current focus View as an example. This process involves reading the source code of WindowManagerService and Looper. If it is difficult to read the source code of WindowManagerService, the event distribution of InputManagerService cannot be understood thoroughly. This problem is also encountered in the event distribution of InputManagerService in this chapter, so the focus change logic of Window in this chapter is only briefly analyzed here, and will be analyzed in detail in the subsequent chapters of WindowManagerService.

My humble opinion: android mobile phone is a pile of parts and software, and android system Service can also be understood in this way. The process of reading source code is a process of simplifying. The following chapters will briefly understand this sentence through a simple C program.

1, Event registration

Event distribution in this chapter mainly introduces that InputManagerSevice distributes data to applications. For information about how InputManagerService receives data, please refer to InputManagerService initialization.
We know that InputManagerService runs in system_ In process, the client application runs in an independent process, so event distribution is cross process communication.
This section is divided into five parts to introduce the application to establish a bilateral communication link with InputManagerService. To facilitate understanding, attach a screenshot from the Internet to illustrate the relationship between Client and Server. In order to facilitate understanding, the following Cllient end refers to the Client process and the Server end refers to the system system_process.

1. Session creation

The establishment of bilateral communication link between Client process and service process is based on AIDL. The Client obtains the server Session agent by calling OpenSession() of WindowManagerService. The code is as follows:

//frameworks\base\core\java\android\view\ViewRootImpl.java
 public ViewRootImpl(Context context, Display display) {
        mContext = context;
        mWindowSession = WindowManagerGlobal.getWindowSession();
        ...
 }
 
//frameworks\base\core\java\android\view\WindowManagerGlobal.java
//An application has only one WindowSession
public static IWindowSession getWindowSession() {
        synchronized (WindowManagerGlobal.class) {
            if (sWindowSession == null) {
                try {
                    IWindowManager windowManager = getWindowManagerService();
                    //Call the Service through the WindowManagerService proxy
                    sWindowSession = windowManager.openSession(
                            new IWindowSessionCallback.Stub() {
                                @Override
                                public void onAnimatorScaleChanged(float scale) {
                                    ValueAnimator.setDurationScale(scale);
                                }
                            });
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            }
            return sWindowSession;
        }
    }

//frameworks\base\services\core\java\com\android\server\wm\WindowManagerService.java
@Override
public IWindowSession openSession(IWindowSessionCallback callback) {
    return new Session(this, callback);
}
    
frameworks\base\services\core\java\com\android\server\wm\Session.java
//Session inherits IWindowSession.Stub and returns IWindowSession of the customer as Binder agent
class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
}

Get the sequence diagram of remote Session agent, as shown below.

2. Create windowstate

After obtaining the Session agent, communicate with the remote by calling its addToDisplay() method. The analysis ends with the java layer WindowState calling openInputChannel(), and the subsequent native layer code analysis will be analyzed in the next section. This section mainly makes a brief analysis through four steps
Step 1: create InputChannel on client side
Create an InputChannel inside the setView() method, which is a serializable object. The InputChannel created through new is an empty method and has no corresponding native layer instance. Subsequently, it will be analyzed and transmitted to the remote service, and the valid value will be returned.

//frameworks\base\core\java\android\view\ViewRootImpl.java
 public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
   ...
   if ((mWindowAttributes.inputFeatures
                        & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                    mInputChannel = new InputChannel();
   }
 }

Step 2: call addToDisplay() to call WMS remote service
The addWindow() method in WindowManagerService is finally called through the AIDL interface.

//frameworks\base\core\java\android\view\ViewRootImpl.java
 public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
   ...
   res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame,
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel,
                            mTempInsets);
 }
 
//frameworks\base\services\core\java\com\android\server\wm\Session.java
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
            int viewVisibility, int displayId, Rect outFrame, Rect outContentInsets,
            Rect outStableInsets, Rect outOutsets,
            DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
            InsetsState outInsetsState) {
        return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outFrame,
                outContentInsets, outStableInsets, outOutsets, outDisplayCutout, outInputChannel,
                outInsetsState);
    }
 

Step 3: create WindowState
In the addWindow() method, we will create a WindowState (we omitted the attribute and token verification for analysis). The WindowState can be translated into a Window state, and the information such as the Window display area and the InputChannel corresponding to the Client side will be stored inside. The ViewRootImpl of the Client side has a corresponding WindowState of the Sever side.

//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, Rect outOutsets,
            DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
            InsetsState outInsetsState) {
            ...
            final WindowState win = new WindowState(this, session, client, token, parentWindow,
                    appOp[0], seq, attrs, viewVisibility, session.mUid,
                    session.mCanAddInternalSystemWindow);
             ...
             final boolean openInputChannels = (outInputChannel != null
                    && (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
             if  (openInputChannels) {
                win.openInputChannel(outInputChannel);
             }
            }

Step 4: the code of calling openInputChannel() of WindowState is as follows:

//frameworks\base\services\core\java\com\android\server\wm\WindowState.java
void openInputChannel(InputChannel outInputChannel) {
        if (mInputChannel != null) {
            throw new IllegalStateException("Window already has an input channel.");
        }
        String name = getName();
        //Create a pair of inputchannels
        InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
        mInputChannel = inputChannels[0];
        mClientChannel = inputChannels[1];
        mInputWindowHandle.token = mClient.asBinder();
        if (outInputChannel != null) {
            mClientChannel.transferTo(outInputChannel);
            mClientChannel.dispose();
            mClientChannel = null;
        } else {
            mDeadWindowEventReceiver = new DeadWindowEventReceiver(mClientChannel);
        }
        //Server side listening
        mWmService.mInputManager.registerInputChannel(mInputChannel, mClient.asBinder());
    }

In the next section, we will introduce InputChannel.openInputChannelPair() to create InputChannel []. Later, we will introduce the use of this pair of inputchannels in the Server listening and Client listening sections respectively.
The following is the call sequence diagram of addToDisplay() java layer:

3. InputChannel creation

The core method of InputChannel.openInputChannelPair() is the nativeopinputchannelpair of the native layer. The code is as follows:

//frameworks\base\core\jni\android_view_InputChannel.cpp
static jobjectArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* env,
        jclass clazz, jstring nameObj) {
    ScopedUtfChars nameChars(env, nameObj);
    std::string name = nameChars.c_str();

    sp<InputChannel> serverChannel;
    sp<InputChannel> clientChannel;
    //Step 1 establish a pair of interconnected inputchannels
    status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);

    if (result) {
        String8 message;
        message.appendFormat("Could not open input channel pair.  status=%d", result);
        jniThrowRuntimeException(env, message.string());
        return NULL;
    }
    //Step 2 create a java layer InputChannel
    jobjectArray channelPair = env->NewObjectArray(2, gInputChannelClassInfo.clazz, NULL);
    if (env->ExceptionCheck()) {
        return NULL;
    }
    //Step 3 create C-layer NativeInputChannel
    jobject serverChannelObj = android_view_InputChannel_createInputChannel(env,
            std::make_unique<NativeInputChannel>(serverChannel));
    if (env->ExceptionCheck()) {
        return NULL;
    }

    jobject clientChannelObj = android_view_InputChannel_createInputChannel(env,
            std::make_unique<NativeInputChannel>(clientChannel));
    if (env->ExceptionCheck()) {
        return NULL;
    }
    //Step 3 create C-layer NativeInputChannel
    env->SetObjectArrayElement(channelPair, 0, serverChannelObj);
    env->SetObjectArrayElement(channelPair, 1, clientChannelObj);
    return channelPair;
}

You can analyze this code in the next three steps.
Step 1: establish a pair of interconnected sockets and encapsulate them into InputChannel instances of the native layer.

//frameworks\native\libs\input\InputTransport.cpp
status_t InputChannel::openInputChannelPair(const std::string& name,
        sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) {
    int sockets[2];
    //Create a pair of anonymous, interconnected sockets
    if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) {
        status_t result = -errno;
        ALOGE("channel '%s' ~ Could not create socket pair.  errno=%d",
                name.c_str(), errno);
        outServerChannel.clear();
        outClientChannel.clear();
        return result;
    }

    int bufferSize = SOCKET_BUFFER_SIZE;
    //Set up bilateral communication
    setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
    setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));
    setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
    setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));
    //Create an InputChannel instance of the Native layer
    std::string serverChannelName = name;
    serverChannelName += " (server)";
    outServerChannel = new InputChannel(serverChannelName, sockets[0]);

    std::string clientChannelName = name;
    clientChannelName += " (client)";
    outClientChannel = new InputChannel(clientChannelName, sockets[1]);
    return OK;
}

Step 2: create InputChannel array of java layer
Create an empty array with an array type of InputChannel, that is, an empty array, which will be assigned when creating NativeInputChannel later.

//frameworks\base\core\jni\android_view_InputChannel.cpp
status_t InputChannel::openInputChannelPair(const std::string& name,
        sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) {
        ...
        jobjectArray channelPair = env->NewObjectArray(2, gInputChannelClassInfo.clazz, NULL);
        ...
        }

Step 3: create C-layer NativeInputChannel

//frameworks\base\core\jni\android_view_InputChannel.cpp
status_t InputChannel::openInputChannelPair(const std::string& name,
        sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) {
        ...
		jobject clientChannelObj = android_view_InputChannel_createInputChannel(env,
            std::make_unique<NativeInputChannel>(clientChannel));
        ...
        }

//frameworks\base\core\jni\android_view_InputChannel.cpp
static jobject android_view_InputChannel_createInputChannel(JNIEnv* env,
        std::unique_ptr<NativeInputChannel> nativeInputChannel) {
     //Call the initialization method to create the java layer InputChannel
    jobject inputChannelObj = env->NewObject(gInputChannelClassInfo.clazz,
            gInputChannelClassInfo.ctor);
    if (inputChannelObj) {
        android_view_InputChannel_setNativeInputChannel(env, inputChannelObj,
                 nativeInputChannel.release());
    }
    return inputChannelObj;
}

//frameworks\base\core\jni\android_view_InputChannel.cpp
static void android_view_InputChannel_setNativeInputChannel(JNIEnv* env, jobject inputChannelObj,
        NativeInputChannel* nativeInputChannel) {
    //Copy the NativeInputChannel object pointer to the mPtr in the InputChannel
    env->SetLongField(inputChannelObj, gInputChannelClassInfo.mPtr,
             reinterpret_cast<jlong>(nativeInputChannel));
}

The C + + layer creates a pair of InputChannel timing diagrams as follows:

4. Server side listening

The previous section introduced creating a pair of inputchannels on the Server side. This section mainly analyzes adding socket listening on the sever side. The analysis of the Server listening process ends with the addFd method in the Looper method. We will briefly introduce the functions of the addFd method. Now start with the first part:

step 1: get the InputChannel instance of the native layer corresponding to the InputChannel on the Server side.

//frameworks\base\services\core\jni\com_android_server_input_InputManagerService.cpp
static void nativeRegisterInputChannel(JNIEnv* env, jclass /* clazz */,
        jlong ptr, jobject inputChannelObj, jint displayId) {
    //step 1: get the NativeManager of the native layer
    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
    //step 1: get the InputChannel of the native layer corresponding to the java layer
    sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
            inputChannelObj);
   //step 2: register InputChannel
   status_t status = im->registerInputChannel(env, inputchannel, displayId)
}

//frameworks\base\core\jni\android_view_InputChannel.cpp
sp<InputChannel> android_view_InputChannel_getInputChannel(JNIEnv* env, jobject inputChannelObj) {
    NativeInputChannel* nativeInputChannel =
            android_view_InputChannel_getNativeInputChannel(env, inputChannelObj);
    return nativeInputChannel != NULL ? nativeInputChannel->getInputChannel() : NULL;
}

//frameworks\base\core\jni\android_view_InputChannel.cpp
//Get the native layer NativeInputChannel used by the java layer InputChannel pair
static NativeInputChannel* android_view_InputChannel_getNativeInputChannel(JNIEnv* env,
        jobject inputChannelObj) {
    jlong longPtr = env->GetLongField(inputChannelObj, gInputChannelClassInfo.mPtr);
    return reinterpret_cast<NativeInputChannel*>(longPtr);
}


step 2: register InputChannel. Registering InputChannel mainly involves two parts:

  1. Encapsulating Connection, responsible for event sending: the encapsulating class of InputChannel on the server side. Subsequently, the corresponding Connection instance will be obtained according to the activation window and events will be distributed to the client process.
  2. Add the Server-side InputChannel to the Looper to receive events. After subsequent Client-side events are processed, a finish signal will be sent, and the handleReceiveCallback() method will be called to clean up Server-side events.
    The following two chapters will cover Server-side event sending and Client-side event receiving. These two processes are very important.
//frameworks\base\services\core\jni\com_android_server_input_InputManagerService.cpp
status_t NativeInputManager::registerInputChannel(JNIEnv* /* env */,
        const sp<InputChannel>& inputChannel, int32_t displayId) {
    ATRACE_CALL();
    return mInputManager->getDispatcher()->registerInputChannel(
            inputChannel, displayId);
}

//frameworks\native\services\inputflinger\InputDispatcher.cpp
status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel,
        int32_t displayId) {
    { // acquire lock
        std::scoped_lock _l(mLock);

        if (getConnectionIndexLocked(inputChannel) >= 0) {
            ALOGW("Attempted to register already registered input channel '%s'",
                    inputChannel->getName().c_str());
            return BAD_VALUE;
        }
        // 1. Package Connection,
        sp<Connection> connection = new Connection(inputChannel, false /*monitor*/);

        int fd = inputChannel->getFd();
        mConnectionsByFd.add(fd, connection);
        mInputChannelsByToken[inputChannel->getToken()] = inputChannel;
        //2. Use Looper mechanism to listen for the input of fd corresponding Client
        mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
    } // release lock
 
    // Wake the looper because some connections have changed.
    mLooper->wake();
    return OK;
}

The Server side InputChannel registration flow chart is as follows:

5. Client event listening

Step 1: return the InputChannel copy generated on the Server side to the Client side.
The data is copied mainly through transerTo() and writeToParcel(), and the core code is in the native layer. Due to the length of space, it is not analyzed here.

//frameworks\base\services\core\java\com\android\server\wm\WindowState.java
void openInputChannel(InputChannel outInputChannel) {
        ...
        if (outInputChannel != null) {
            //Copy
            mClientChannel.transferTo(outInputChannel);
            mClientChannel.dispose();
            mClientChannel = null;
        } else {
            ...
        }
       ...
    }
//frameworks\base\core\java\android\view\InputChannel.java
//Copy writeToParcel, copy the InputChannel generated on the Server side, and serialize it back to the client
public void writeToParcel(Parcel out, int flags) {
    if (out == null) {
        throw new IllegalArgumentException("out must not be null");
    }
    //native layer copy
    nativeWriteToParcel(out);
    if ((flags & PARCELABLE_WRITE_RETURN_VALUE) != 0) {
        dispose();
    }
}

Step 2: the second section introduces the remote call of addToDisplay(). Now, after the AIDL is returned, the Client side processes the InputChannel returned by the Server side.

//frameworks\base\core\java\android\view\ViewRootImpl.java
 public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
    ...
    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame,
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel,
                            mTempInsets);
    ...
    if (mInputChannel != null) {
      if (mInputQueueCallback != null) {
          mInputQueue = new InputQueue();
          mInputQueueCallback.onInputQueueCreated(mInputQueue);
      }
      //Encapsulate the returned InputChannel as WindowInputReceiver
      mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
              Looper.myLooper());
     }
     ...
}
//frameworks\base\core\java\android\view\InputEventReceiver.java
 public InputEventReceiver(InputChannel inputChannel, Looper looper) {
        ...
        mInputChannel = inputChannel;
        mMessageQueue = looper.getQueue();
        mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),
                inputChannel, mMessageQueue);
        ...
    }
    
//frameworks\base\core\jni\android_view_InputEventReceiver.cpp
void NativeInputEventReceiver::setFdEvents(int events) {
    if (mFdEvents != events) {
        mFdEvents = events;
        int fd = mInputConsumer.getChannel()->getFd();
        if (events) {
            //Add client socket fd epoll listening.
            //The Looper on the Clien side has been reading the message queue loop, and the loop will pass through epoll_wait to get data
            mMessageQueue->getLooper()->addFd(fd, 0, events, this, NULL);
        } else {
            mMessageQueue->getLooper()->removeFd(fd);
        }
    }
}

The addFd method of Looper will not be analyzed here. The internal logic of addFd will be analyzed in detail when Looper is analyzed later. Here is a brief introduction to the internal logic of addFd: using epoll_ The CTL method monitors the socket fd. The Server sends events, and the Client sends events through epoll_wait obtains the event, and throws the event to the NativeInputEventReceiver of the native layer through the handleEvent method. Subsequent Client event reception will describe the process in detail.
The process of adding event listening on the Client side is as follows:

It can be seen that the event listening of Server and Client is realized through the addFd method in Looper.

2, Event distribution

InputManagerService source code analysis II This paper analyzes the events that InputReader handles adding and deleting devices. In this section, we analyze another type of InputReader: input device data.

2.1 equipment introduction

Before analysis, let's introduce my input device: bat 2 game console

2.1.1 handle information

Device 2: HJC Game BETOP BFM GAMEPAD
    Generation: 6
    IsExternal: true
    AssociatedDisplayPort: 0
    HasMic:     false
    Sources: 0x01000511
    KeyboardType: 1
    Motion Ranges:
    ...
    //Key
    Keyboard Input Mapper:
      Parameters:
        OrientationAware: false
        HandlesKeyRepeat: false
      KeyboardType: 1
      Orientation: 0
      KeyDowns: 0 keys currently down
      MetaState: 0x0
      DownTime: 4930839688000
      //remote sensing
      Joystick Input Mapper:
      ...

2.1.2 press log (getEvent)

From the device information, we can see that our game console supports key, and remote sensing operations. Here, we analyze event distribution with game console keys. The log of pressing the B key at hand is captured by getevent command as follows:

zhangmajian:~ zhangmajian$ adb shell getevent -l
add device 1: /dev/input/event0
  name:     "Semidrive Safe TouchScreen"
add device 2: /dev/input/event2
  name:     "HJC Game BETOP BFM GAMEPAD"
add device 3: /dev/input/event1
  name:     "Semidrive Safe TouchScreen"
//Press
/dev/input/event2: EV_MSC       MSC_SCAN             00090002         
/dev/input/event2: EV_KEY       BTN_EAST             DOWN                
/dev/input/event2: EV_SYN       SYN_REPORT           00000000  
//lift        
/dev/input/event2: EV_MSC       MSC_SCAN             00090002           
/dev/input/event2: EV_KEY       BTN_EAST             UP                    
/dev/input/event2: EV_SYN       SYN_REPORT           00000000   

2.1.3 press log analysis (InputManagerService)

The log corresponding to the android side (including self adding, only pressing, not lifting) is displayed as follows:

//Step 1: the inputreader thread handles events
08-25 17:24:55.900 2640-2871/? D/InputReader: BatchSize: 3 Count: 3
    Input event: device=2 type=0x0004 code=0x0004 value=0x00090002 when=6413385826000
    Input event: device=2 type=0x0001 code=0x0131 value=0x00000001 when=6413385826000
    Input event: device=2 type=0x0000 code=0x0000 value=0x00000000 when=6413385826000
//Step 2:InputDispatcher thread handles events
08-25 17:24:55.900 2640-2871/? D/InputDispatcher: notifyKey - eventTime=6413385826000, deviceId=2, source=0x501, displayId=0policyFlags=0x1, action=0x0, flags=0x8, keyCode=0x61, scanCode=0x131, metaState=0x0, downTime=6413385826000
08-25 17:24:55.901 2640-2870/? D/InputDispatcher: Resetting ANR timeouts.
    dispatchKeyLocked dropReason: 0
    dispatchKey - eventTime=6413385826000, deviceId=2, source=0x501, displayId=0, policyFlags=0x62000001, action=0x0, flags=0x8, keyCode=0x61, scanCode=0x131, metaState=0x0, repeatCount=0, downTime=6413385826000
08-25 17:24:55.915 2640-2870/? D/InputDispatcher: dispatchKeyLocked dropReason: 0
    dispatchKeyLocked
    // Gets the Window that can be distributed currently
    findFocusedWindow finished: injectionResult=0, timeSpentWaitingForApplication=0.0ms
    dispatchKeyLocked injectionResult: 0
    dispatchEventToCurrentInputTargets
    channel '1d2cd74 com.android.launcher3/com.android.launcher3.Launcher (server)' ~ prepareDispatchCycle - flags=0x00000101, xOffset=0.000000, yOffset=0.000000, globalScaleFactor=1.000000, windowScaleFactor=(1.000000, 1.000000), pointerIds=0x0
    channel '1d2cd74 com.android.launcher3/com.android.launcher3.Launcher (server)' ~ startDispatchCycle
    //Server side distribution: the purpose is that the client can receive
08-25 17:24:55.915 2640-2870/? D/InputTransport: channel '1d2cd74 com.android.launcher3/com.android.launcher3.Launcher (server)' publisher ~ publishKeyEvent: seq=2527, deviceId=2, source=0x501, action=0x0, flags=0x8, keyCode=97, scanCode=305, metaState=0x0, repeatCount=0,downTime=6413385826000, eventTime=6413385826000
    channel '1d2cd74 com.android.launcher3/com.android.launcher3.Launcher (server)' ~ sent message of type 1
    //Step 4:Server side distribution: the purpose is to globally listen for the information that the listener can receive
08-25 17:24:55.915 2640-2870/? D/InputDispatcher: channel 'PointerEventDispatcher0 (server)' ~ prepareDispatchCycle - flags=0x00000100, xOffset=0.000000, yOffset=0.000000, globalScaleFactor=1.000000, windowScaleFactor=(1.000000, 1.000000), pointerIds=0x0
    channel 'PointerEventDispatcher0 (server)' ~ startDispatchCycle
08-25 17:24:55.915 2640-2870/? D/InputTransport: channel 'PointerEventDispatcher0 (server)' publisher ~ publishKeyEvent: seq=2528, deviceId=2, source=0x501, action=0x0, flags=0x8, keyCode=97, scanCode=305, metaState=0x0, repeatCount=0,downTime=6413385826000, eventTime=6413385826000
... 
08-25 17:24:55.915 2640-2870/? D/InputTransport: channel 'PointerEventDispatcher0 (server)' ~ sent message of type 1

2.2 event analysis

Step 1: the inputreader thread receives the event, parses the event, encapsulates and transmits it to the InputDispatcher
1.1: identify input device input events in processEventsLocked method:

//frameworks\native\services\inputflinger\InputReader.cpp
void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {
    for (const RawEvent* rawEvent = rawEvents; count;) {
        int32_t type = rawEvent->type;
        size_t batchSize = 1;
        if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) {
            int32_t deviceId = rawEvent->deviceId;
            while (batchSize < count) {
                if (rawEvent[batchSize].type >= EventHubInterface::FIRST_SYNTHETIC_EVENT
                        || rawEvent[batchSize].deviceId != deviceId) {
                    break;
                }
                batchSize += 1;
            }
#if DEBUG_RAW_EVENTS
            ALOGD("BatchSize: %zu Count: %zu", batchSize, count);
#endif
            processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
        } else {
            ...
        }
        //Ensure that the processeventsfordevice locked method is called only once for one press
        count -= batchSize;
        rawEvent += batchSize;
    }
}

1.2: obtain the corresponding InputDevice according to the device id (the InputDevice will be encapsulated when the device is mounted, including the entity mapper that can receive events)

//frameworks\native\services\inputflinger\InputReader.cpp
void InputDevice::process(const RawEvent* rawEvents, size_t count) {
    for (const RawEvent* rawEvent = rawEvents; count != 0; rawEvent++) {
#if DEBUG_RAW_EVENTS
        ALOGD("Input event: device=%d type=0x%04x code=0x%04x value=0x%08x when=%" PRId64,
                rawEvent->deviceId, rawEvent->type, rawEvent->code, rawEvent->value,
                rawEvent->when);
#endif

        if (mDropUntilNextSync) {
            if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
                mDropUntilNextSync = false;
#if DEBUG_RAW_EVENTS
                ALOGD("Recovered from input event buffer overrun.");
#endif
            } else {
#if DEBUG_RAW_EVENTSA
                ALOGD("Dropped input event while waiting for next input sync.");
#endif
            }
        } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) {
            ALOGI("Detected input event buffer overrun for device %s.", getName().c_str());
            mDropUntilNextSync = true;
            reset(rawEvent->when);
        } else {
            for (InputMapper* mapper : mMappers) {
                //When the device is added, it will be parsed. In this example, the device includes KeyboardInputMapper and joysticks inputmapper. Press the key here to analyze the KeyboardInputMapper
                mapper->process(rawEvent);
            }
        }
        --count;
    }
}

1.3: event analysis, encapsulation and transfer to InputDispatcher

//frameworks\native\services\inputflinger\InputReader.cpp
void KeyboardInputMapper::process(const RawEvent* rawEvent) {
    switch (rawEvent->type) {
    case EV_KEY: {
        int32_t scanCode = rawEvent->code;
        int32_t usageCode = mCurrentHidUsage;
        mCurrentHidUsage = 0;

        if (isKeyboardOrGamepadKey(scanCode)) {
            processKey(rawEvent->when, rawEvent->value != 0, scanCode, usageCode);
        }
        break;
    }
    case EV_MSC: {
        if (rawEvent->code == MSC_SCAN) {
            mCurrentHidUsage = rawEvent->value;
        }
        break;
    }
    case EV_SYN: {
        if (rawEvent->code == SYN_REPORT) {
            mCurrentHidUsage = 0;
        }
    }
    }
}
//frameworks\native\services\inputflinger\InputReader.cpp
void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t scanCode,
        int32_t usageCode) {
    int32_t keyCode;
    int32_t keyMetaState;
    uint32_t policyFlags;
    //Keyboard mapping is not analyzed here
    if (getEventHub()->mapKey(getDeviceId(), scanCode, usageCode, mMetaState,
                              &keyCode, &keyMetaState, &policyFlags)) {
        keyCode = AKEYCODE_UNKNOWN;
        keyMetaState = mMetaState;
        policyFlags = 0;
    }
    ...
    //Event encapsulation passes events to inputdispatcher
    NotifyKeyArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource,
            getDisplayId(), policyFlags, down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
            AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime);
    getListener()->notifyKey(&args);
}

Step 2: InputDispatcher event handling. There are two main tasks:

  1. JNI calls the java layer InputManagerService to handle global events. Subsequent Section 2.3 analysis.
  2. The event is encapsulated as a KeyEntry and pushed into the queue, and the InputDispatcher thread polls for processing. Follow up Section 2.4 analysis.
//frameworks\native\services\inputflinger\InputDispatcher.cpp
void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {
#if DEBUG_INBOUND_EVENT_DETAILS
    ALOGD("notifyKey - eventTime=%" PRId64
            ", deviceId=%d, source=0x%x, displayId=%" PRId32 "policyFlags=0x%x, action=0x%x, "
            "flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, downTime=%" PRId64,
            args->eventTime, args->deviceId, args->source, args->displayId, args->policyFlags,
            args->action, args->flags, args->keyCode, args->scanCode,
            args->metaState, args->downTime);
#endif
    ...
    //Encapsulated as KeyEvent
    KeyEvent event;
    event.initialize(args->deviceId, args->source, args->displayId, args->action,
            flags, keyCode, args->scanCode, metaState, repeatCount,
            args->downTime, args->eventTime);

    android::base::Timer t;
    //Call Java InputManagerService through JNI.
    mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags);
    ...
    bool needWake;
    { // acquire lock
        mLock.lock();

        ...
        //Encapsulated as KeyEntry
        KeyEntry* newEntry = new KeyEntry(args->sequenceNum, args->eventTime,
                args->deviceId, args->source, args->displayId, policyFlags,
                args->action, flags, keyCode, args->scanCode,
                metaState, repeatCount, args->downTime);
        //When pushed into the queue, the InputDispatcher thread polls and distributes events to the Server and then to the Client
        needWake = enqueueInboundEventLocked(newEntry);
        mLock.unlock();
    } // release lock

    ...
}
//frameworks\native\services\inputflinger\InputDispatcher.cpp
bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) {
    bool needWake = mInboundQueue.isEmpty();
    //Push event encapsulated objects into the queue
    mInboundQueue.enqueueAtTail(entry);
    traceInboundQueueLengthLocked();
    ...
}

2.3 global event distribution

JNI calls the java layer InputManagerService method interceptkeybeforequeuing, and finally calls the phonewindowmanager (some can be carwindowmanager) interceptkeybeforequeuing method. In the interceptkeybeforequeuing method, we mainly do some global processing. Some known key functions (display the nearest background, adjust the volume, and reject incoming calls) are processed at this level, or called to system applications (such as SystemUI).
Referring to the system scheme, the system global keys can be processed in PhoneWindowManager. Recently, there is a party control requirement for the vehicle project (the upper layer of the system intercepts the party control keys, broadcasts the keys to each application, and each application handles them separately). It is customized in PhoneWindowManager. Some codes are as follows.

frameworks\base\services\core\java\com\android\server\policy\PhoneWindowManager.java
@Override
public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
  if (!mSystemBooted) {
      // If we have not yet booted, don't let key events do anything.
      return 0;
  }
  if (mSaicInputManager.isInterceptHardKey(event)) {
      Log.i(SaicInputManager.TAG, "interceptKeyBeforeQueueing");
      return 0;
  }
  ...
}

The flow chart of global key events is as follows:

2.4 Server side event distribution

android system creates a pair of anonymous and interconnected sockets through socket pair: client-side sending and server-side interface. Because the android system uses two-way communication, it is client-side and server-side. To avoid ambiguity, the server-side event distribution in this section refers to the android system_ The socket inside the process sends (sends to the client process) and receives (accepts the result of the event processed by the client process).
The following mainly introduces the brief steps of Server-side event distribution:
step 1: get the events pushed into the queue

//frameworks\native\services\inputflinger\InputDispatcher.cpp
void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
    if (! mPendingEvent) {
        if (mInboundQueue.isEmpty()) {
            ...
        } else {
            // Inbound queue has at least one entry.
            //Get mainboundqueue header event
            mPendingEvent = mInboundQueue.dequeueAtHead();
            traceInboundQueueLengthLocked();
        }
       ...
     switch (mPendingEvent->type) {
    ...
    case EventEntry::TYPE_KEY: {
        KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent);
        ...
        ALOGD("dispatchKeyLocked dropReason"+dropReason);
        done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
        break;
    }

    case EventEntry::TYPE_MOTION: {
        ...
    }

    default:
        ALOG_ASSERT(false);
        break;
    }
    ...
}
//frameworks\native\services\inputflinger\InputDispatcher.cpp
bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry,
        DropReason* dropReason, nsecs_t* nextWakeupTime) {
    ...

    // Identify targets.
    std::vector<InputTarget> inputTargets;
    //step 2: get the Window of the current focus
    int32_t injectionResult = findFocusedWindowTargetsLocked(currentTime,
            entry, inputTargets, nextWakeupTime);
    if (injectionResult == INPUT_EVENT_INJECTION_PENDING) {
        return false;
    }

    setInjectionResult(entry, injectionResult);
    if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
        return true;
    }

    // step 3: add screen global listening
    addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(entry));

    // step 4, 5, 6: event distribution
    dispatchEventLocked(currentTime, entry, inputTargets);
    return true;
}

step 2: retrieve the customer Window that the event needs to be sent and encapsulate it into an InputTarget object

//frameworks\native\services\inputflinger\InputDispatcher.cpp
int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime,
        const EventEntry* entry, std::vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime) {
    int32_t injectionResult;
    std::string reason;

    int32_t displayId = getTargetDisplayId(entry);
    sp<InputWindowHandle> focusedWindowHandle =
            getValueByKey(mFocusedWindowHandlesByDisplay, displayId);
    sp<InputApplicationHandle> focusedApplicationHandle =
            getValueByKey(mFocusedApplicationHandlesByDisplay, displayId);
    ...
    // Success!  Output targets.
    injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
    //Encapsulate InputTarget and add it to inputTargets
    //Subsequent events are pushed into the event processing queue, involving the attribute value InputTarget::FLAG_DISPATCH_AS_IS judgment
    addWindowTargetLocked(focusedWindowHandle,
            InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS, BitSet32(0),
            inputTargets);

    // Done.
Failed:
Unresponsive:
    ...
#if DEBUG_FOCUS
    ALOGD("findFocusedWindow finished: injectionResult=%d, "
            "timeSpentWaitingForApplication=%0.1fms",
            injectionResult, timeSpentWaitingForApplication / 1000000.0);
#endif
    return injectionResult;
}
//frameworks\native\services\inputflinger\InputDispatcher.cpp
void InputDispatcher::addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle,
        int32_t targetFlags, BitSet32 pointerIds, std::vector<InputTarget>& inputTargets) {
    sp<InputChannel> inputChannel = getInputChannelLocked(windowHandle->getToken());
    if (inputChannel == nullptr) {
        ALOGW("Window %s already unregistered input channel", windowHandle->getName().c_str());
        return;
    }

    const InputWindowInfo* windowInfo = windowHandle->getInfo();
    InputTarget target;
    target.inputChannel = inputChannel;
    target.flags = targetFlags;
    target.xOffset = - windowInfo->frameLeft;
    target.yOffset = - windowInfo->frameTop;
    target.globalScaleFactor = windowInfo->globalScaleFactor;
    target.windowXScale = windowInfo->windowXScale;
    target.windowYScale = windowInfo->windowYScale;
    target.pointerIds = pointerIds;
    inputTargets.push_back(target);
}

step 3: add global monitoring to handle system level events, such as negative one screen and global gesture operation

//frameworks\native\services\inputflinger\InputDispatcher.cpp
void InputDispatcher::addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets,
         int32_t displayId, float xOffset, float yOffset) {

    std::unordered_map<int32_t, std::vector<Monitor>>::const_iterator it =
            mGlobalMonitorsByDisplay.find(displayId);

    if (it != mGlobalMonitorsByDisplay.end()) {
        const std::vector<Monitor>& monitors = it->second;
        for (const Monitor& monitor : monitors) {
            addMonitoringTargetLocked(monitor, xOffset, yOffset, inputTargets);
        }
    }
}
//frameworks\native\services\inputflinger\InputDispatcher.cpp
void InputDispatcher::addMonitoringTargetLocked(const Monitor& monitor,
        float xOffset, float yOffset, std::vector<InputTarget>& inputTargets) {
    InputTarget target;
    target.inputChannel = monitor.inputChannel;
    //Set the attribute to FLAG_DISPATCH_AS_IS, subsequent Step 5 events entering the queue will involve
    target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
    target.xOffset = xOffset;
    target.yOffset = yOffset;
    target.pointerIds.clear();
    target.globalScaleFactor = 1.0f;
    inputTargets.push_back(target);
}

It can be seen from the code that there is an mGlobalMonitorsByDisplay object. For its addition logic, please refer to the section on Server-side event listening. Due to space constraints, I will not repeat it here for the time being.

step 4: get Connection

//frameworks\native\services\inputflinger\InputDispatcher.cpp
void InputDispatcher::dispatchEventLocked(nsecs_t currentTime,
        EventEntry* eventEntry, const std::vector<InputTarget>& inputTargets) {
    ATRACE_CALL();
#if DEBUG_DISPATCH_CYCLE
    ALOGD("dispatchEventToCurrentInputTargets");
#endif
    ...
    //Traverse inputTargets: including the current foucus window and the global listening window
    for (const InputTarget& inputTarget : inputTargets) {
        //Traverse to obtain the Server-side storage Connection corresponding to InputTarget,
        ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel);
        if (connectionIndex >= 0) {
            sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
            //Step 5: event distribution
            prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget);
        } else {
#if DEBUG_FOCUS
            ALOGD("Dropping event delivery to target with channel '%s' because it "
                    "is no longer registered with the input dispatcher.",
                    inputTarget.inputChannel->getName().c_str());
#endif
        }
    }
}
//frameworks\native\services\inputflinger\InputDispatcher.cpp
ssize_t InputDispatcher::getConnectionIndexLocked(const sp<InputChannel>& inputChannel) {
    if (inputChannel == nullptr) {
        return -1;
    }

    for (size_t i = 0; i < mConnectionsByFd.size(); i++) {
        sp<Connection> connection = mConnectionsByFd.valueAt(i);
        if (connection->inputChannel->getToken() == inputChannel->getToken()) {
            return i;
        }
    }

    return -1;
}

step 5: press the event into the queue
When introducing Step 4, we mentioned the prepareDispatchCycleLocked method. The main method in this method is enqueueDispatchEntriesLocked. The code is as follows. Now use enqueueDispatchEntriesLocked to introduce event distribution.

//frameworks\native\services\inputflinger\InputDispatcher.cpp
void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime,
        const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget) {
    ...
    bool wasEmpty = connection->outboundQueue.isEmpty();

    // Enqueue dispatch entries for the requested modes.
    enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
            InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT);
    enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
            InputTarget::FLAG_DISPATCH_AS_OUTSIDE);
    enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
            InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER);
    //The InputTarget attribute corresponding to the window of global listening and Focus is FLAG_DISPATCH_AS_IS,
    enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
            InputTarget::FLAG_DISPATCH_AS_IS);
    enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
            InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT);
    enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
            InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER);

    // If the outbound queue was previously empty, start the dispatch cycle going.
    if (wasEmpty && !connection->outboundQueue.isEmpty()) {
        startDispatchCycleLocked(currentTime, connection);
    }
}
//frameworks\native\services\inputflinger\InputDispatcher.cpp
void InputDispatcher::enqueueDispatchEntryLocked(
        const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget,
        int32_t dispatchMode) {
    ...
    int32_t inputTargetFlags = inputTarget->flags;
    //If the flag is not satisfied, it will be returned directly. DispatchEntry will not be added and pushed into the outboundQueue queue queue
    if (!(inputTargetFlags & dispatchMode)) {
        return;
    }
    inputTargetFlags = (inputTargetFlags & ~InputTarget::FLAG_DISPATCH_MASK) | dispatchMode;

    // This is a new event.
    // Enqueue a new dispatch entry onto the outbound queue for this connection.
    DispatchEntry* dispatchEntry = new DispatchEntry(eventEntry, // increments ref
            inputTargetFlags, inputTarget->xOffset, inputTarget->yOffset,
            inputTarget->globalScaleFactor, inputTarget->windowXScale,
            inputTarget->windowYScale);

    ...

    // Enqueue the dispatch entry.
    connection->outboundQueue.enqueueAtTail(dispatchEntry);
}

The enqueueDispatchEntryLocked method encapsulates the event to be distributed into a DispatchEntry and adds it to the end of the queue outboundQueue. The outboundQueue is not empty, and then enter Step 6 event sending.
step 6: event sending
The startDispatchCycleLocked method finally calls the socket api to send the event to the corresponding handle fd on the Server side.

//frameworks\native\services\inputflinger\InputDispatcher.cpp
void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
        const sp<Connection>& connection) {
    
    ...
    while (connection->status == Connection::STATUS_NORMAL
            && !connection->outboundQueue.isEmpty()) {
        DispatchEntry* dispatchEntry = connection->outboundQueue.head;
        dispatchEntry->deliveryTime = currentTime;

        // Publish the event.
        status_t status;
        EventEntry* eventEntry = dispatchEntry->eventEntry;
        switch (eventEntry->type) {
        case EventEntry::TYPE_KEY: {
            KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);

            // sd add start for multidisplay emulator input
            if(mIsEmulator && keyEntry->displayId == 0)
                mShouldSwitchDisplay = true;
            // sd add end

            // Call the publishKeyEvent inside the Connection to send the event to the corresponding fd on the Server side
            status = connection->inputPublisher.publishKeyEvent(dispatchEntry->seq,
                    keyEntry->deviceId, keyEntry->source, keyEntry->displayId,
                    dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags,
                    keyEntry->keyCode, keyEntry->scanCode,
                    keyEntry->metaState, keyEntry->repeatCount, keyEntry->downTime,
                    keyEntry->eventTime);
            break;
        }

        case EventEntry::TYPE_MOTION: {
            ...
        }

        default:
            ALOG_ASSERT(false);
            return;
        }

        // Check the result.
        if (status) {
            ...
        }

        //Out of queue
        connection->outboundQueue.dequeue(dispatchEntry);
        traceOutboundQueueLength(connection);
        //Press the events that need to wait for processing results into the waitQueue. After subsequent client events are processed, the processed events will be out of the queue
        connection->waitQueue.enqueueAtTail(dispatchEntry);
        traceWaitQueueLength(connection);
    }
//frameworks\native\libs\input\InputTransport.cpp
status_t InputChannel::sendMessage(const InputMessage* msg) {
    const size_t msgLength = msg->size();
    InputMessage cleanMsg;
    ...
    ssize_t nWrite;
    do {
    //socket communication
        nWrite = ::send(mFd, &cleanMsg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);
    } while (nWrite == -1 && errno == EINTR);

    if (nWrite < 0) {
      ...
    }
    ...
    return OK;
}

step 6: accept the results of client event processing
After the event is processed, the pollOnce method of Looper will be called to read the processing results of the event.

void InputDispatcher::dispatchOnce() {
    nsecs_t nextWakeupTime = LONG_LONG_MAX;
    { 
         //
        if (!haveCommandsLocked()) {
            dispatchOnceInnerLocked(&nextWakeupTime);
        }
         ...
    } 
    ...
    //Gets the completed events in the queue
    mLooper->pollOnce(timeoutMillis);
}

The flow chart of server-side event distribution is shown below

The sequence diagram is a little convoluted. It can be combined with the class diagram to facilitate the relationship between classes. The class diagram designed for sever end event distribution is as follows:

2.3 Client side event reception

Step 1: read the events sent by the Server
Chapter 1, section 5, Client registration, briefly introduces the function of Looper's addFd method. This section mainly introduces the logic of the Client receiving events after the Server sends events. Start with the handleEvent method (loop core logic, which will not be introduced here):

//frameworks\base\core\jni\android_view_InputEventReceiver.cpp
int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) {
    ...
    if (events & ALOOPER_EVENT_INPUT) {
        JNIEnv* env = AndroidRuntime::getJNIEnv();
        status_t status = consumeEvents(env, false /*consumeBatches*/, -1, NULL);
        mMessageQueue->raiseAndClearException(env, "handleReceiveCallback");
        return status == OK || status == NO_MEMORY ? 1 : 0;
    }
    ...
    ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event.  "
            "events=0x%x", getInputChannelName().c_str(), events);
    return 1;
}
//frameworks\base\core\jni\android_view_InputEventReceiver.cpp
status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
        bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) {
    ...

    ScopedLocalRef<jobject> receiverObj(env, NULL);
    bool skipCallbacks = false;
    for (;;) {
        uint32_t seq;
        InputEvent* inputEvent;
        status_t status = mInputConsumer.consume(&mInputEventFactory,
                consumeBatches, frameTime, &seq, &inputEvent);
        if (status) {
        ...
        assert(inputEvent);

        if (!skipCallbacks) {
            ...
            jobject inputEventObj;
            switch (inputEvent->getType()) {
            case AINPUT_EVENT_TYPE_KEY:
                if (kDebugDispatchCycle) {
                    ALOGD("channel '%s' ~ Received key event.", getInputChannelName().c_str());
                }
                //Event encapsulation
                inputEventObj = android_view_KeyEvent_fromNative(env,
                        static_cast<KeyEvent*>(inputEvent));
                break;

            case AINPUT_EVENT_TYPE_MOTION: {
                ...
                break;
            }

            default:
                assert(false); // InputConsumer should prevent this from ever happening
                inputEventObj = NULL;
            }

            if (inputEventObj) {
                if (kDebugDispatchCycle) {
                    ALOGD("channel '%s' ~ Dispatching input event.", getInputChannelName().c_str());
                }
                //Step 2: call the Java layer method to enter the View event distribution logic
                env->CallVoidMethod(receiverObj.get(),
                        gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);
                if (env->ExceptionCheck()) {
                    ALOGE("Exception dispatching input event.");
                    skipCallbacks = true;
                }
                env->DeleteLocalRef(inputEventObj);
            } else {
                ALOGW("channel '%s' ~ Failed to obtain event object.",
                        getInputChannelName().c_str());
                skipCallbacks = true;
            }
        }
        ...
    }
}

It can be seen from the code that the event reading mainly calls the consume method of InputConsumer, which is finally implemented by calling the socket recv method.
Step 2: throw the read event to the Java layer for View event distribution
The dispatchInputEvent method is not introduced here. The event distribution of View involved in the general interview starts from this step.

Step 3: after the event processing ends, send the event processing result to the Server side
After the View event is distributed, the processing results will be sent to the Server. The Server side will get through the pollOnce method of Looper and clean up the event today. The View result is sent using the sendFinishedSignal method of inputconsumer. Finally, the socket API send method is called to send the event to the Server.

//frameworks\base\core\jni\android_view_InputEventReceiver.cpp
status_t NativeInputEventReceiver::finishInputEvent(uint32_t seq, bool handled) {
    if (kDebugDispatchCycle) {
        ALOGD("channel '%s' ~ Finished input event.", getInputChannelName().c_str());
    }

    status_t status = mInputConsumer.sendFinishedSignal(seq, handled);
    if (status) {
        if (status == WOULD_BLOCK) {
            if (kDebugDispatchCycle) {
                ALOGD("channel '%s' ~ Could not send finished signal immediately.  "
                        "Enqueued for later.", getInputChannelName().c_str());
            }
            Finish finish;
            finish.seq = seq;
            finish.handled = handled;
            mFinishQueue.add(finish);
            if (mFinishQueue.size() == 1) {
                setFdEvents(ALOOPER_EVENT_INPUT | ALOOPER_EVENT_OUTPUT);
            }
            return OK;
        }
        ALOGW("Failed to send finished signal on channel '%s'.  status=%d",
                getInputChannelName().c_str(), status);
    }
    return status;
}

The sequence diagram of events received by the Client is as follows:

summary

InputManagerService event distribution ends here. There are still many problems to be analyzed:

  • [1] switch of focus window
  • [2] Analysis of looper addfd monitoring method, and pollOnce method
  • [3] Processing of waitQueue by Server after event processing.
  • [4] View event distribution, how to distribute events to each view.
  • [5] Key mapping
    It can be seen from here that the Input system is quite complex, and the problems in doubt will be analyzed in other chapters [1] [2]. The same will be true for other issues when appropriate.

Posted by trg on Sat, 09 Oct 2021 02:26:30 -0700