Android 5.0 (Lollipop) event Input System)

Source: Internet
Author: User
Tags epoll inotify

Android 5.0 (Lollipop) event Input System)
In fact, the framework and process of the event input subsystem in Android 5.0 have not changed substantially. The Service is implemented under/frameworks/native/services/inputflinger/(4.4 in/frameworks/base/services/input ). The general part is implemented under/frameworks/native/libs/input. In the Android system, InputManagerService (IMS) is used to manage input events ). Its main task is to read event data from the device, and then send the input event to the focus window. In addition, the system must have the opportunity to process some system buttons. Obviously, to do this, IMS needs to deal with other modules, the most important of which is WMS and ViewRootImpl. The main modules are as follows:

VcPmvavKwrz + zai5/strong + strong/strong + jrFZpZXdSb290SW1wbM/strong + strong/rXEtLC/strong/atcS + strong/strong + tNNEcml2ZXK/strong/ co7o8YnI + cda-vcd4kpha + pgltzybzcm9 "http://www.2cto.com/uploadfile/Collfiles/20141215/2014121508324848.png" alt = "\">

For the integrity of the story, let's take a look at initialization first. Initialize IMS in SystemServer, initialize WMS, and pass IMS as a parameter.

470            Slog.i(TAG, "Input Manager");471            inputManager = new InputManagerService(context);472473            Slog.i(TAG, "Window Manager");474            wm = WindowManagerService.main(context, inputManager,475                    mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,476                    !mFirstBoot, mOnlyCore);...483            inputManager.start();

In the IMS constructor, nativeInit () is called for initialization. Note that the Handler of DisplayThread is taken here, which means that message queue processing in IMS is performed in a separate DisplayThread. It is a frontend thread shared in the system and is mainly used for processing input and output. This allows users to experience sensitive processing less affected by other work, reducing latency. The entire initialization process is as follows:


We can see that NativeInputManager, EventHub, InputManager, InputDispatcher, InputReader, InputReaderThread, and InputDispatcherThread are initialized in sequence during initialization. NativeInputManager can be seen as the middle layer of IMS and InputManager. It converts IMS requests to operations on InputManager and its internal objects, and CALLS requests from modules in InputManager back to IMS through JNI. InputManager is the input control center. It has two key threads: InputReaderThread and InputDispatcherThread. Their main functions are InputReader and InputDispacher. The former is used to read events from the device, and the latter distributes the events to the target window. EventHub is the control center of the input device. It directly deals with the input driver. Processes the increase, decrease, and query of input devices, processes input events, and provides the getEvents () interface to the upper layer to receive events. In its constructor, there are three main tasks:
1. Create an epoll object, and then you can hook the fd of each input device to the above multi-channel waiting for input events.
2. create a pipe for wakeup and mount the read end to epoll. If the device parameter changes need to be processed and getEvents () is blocked on the device, you can call wake () write Data to the write end of pipe, so that the thread can return data from the wait.
3. The inotify mechanism is used to listen for changes in the/dev/input directory. If any change occurs, the changes must be handled.
Because the event processing is a pipeline, InputReader needs to read the event first, and then InputDispatcher can further process and distribute the event. Therefore, InputDispatcher needs to listen to InputReader. The Listener mode is used here. InputDispacher is the third parameter of the InputReader constructor. It implements the InputListenerInterface interface. In the InputReader constructor, package it into a QueuedInputListener. The member variable mArgsQueue in QueuedInputListener is a Buffer Queue. Only when flush () is used, InputDispatcher is notified at a time. QueuedInputListener applies the Command mode. It encapsulates the InputDispatcher (implementing the InputListenerInterface interface Interface), encapsulates the event processing request into policyargs, and provides the buffer execution function.

After the initialization is complete, SystemServer calls the start () function to start two threads in InputManager. First look at InputReaderThread, which is the starting point of the event processing process in the user State. Here, we take the handling of key events as an example.


