[Android event distribution] analysis of event distribution source code (the driver layer transmits events through interrupt | WindowManagerService transmits events to the View layer)

Android event distribution series article directory

[Android event distribution] analysis of event distribution source code (the driver layer transmits events through interrupt | WindowManagerService transmits events to the View layer)





1, Event distribution context


Event distribution analysis process:

① Driver layer - > Framework layer: after the user touches or presses a key, events are generated in the hardware and passed from the hardware driver layer to the Framework layer;

② WMS - > View layer: WindowManagerService (WMS for short) transfers events to the View layer;

③ Inside the View layer: events are passed between the View container and the lower container / component;





2, The driver layer passes events through interrupts


After the hardware generates an event, the driver layer transmits the event through interrupt;

Interrupt is often used in embedded Linux, which is divided into external interrupt and internal interrupt;

  • External interrupt: the interrupt generated by external events, such as the interrupt generated by the event generated by hardware touch / key;
  • Internal interruption: crash, abnormality and other conditions in program operation;

Interrupt means that when the CPU normally executes instructions, internal or external events / preset interrupt operations will cause the CPU to interrupt the currently executing instructions and run the relevant instructions corresponding to the current interrupt instead. After the interrupt program is executed, continue to execute the code that has not been executed before the subsequent interrupt;


There are two ways to interrupt: one is polling, and the CPU constantly reads the status of the hardware; The other is to send information to the CPU after the hardware generates an event, so that the CPU can suspend the current work and execute the interrupt task;





3, WindowManagerService passes events to View


stay [Android application development] UI drawing process (life cycle mechanism | layout loading mechanism | UI drawing process | layout measurement | layout placement | component drawing | waterfall flow layout case) In the blog, the UI layout drawing process is analyzed. Starting from ActivityThread, it is called step by step to draw the UI interface. The call chain is as follows:

ActivityThread | handleResumeActivity -> WindowManager | addView -> ViewRootImpl | setView ;
Finally, in the performTraversals method of ViewRootImpl, complete the operations of measurement, layout and painting;

The addView method in WindowManagerGlobal is mainly used to add DecorView;


The levels of each window are as follows: events are transferred from the Activity to the View component layer by layer;


Here, start to analyze from the setView method of ViewRootImpl;

Create an input channel directly through new InputChannel();

The addToDisplay method of WindowSession is also called. The mWindowSession member is of IWindowSession type, which is passed through mWindowSession = windowmanagerglobal Getwindowsession();
In the getWindowSession method of WindowManagerGlobal, the final WindowSession calls back to the openSession of WMS and creates a WindowSession object;

In the setView method of ViewRootImpl, register the mInputEventReceiver, and pass in the InputChannel and Looper parameters. InputChannel is the channel of the event, and Looper is used to poll whether the event occurs;

ViewRootImpl reference source code:

public final class ViewRootImpl implements ViewParent,
        View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
                requestLayout();
                if ((mWindowAttributes.inputFeatures
                        & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                    mInputChannel = new InputChannel();
                }
                
               	...

                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(), mWinFrame,
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel);

				...

                if (mInputChannel != null) {
                    if (mInputQueueCallback != null) {
                        mInputQueue = new InputQueue();
                        mInputQueueCallback.onInputQueueCreated(mInputQueue);
                    }
                    mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
                            Looper.myLooper());
                }
    }
}

/frameworks/base/core/java/android/view/ViewRootImpl.java


WindowManagerService obtains WindowSession method: by calling getWindowSession of WindowManagerGlobal, the openSession method of WindowManagerService is finally called;

WindowManagerGlobal reference source code:

public final class WindowManagerGlobal {
    public static IWindowSession getWindowSession() {
        synchronized (WindowManagerGlobal.class) {
            if (sWindowSession == null) {
                try {
                    InputMethodManager imm = InputMethodManager.getInstance();
                    IWindowManager windowManager = getWindowManagerService();
                    sWindowSession = windowManager.openSession(
                            new IWindowSessionCallback.Stub() {
                                @Override
                                public void onAnimatorScaleChanged(float scale) {
                                    ValueAnimator.setDurationScale(scale);
                                }
                            },
                            imm.getClient(), imm.getInputContext());
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            }
            return sWindowSession;
        }
    }
}

/frameworks/base/core/java/android/view/WindowManagerGlobal.java


In the addWindow method of WindowManagerService,

