Where do the events distributed by Andriod events come from (2)

Where do the events of Andriod event distribution come from (2)

The last article left a question, where did the WMS incident come from?

The registration event callback is implemented through mWindowSession.addToDisplayAsUser, which is a Binder call that actually calls the frameworks/base/services/core/java/com/android/server/wm/Session.java class.

 //frameworks/base/services/core/java/com/android/server/wm/Session.java
  @Override
    public int addToDisplayAsUser(IWindow window, WindowManager.LayoutParams attrs,
            int viewVisibility, int displayId, int userId, InsetsVisibilities requestedVisibilities,
            InputChannel outInputChannel, InsetsState outInsetsState,
            InsetsSourceControl[] outActiveControls, Rect outAttachedFrame,
            float[] outSizeCompatScale) {<!-- -->
        return mService.addWindow(this, window, attrs, viewVisibility, displayId, userId,
                requestedVisibilities, outInputChannel, outInsetsState, outActiveControls,
                outAttachedFrame, outSizeCompatScale);
    }

The mService here is WMS. What is called is the addWindow of WMS. The addWindow method is very long, and there are only two lines related to the event.

//frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
?…
  final WindowState win = new WindowState(this, session, client, token, parentWindow, appOp[0], attrs, viewVisibility, session.mUid, userId,session.mCanAddInternalSystemWindow);
 win. openInputChannel(outInputChannel);
//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();
        mInputChannel = mWmService.mInputManager.createInputChannel(name);
        mInputChannelToken = mInputChannel. getToken();
        mInputWindowHandle.setToken(mInputChannelToken);
        mWmService.mInputToWindowMap.put(mInputChannelToken, this);
        if (outInputChannel != null) {<!-- -->
          //Copy the InputChannel created by native to the parameter outInputChannel
            mInputChannel. copyTo(outInputChannel);
        } else {<!-- -->
            // If the window died visible, we setup a fake input channel, so that taps
            // can still detected by input monitor channel, and we can relaunch the app.
            // Create fake event receiver that simply reports all events as handled.
            mDeadWindowEventReceiver = new DeadWindowEventReceiver(mInputChannel);
        }
    }

The member mInputManager in WMS is called to register InputChannel, and mInputManager is an InputManagerService. That’s right, it’s reasonable for events to come from InputManagerService.

 public InputChannel createInputChannel(String name) {<!-- -->
        return mNative.createInputChannel(name);
    }

The method of calling mNative, this object is initialized when InputManagerService is created

 public InputManagerService(Context context) {<!-- -->
        this(new Injector(context, DisplayThread. get(). getLooper()));
    }

    @VisibleForTesting
    InputManagerService(Injector injector) {<!-- -->
        // The static association map is accessed by both java and native code, so it must be
        // initialized before initializing the native service.
        mStaticAssociations = loadStaticInputPortAssociations();

        mContext = injector. getContext();
        mHandler = new InputManagerHandler(injector. getLooper());
        mNative = injector. getNativeService(this);
      ....
    }
//frameworks/base/services/core/java/com/android/server/input/NativeInputManagerService.java
       NativeInputManagerService getNativeService(InputManagerService service) {<!-- -->
            return new NativeInputManagerService. NativeImpl(service, mContext, mLooper. getQueue());
        }

What is finally returned is a NativeImpl instance. The literal meaning is known, this is the implementation of a Native method, createInputChannel came to the native layer.

//frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
base::Result<std::unique_ptr<InputChannel>> NativeInputManager::createInputChannel(
        const std::string & amp; name) {
    ATRACE_CALL();
    return mInputManager->getDispatcher().createInputChannel(name);
}

After calling the getDispatcher function of mInputManager, you can see that there should be a variable mDispatcher by looking at the name. If you check how mInputManager is created, you can find that it was initialized when NativeInputManager was created.

 InputManager* im = new InputManager(this, this);
    mInputManager = im;

Then look at how the InputManager is initialized.

InputManager::InputManager(
        const sp<InputReaderPolicyInterface> & readerPolicy,
        const sp<InputDispatcherPolicyInterface> & dispatcherPolicy) {
    mDispatcher = createInputDispatcher(dispatcherPolicy);
    mClassifier = std::make_unique<InputClassifier>(*mDispatcher);
    mBlocker = std::make_unique<UnwantedInteractionBlocker>(*mClassifier);
    mReader = createInputReader(readerPolicy, *mBlocker);
}