InputReaderThread constantly calls InputReader's pollOnce ()-> getEvents () function to get events. These events can be input events, or events triggered by inotify Monitoring Device increase or decrease changes. When you enter the device list for the first time, the/dev/input directory is scanned to create a device list, which is included in the mDevice member variable (the device list KeyedVector is included in EventHub ). MDevices; corresponding, InputReader also has the device list KeyedVector MDevices. Add it to the former, and then add it to the latter in InputReader: addDeviceLocked .), Add the added fd to the epoll wait set. In the following epoll_wait () wait time, if an event exists, the system will return the number of readable events at the same time. Here, the events read from the Input driver are converted from the original input_event structure to the RawEvent structure and placed in the buffer of the output parameter of getEvents. After getEvents () is returned, InputReader calls processEventsLocked () to process the event. For device changes, addDeviceLocked (), removeDeviceLocked (), and handleConfigurationChangedLocked () are called based on the actual situation (). For input events from other devices, processEventsForDeviceLocked () is called for further processing. The system processes the event based on the registered InputMapper and puts the event processing request into the Buffer Queue (mArgsQueue in QueuedInputListener ).

155void QueuedInputListener::notifyKey(const NotifyKeyArgs* args) {156    mArgsQueue.push(new NotifyKeyArgs(*args));157}
At the end of loopOnce () of InputReader, call the notify () interface of each element in the QueuedInputListener: flush () Unified callback Buffer Queue:
171void QueuedInputListener::flush() {172    size_t count = mArgsQueue.size();173    for (size_t i = 0; i < count; i++) {174        NotifyArgs* args = mArgsQueue[i];175        args->notify(mInnerListener);176        delete args;177    }178    mArgsQueue.clear();179}

Take the key event as an example, and then call the policykey () function of InputDispatcher. Here, we encapsulate the parameter as a KeyEvent:

2416    KeyEvent event;2417    event.initialize(args->deviceId, args->source, args->action,2418            flags, keyCode, args->scanCode, metaState, 0,2419            args->downTime, args->eventTime);
Call the interceptKeyBeforeQueueing () function of NativeInputManager as a parameter. As the name suggests, it is to check whether the system Button needs to be processed before putting it in the waiting queue. It will be transferred back to the Java World through JNI, and finally transferred to interceptKeyBeforeQueueing () of PhoneWindowManager (). Then, create a KeyEntry object based on the input event information and call enqueueInboundEventLocked () to put it in the queue and wait for the InputDiaptcherThread thread to handle it.
2439        KeyEntry* newEntry = new KeyEntry(args->eventTime,2440                args->deviceId, args->source, policyFlags,2441                args->action, flags, keyCode, args->scanCode,2442                metaState, repeatCount, args->downTime);24432444        needWake = enqueueInboundEventLocked(newEntry);
The InputDispatcherThread is now available.


As you can see, the main task of InputDisptacher is to send the previously received input events to the PWM and App focus windows. As mentioned above, InputReaderThread will call policykey () to notify InputDispatcher after receiving the event, that is, it will be placed in mInboundQueue. In the dispatchOnce () function of InputDispatcher, it will be taken out from this queue for processing.

234        if (!haveCommandsLocked()) {235            dispatchOnceInnerLocked(&nextWakeupTime);236        }...240        if (runCommandsLockedInterruptible()) {241            nextWakeupTime = LONG_LONG_MIN;242        }

Among them, dispatchOnceInnerLocked () will call the corresponding processing function based on the EventEntry type, and call dispatchKeyLocked () as a regular example using the Key event ():

767            CommandEntry* commandEntry = postCommandLocked(768                    & InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible);769            if (mFocusedWindowHandle != NULL) {770                commandEntry->inputWindowHandle = mFocusedWindowHandle;771            }772            commandEntry->keyEntry = entry;...791    // Identify targets.792    Vector
 
   inputTargets;793    int32_t injectionResult = findFocusedWindowTargetsLocked(currentTime,794            entry, inputTargets, nextWakeupTime);..804    addMonitoringTargetsLocked(inputTargets);805806    // Dispatch the key.807    dispatchEventLocked(currentTime, entry, inputTargets);
 
