The input driver has been modified in the past few days. Here is an introduction to the InputManagerService process: 1. Brief introduction of input drive system - Android devices can be connected to multiple input devices at the same time, such as touch screen, keyboard, mouse, etc.
- The user's input on any device will generate an interrupt, which is converted into an Event through the interrupt processing of the Linux kernel and the device driver, and then passed to the application in the user space for processing;
- Each input device has its own driver and different data interfaces. How to capture all user inputs in one thread (there is only one InputReader Thread as mentioned above)? This is first of all due to the Input Subsystem of the Linux kernel.
- It adds an abstraction layer on various device drivers. As long as the underlying device drivers are implemented according to this abstract interface, the upper-layer applications can access all input devices through a unified interface.
- This abstraction layer has three important concepts: input handler, input handle and input_dev.
- input_dev represents the underlying driver
- input_handler represents the processing method of a certain type of input device, which is equivalent to an upper-level driver
- An input_dev can have multiple input_handlers, and similarly, an input_handler can be used for multiple input devices;
- Used to associate a certain input_dev with a certain input_handler, which corresponds to the purple origin in the above figure. Each input handle will generate a file node;
input $ ls event0 event1 event2 event3 event4 event5 event6 The process of obtaining user input through the Linux input system is as simple as follows: - The device registers its driver to the Input system through input_register_dev.
- Various Handlers register themselves to the Input system through input_register_handler.
- Each registered input_dev or input_handler will find each other through input_connect(), generate the corresponding input_handle, and generate a device node file under /dev/input/.
- The application opens the file node corresponding to Input_handle and opens the corresponding input_dev and input_handler drivers. In this way, when the user presses a key, the underlying driver can capture it, hand it over to the corresponding upper driver (handler) for processing, and then return it to the application.
2. Detailed explanation of InputManagerService 2.1、InputManagerService starts InputManagerService is an abstract service for Android to handle various user operations. It can be regarded as a Binder service entity. It is instantiated when the SystemServer process is started and registered with the ServiceManager. However, this service is mainly used to provide some input device information to the outside world. As a Binder service, its role is relatively small. private void startOtherServices ( ) { ... inputManager = new InputManagerService ( context ) ; wm = WindowManagerService .main ( context , inputManager , mFactoryTestMode != FactoryTest .FACTORY_TEST_LOW_LEVEL , ! mFirstBoot , mOnlyCore ) ; ServiceManager .addService ( Context .WINDOW_SERVICE , wm ) ; ServiceManager .addService ( Context .INPUT_SERVICE , inputManager ) ; ... } InputManagerService and WindowManagerService were added almost at the same time, which to some extent shows that the two are almost mutually dependent. The handling of touch events does involve two services at the same time. The best evidence is that WindowManagerService needs to directly hold the reference of InputManagerService; If we compare it with the above processing model, InputManagerService is mainly responsible for collecting touch events; The WindowManagerService is responsible for finding the target window. Next, let's take a look at how the InputManagerService completes the collection of touch events; 2.2 How to capture touch events InputManagerService will open a separate thread specifically for reading touch events. NativeInputManager :: NativeInputManager ( jobject contextObj , jobject serviceObj , const sp < Looper >& looper ) : mLooper ( looper ) , mInteractive ( true ) { ... sp < EventHub > eventHub = new EventHub ( ) ; mInputManager = new InputManager ( eventHub , this , this ) ; } - There is an EventHub, which mainly uses Linux's inotify and epoll mechanisms;
- Monitor device events: including device plug-in and unplug, various touch and button events, etc. It can be regarded as a hub for different devices, mainly targeting the device nodes under the /dev/input directory. For example, the event on /dev/input/event0 is an input event, which can be monitored and obtained through EventHub's getEvents:
When creating a new InputManager, a new InputReader object and InputReaderThread Loop thread will be created. The main function of this loop thread is to obtain Input events through EventHub's getEvents InputManager :: InputManager ( const sp < EventHubInterface >& eventHub , const sp <InputReaderPolicyInterface> & readerPolicy , const sp < InputDispatcherPolicyInterface >& dispatcherPolicy ) { <!-- Event distribution execution class --> mDispatcher = new InputDispatcher ( dispatcherPolicy ) ; <!-- Event reading execution class --> mReader = new InputReader ( eventHub , readerPolicy , mDispatcher ) ; initialize ( ) ; } void InputManager :: initialize ( ) { mReaderThread = new InputReaderThread ( mReader ) ; mDispatcherThread = new InputDispatcherThread ( mDispatcher ) ; } bool InputReaderThread :: threadLoop ( ) { mReader- > loopOnce ( ) ; return true ; } void InputReader :: loopOnce ( ) { int32_t oldGeneration ; int32_t timeoutMillis ; bool inputDevicesChanged = false ; Vector < InputDeviceInfo > inputDevices ; { ... <!-- Monitoring events --> size_t count = mEventHub -> getEvents ( timeoutMillis , mEventBuffer , EVENT_BUFFER_SIZE ) ; .... <!-- Handle events --> processEventsLocked ( mEventBuffer , count ) ; ... <!-- Notification distribution --> mQueuedListener -> flush ( ) ; } The input event can be read, initially encapsulated into a RawEvent through processEventsLocked, and finally a notification is sent to request the dispatch of a message; 2.3 Event Distribution - When creating a new InputManager, not only an event reading thread is created;
- An event dispatching thread is also created. Although it can be dispatched directly in the reading thread, this will definitely increase the time consumption and is not conducive to the timely reading of events.
- Therefore, after the event is read, a notification is sent directly to the dispatch thread, asking the dispatch thread to handle it. In this way, the reading thread can be more agile and prevent event loss. Therefore, the model of InputManager is as follows:
The mQueuedListener of InputReader is actually the InputDispatcher object, so mQueuedListener->flush() is to notify the InputDispatcher that the event has been read and the event can be dispatched. InputDispatcherThread is a typical Looper thread. It implements the Hanlder message processing model based on native Looper. If an Input event arrives, it will be awakened to process the event, and continue to sleep and wait after processing. The code is as follows: bool InputDispatcherThread :: threadLoop ( ) { mDispatcher -> dispatchOnce ( ) ; return true ; } void InputDispatcher :: dispatchOnce ( ) { nsecs_t nextWakeupTime = LONG_LONG_MAX ; { <!-- Wake up and process Input message --> if ( ! haveCommandsLocked ( ) ) { dispatchOnceInnerLocked ( & nextWakeupTime ) ; } ... } nsecs_t currentTime = now ( ) ; int timeoutMillis = toMillisecondTimeoutDelay ( currentTime , nextWakeupTime ) ; <!-- Sleep waiting for input event --> mLooper -> pollOnce ( timeoutMillis ) ; } The above is the model of the dispatch thread. dispatchOnceInnerLocked is the specific dispatch processing logic. Here is one of the branches, the touch event: void InputDispatcher :: dispatchOnceInnerLocked ( nsecs_t * nextWakeupTime ) { ... case EventEntry :: TYPE_MOTION : { MotionEntry * typedEntry = static_cast < MotionEntry *> ( mPendingEvent ) ; ... done = dispatchMotionLocked ( currentTime , typedEntry , & dropReason , nextWakeupTime ) ; break ; } bool InputDispatcher :: dispatchMotionLocked ( nsecs_t currentTime , MotionEntry * entry , DropReason * dropReason , nsecs_t * nextWakeupTime ) { ... Vector < InputTarget > inputTargets ; bool conflictingPointerActions = false ; int32_t injectionResult ; if ( isPointerEvent ) { <!-- Key point 1 Find the target Window --> injectionResult = findTouchedWindowTargetsLocked ( currentTime , entry , inputTargets , nextWakeupTime , & conflictingPointerActions ) ; } else { injectionResult = findFocusedWindowTargetsLocked ( currentTime , entry , inputTargets , nextWakeupTime ) ; } ... <!-- Key point 2 Distribution --> dispatchEventLocked ( currentTime , entry , inputTargets ) ; return true ; } As can be seen from the above code, for touch events, the target Window will be found first through findTouchedWindowTargetsLocked, and then the message will be sent to the target window through dispatchEventLocked; 2.4 Summary Now connect all the processes and modules in series, the process is as follows: - Tap the screen
- The Read thread of InputManagerService captures events and sends them to the Dispatcher thread after preprocessing.
- Dispatcher finds the target window
- Send events to the target window via Socket
- Find the target window to handle the event
|