Here are two important classes InputDispatcher and InputReader, and the createInputChanne method l finally calls the createInputChannel in InputDispatcher.

//frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputChannel(const std::string & amp; name) {
    if (DEBUG_CHANNEL_CREATION) {
        ALOGD("channel '%s' ~ createInputChannel", name.c_str());
    }

    std::unique_ptr<InputChannel> serverChannel;
    std::unique_ptr<InputChannel> clientChannel;
  //The call creates a serverChannel and a clientChannel
    status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);

    if (result) {
        return base::Error(result) << "Failed to open input channel pair with name " << name;
    }

    { // acquire lock
        std::scoped_lock_l(mLock);
        const sp<IBinder> & token = serverChannel->getConnectionToken();
        int fd = serverChannel->getFd();
        sp<Connection> connection =
                new Connection(std::move(serverChannel), false /*monitor*/, mIdGenerator);

        if (mConnectionsByToken. find(token) != mConnectionsByToken. end()) {
            ALOGE("Created a new connection, but the token %p is already known", token.get());
        }
        mConnectionsByToken. emplace(token, connection);

        std::function<int(int events)> callback = std::bind( &InputDispatcher::handleReceiveCallback,
                                                            this, std::placeholders::_1, token);

        mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, new LooperEventCallback(callback), nullptr);
    } // release lock

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

createInputChannel does 3 things

  1. First use openInputChannelPair to create 2 InputChannels, a clientChannel and a serverChannel
  2. Encapsulate the serverChannel into a connection and put it into the member variable mConnectionsByToken for management, so that when the event arrives, the connection can be used to send the event to the client
  3. Using Looper to continuously monitor the serverChannel, the callback message of event processing will be called back to InputDispatcher::handleReceiveCallback, and finally the clientChannel will be returned to the client, which is the InputChannel originally obtained in WMS.

First look at openInputChannelPair

//frameworks/native/libs/input/InputTransport.cpp
status_t InputChannel::openInputChannelPair(const std::string & amp; name,
                                            std::unique_ptr<InputChannel> & outServerChannel,
                                            std::unique_ptr<InputChannel> & outClientChannel) {
    int sockets[2];
  //Really create the socket
    if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) {
        status_t result = -errno;
        ALOGE("channel '%s' ~ Could not create socket pair. errno=%s(%d)", name.c_str(),
              strerror(errno), errno);
        outServerChannel. reset();
        outClientChannel. reset();
        return result;
    }
//Set the size of socket transmission to 32k
    int bufferSize = SOCKET_BUFFER_SIZE;
    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));

    sp<IBinder> token = new BBinder();

    std::string serverChannelName = name + " (server)";
    android::base::unique_fd serverFd(sockets[0]);
    outServerChannel = InputChannel::create(serverChannelName, std::move(serverFd), token);

    std::string clientChannelName = name + " (client)";
    android::base::unique_fd clientFd(sockets[1]);
    outClientChannel = InputChannel::create(clientChannelName, std::move(clientFd), token);
    return OK;
}

If you are familiar with Linux, you will know that socketpair creates a pair of two-way sockets. Writing to socket[0] can read from socket[1]. InputChannel has the same BBinder as token.

Back to createInputChannel

 const sp<IBinder> & token = serverChannel->getConnectionToken();
        int fd = serverChannel->getFd();//get socket fd
        sp<Connection> connection =
                new Connection(std::move(serverChannel), false /*monitor*/, mIdGenerator);

        if (mConnectionsByToken. find(token) != mConnectionsByToken. end()) {
            ALOGE("Created a new connection, but the token %p is already known", token.get());
        }
        mConnectionsByToken. emplace(token, connection);
 std::function<int(int events)> callback = std::bind( &InputDispatcher::handleReceiveCallback,
                                                            this, std::placeholders::_1, token);

        mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, new LooperEventCallback(callback), nullptr);

Here, the created serverChannel is encapsulated into a connection, and at the same time, the token is used as the key and stored in mConnectionsByToken, so that the token can be used to quickly find the connection encapsulated by the serverChannel. Finally, listen to the fd of the serverChannel, call back to the InputDispatcher::handleReceiveCallback method when there is an event, and finally return the created clientChannel to the client, which is the beginning of the WMS. In this way, events can also be obtained through clientChannel in WMS.