  • The state of the window is initialized,
  • Set the InputChannel by calling the openInputChannel method of WindowState, that is, pass in the InputChannel created by new InputChannel() in setView method in ViewRootImpl;

WindowManagerService reference source code:

/** {@hide} */
public class WindowManagerService extends IWindowManager.Stub
        implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {
    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) {
			// Initialize window status 
            final WindowState win = new WindowState(this, session, client, token, parentWindow,
                    appOp[0], seq, attrs, viewVisibility, session.mUid,
                    session.mCanAddInternalSystemWindow);

            if  (openInputChannels) {
                win.openInputChannel(outInputChannel);
            }
	}

    @Override
    public IWindowSession openSession(IWindowSessionCallback callback, IInputMethodClient client,
            IInputContext inputContext) {
        if (client == null) throw new IllegalArgumentException("null client");
        if (inputContext == null) throw new IllegalArgumentException("null inputContext");
        Session session = new Session(this, callback, client, inputContext);
        return session;
    }
}

/frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java


WindowState's openInputChannel method, in which InputChannel. is called. Openinputchannelpair (name) static method, which is enabled 2 2 Two inputchannels, inputchannels [0] on the server and inputChannels[1] on the client;

The server and the client need to communicate through Looper. Before communication, they need to register in inputdispatcher Registration in CPP;

WindowState reference source code:

/** A window in the window manager. */
class WindowState extends WindowContainer<WindowState> implements WindowManagerPolicy.WindowState {
    void openInputChannel(InputChannel outInputChannel) {
        if (mInputChannel != null) {
            throw new IllegalStateException("Window already has an input channel.");
        }
        String name = getName();
        InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
        // Server
        mInputChannel = inputChannels[0];
        // client 
        mClientChannel = inputChannels[1];
        mInputWindowHandle.inputChannel = inputChannels[0];
        if (outInputChannel != null) {
            mClientChannel.transferTo(outInputChannel);
            mClientChannel.dispose();
            mClientChannel = null;
        } else {
            // If the window died visible, we setup a dummy input channel, so that taps
            // can still detected by input monitor channel, and we can relaunch the app.
            // Create dummy event receiver that simply reports all events as handled.
            mDeadWindowEventReceiver = new DeadWindowEventReceiver(mClientChannel);
        }
        mService.mInputManager.registerInputChannel(mInputChannel, mInputWindowHandle);
    }
}

/frameworks/base/services/core/java/com/android/server/wm/WindowState.java


The following analyzes the process of registering the server and client InputChannel in InputDispatcher;

In the registerInputChannel method, a Connection connection is created, which is the channel through which the two servers communicate with the client InputChannel. Each InputChannel has an fd. Get fd through int fd = InputChannel - > getfd(), and call mconnectionsbyfd Add (fd, Connection) corresponds fd to Connection;
Finally, register fd in Looper, mloop - > addfd;

As long as there is any event input, the Looper will be awakened and passed to the Activity through the InputChannel, and then to the View components at all levels;

status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel,
        const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {
#if DEBUG_REGISTRATION
    ALOGD("channel '%s' ~ registerInputChannel - monitor=%s", inputChannel->getName().c_str(),
            toString(monitor));
#endif

    { // acquire lock
        AutoMutex _l(mLock);

        if (getConnectionIndexLocked(inputChannel) >= 0) {
            ALOGW("Attempted to register already registered input channel '%s'",
                    inputChannel->getName().c_str());
            return BAD_VALUE;
        }

        sp<Connection> connection = new Connection(inputChannel, inputWindowHandle, monitor);

        int fd = inputChannel->getFd();
        mConnectionsByFd.add(fd, connection);

        if (monitor) {
            mMonitoringChannels.push(inputChannel);
        }

        mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
    } // release lock

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

/frameworks/native/services/inputflinger/InputDispatcher.cpp

Keywords: Android

Added by pixelfish on Tue, 25 Jan 2022 17:16:36 +0200