It finds the target window and sends the event through the connection established with the App. If a Key event needs to be processed by the system, it is encapsulated as a CommandEntry and inserted into the mCommandQueue queue. The following runCommandLockedInterruptible () function calls doInterceptKeyBeforeDispatchingLockedInterruptible () to give PWM the opportunity for processing. Finally, dispatchOnce () calls pollOnce () to receive and process the message from the connection with the App. So how does InputDispatcher determine the window in which to send events? The member variable mfocused?whandle indicates the focus window. Then, handler () will call a series of functions (handleTargetsNotReadyLocked (), checkInjectionPermission (), callback () to check whether mfocused=whandle can receive input. If possible, add it to the target window array in the form of InputTarget. Then, dispatchEventLocked () is called for sending. How is mfocused1_whandle maintained? For better understanding, we will analyze the management of window connections and the management of the Focus window.


On the App side, the new top-level window needs to be registered to WMS, which is done in ViewRootImpl: setView.


The main steps related to input are as follows:
Create an InputChannel first. Note that it has not been initialized.

521                     mInputChannel = new InputChannel();
Initialize InputChannel and use the corresponding interface addToDisplay () of WMS ():
527                     res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,528                             getHostVisibility(), mDisplay.getDisplayId(),529                             mAttachInfo.mContentInsets, mInputChannel);
WMS establishes a connection with InputDispatcher. The process is as follows:


ViewRootImpl uses addToDisplay () in the Session to call the addWindow () of WMS (). In WMS, a pair of InputChannel is created, which is essentially a pair of local sockets. Then one is registered with InputDispatcher, and the other is the ViewRootImpl that is returned to the App as an output parameter. In this way, a connection between the App and IMS is established.

2409            if (outInputChannel != null && (attrs.inputFeatures2410                    & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {2411                String name = win.makeInputChannelName();2412                InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);2413                win.setInputChannel(inputChannels[0]);2414                inputChannels[1].transferTo(outInputChannel);24152416                mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle);2417            }
In InputDispatcher: registerInputChannel:
3327        sp
 
   connection = new Connection(inputChannel, inputWindowHandle, monitor);33283329        int fd = inputChannel->getFd();3330        mConnectionsByFd.add(fd, connection);...3336        mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
 
The Connection created here indicates an InputDispatcher Connection to the application window. In addition to the inputChannel used for transmission, inputPublisher, inputWindowHandle indicating the event receiving window, there are two queues. outboundQueue is the event to be sent, waitQueue is an event that has been sent but has not received a notification from the App. This is because for some events, the Input Dispatcher will not issue the second one when the App does not finish processing the previous one. MLooper-> addFd () puts the corresponding fd into the set where InputDispatcher is waiting. The callback function is handleReceiveCallback (), that is, InputDispatcher calls it for processing when receiving a message from the App. Finally, call mLooper-> wake () to make InputDispatcherThread return from epoll_wait.

Go back to the App. If there is no problem, create the javaswinputeventreceiver, which is the event receiver of the App.
 607                     mInputEventReceiver = new WindowInputEventReceiver(mInputChannel, 608                             Looper.myLooper());
After initialization, the fd of this connection is mounted to the waiting fd set of the main thread (InputEventReceiver: nativeInit ()). That is to say, when there is a message on the connection, the main thread will call the corresponding callback to process NativeInputEventReceiver: handleEvent ().

Next, initialize the App-side event processing pipeline. The Chain of responsibility mode is used here to let the event go through various inputstages. Each Stage can decide whether to process it by itself or pass it to the next one. This is also true for the next generation. You can see their usage in handleEvent.
 623                 // Set up the input pipeline. 624                 CharSequence counterSuffix = attrs.getTitle(); 625                 mSyntheticInputStage = new SyntheticInputStage(); 626                 InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage); 627                 InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage, 628                         "aq:native-post-ime:" + counterSuffix); 629                 InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage); 630                 InputStage imeStage = new ImeInputStage(earlyPostImeStage, 631                         "aq:ime:" + counterSuffix); 632                 InputStage viewPreImeStage = new ViewPreImeInputStage(imeStage); 633                 InputStage nativePreImeStage = new NativePreImeInputStage(viewPreImeStage, 634                         "aq:native-pre-ime:" + counterSuffix); 635 636                 mFirstInputStage = nativePreImeStage; 637                 mFirstPostImeInputStage = earlyPostImeStage;
As you can see, InputDispatcher maintains connections to all windows in WMS, although it generally only sends events to the focus window. As shown below.


After the connection is established, how can WMS transmit the focus window information to InputDispatcher. For example, when a new window is added to WMS, the focus is generally placed on the new window. Let's look at the addWindow () function in WMS.


First, when the focus needs to change. When the focus window changes, WMS calls

mInputMonitor.setInputFocusLw(mCurrentFocus, updateInputWindows);
Set the focus window to mInputFocus of InputMonitor. Then call
mInputMonitor.updateInputWindowsLw(true);
To create the input?whandle list, where the hasFocus of the input?whandle set as the focus window will be set. Will be called later
mService.mInputManager.setInputWindows(mInputWindowHandles);
Upload the information to the InputDispatcher at the Native layer. In this way, InputDispatcher can know the window to which the event will be uploaded. In setInputWindows () of InputDispatcher, the focus window handle in InputDispatcher is updated. In this way, the focus window information is recorded in InputDispatcher. When IMS InputDispatcher sends an event to the focus window through InputChannel, NativeInputEventReceiver's handleEvent () will be called.


The basic process is intuitive. First, the system receives the event and puts it into the processing queue of ViewRootImpl. Then, the dispatch is sent to the View for processing. After a series of inputstages mentioned above, after the App completes event processing, it also needs to send a completion signal to IMS.

Note that the preceding example uses the Key event. There is a difference between Motion events. Because the event in touch movement does not have to be processed at all, because the display is also 60 HZ. If you input a HZ event, full processing will only waste computing resources. The above path is triggered when an InputDispatcher event is sent. For a Motion event, the system saves the event in a VSync cycle as Batch, it is processed together when VSync arrives. Starting from JB, the App's processing of input events is driven by the VSync signal. It can be seen that the input event is first processed in the VSync callback in Choreographer.

542        doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);543        doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);544        doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
When NativeInputEventReceiver: handleEvent () calls consumeEvents (), the consumeBatches parameter is false, which is known through the InputConsumper: consume () function, it will be placed in Batch:

448                if (canAddSample(batch, &mMsg)) {449                    batch.samples.push(mMsg);
Because the cyclic exit condition is not met, reading receiveMessage () Fails. After exiting, the event will not be processed immediately in consumeEvents () because the return value is not 0. When the VSync signal arrives, the following process will be triggered. Here, the consumeEvents () parameter consumeBatches is true, which means to process Batch.


ViewRootImpl maintains the pending input event list, which is indicated by mPendingInputEventHead and mPendingInputEventTail. The element is of the QueuedInputEvent type. The InputEventReceiver calls enqueueInputEvent () to add the element, and then the ViewRootImpl delay or non-delay is read and processed in doProcessInputEvents. For example, call deliverInputEvent () as shown in the preceding figure, and then call the deliver () method of InputStage for processing. Finally, finishInputEvent () is called to send the completion information to IMS. It actually calls NativeInputEventReceiver: finishInputEvent (), which internally uses sendFinishedSignal () of InputConsumer to send the processing end signal.

In fact, when the Input resample mechanism is enabled (the system property ro_input.noresample is controlled), processing of Motion Events in InputConsumer is more complicated. Android adds a re-sample of the Motion event to InputConsumer. VSync indicates that the Display has completed the Display of a frame. The system needs to prepare the next frame, that is, we need to know the coordinates of the VSync signal when it comes to the touch. The problem is that the input device events are not based on VSync, for example, 10 ms is the cycle (VSync is 16.67 ms ). So how can we know the input coordinates of the VSync time only rely on estimation. How to estimate it depends on the estimation method combining sampling with inner and outer interpolation. See http://www.masonchang.com/blog/2014/8/25/androids-touch-resampling-algorithm for details.

As mentioned above, InputDispatcher not only transmits events to the App, but also processes system buttons. There are some special buttons in the system that need to be processed by the system, such as volume and power keys. It is intercepted by interceptKeyBeforeDispatching () and interceptKeyBeforeQueueing () functions. InterceptKeyBeforeDispatching () is mainly used to process home, menu, search, and interceptkeyBeforeQueueing (). It is mainly used to process volume and power keys. The implementation of these processes is in PWM, And the caller is in InputDispatcherThread. The advantage is that all platform-related things are placed in PWM, while InputDispatcher is a general platform things. To customize policies, you only need to modify PWM. In this way, the Mechanism and Policy are separated. Then, how does InputDisaptcher call PWM? First, the code in InputDispatcher contains several hooks:

3510     nsecs_t delay = mPolicy->interceptKeyBeforeDispatching(commandEntry->inputWindowHandle,3511             &event, entry->policyFlags);
The mPolicy here is actually NativeInputManager. NativeInputManager calls the corresponding IMS functions in the Java World through JNI:
1463     // Native callback.1464     private long interceptKeyBeforeDispatching(InputWindowHandle focus,1465             KeyEvent event, int policyFlags) {1466         return mWindowManagerCallbacks.interceptKeyBeforeDispatching(focus, event,      policyFlags);1467     }
Here mWindowManagerCallbacks is actually InputMonitor. Then the PWM is called.
380     public long interceptKeyBeforeDispatching(381             InputWindowHandle focus, KeyEvent event, int policyFlags) {382         WindowState windowState = focus != null ? (WindowState) focus.windowState :     null;383         return mService.mPolicy.interceptKeyBeforeDispatching(windowState, event,       policyFlags);384     }
Now, let's make a summary. The input event journey from Kernel to App and PWM is roughly shown in:


It can be seen that the input_event structure is read from the Input driver. After passing through the intermediate module, it will be encapsulated as needed and converted into or encapsulated into RawEvent, KeyEntry, DispatchEntry, InputMessage, inputEvent, QueuedInputEvent, and other structures. Some of these structures are encapsulation of other structures, such as DispatchEntry encapsulates KeyEntry and QueuedInputEvent encapsulates InputEvent.

Back to the main line, the story is not finished. As mentioned above, the App processes input events and sends a processing completion notification through InputChannel, the communication channel of InputDispacher in IMS. What should I do if I receive the InputDispatcher?


InputDispatcher calls handleReceiveCallback () to process the complete signal. Here we first put a processing transaction to the Command queue to execute doDispatchCycleFinishedLockedInterruptible (), which will be extracted and executed later in runCommandsLockedInterruptible. In the doDispatchCycleFinishedLockedInterruptible () function, afterKeyEventLockedInterruptible () is called first (). Some Fallback keys can be defined in Android, that is, if a Key event App is not processed, Fallback can be another default Key event, which is processed in the dispatchUnhandledKey () function here. Then InputDispatcher removes the event item that receives the completion signal from the waiting queue. At the same time, because the previous event has been processed by the App, you can call startDispatchCycleLocked () to process the next round of events.

3558        // Dequeue the event and start the next cycle.3559        // Note that because the lock might have been released, it is possible that the3560        // contents of the wait queue to have been drained, so we need to double-check3561        // a few things.3562        if (dispatchEntry == connection->findWaitQueueEntry(seq)) {3563            connection->waitQueue.dequeue(dispatchEntry);...3571        }35723573        // Start the next dispatch cycle for this connection.3574        startDispatchCycleLocked(now(), connection);
The startdispatchreceivelocked function checks whether there are events to be sent in the connection output buffer (connection-> outboundQueue) of the corresponding connection. If yes, it will be sent through InputChannel.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.