Document directory
- 1.2.1 register with nativeinputqueue
- 1.2.2 register with inputmanager
- 2.1.1 yyswitch ()
- 2.1.2 policykey ()
- 2.1.3 policymotion ()
- 2.2.1 basic process of inputdispatcherthread
- 2.2.3 discard event
Because Android is a Linux kernel, event processing is also completed on the basis of Linux. Therefore, we will sort out the processing process from the Linux kernel to the application.
The Linux Kernel provides an input subsystem for implementation. The input subsystem creates a node for our hardware input device in the/dev/input/path, in general, these nodes are named after eventxx in our cell phone, such as event0 and event1. But if it is a virtual machine, we can see a mice, which indicates the mouse device, this is because the PC needs to use the mouse to simulate the touch screen. Because these device nodes are hardware-related, each device is different. When we see the input device nodes, we may be confused about the meaning of these eventxx, that is, what kind of device has created this node? We can read hardware related to eventxx from/proc/bus/input/devices. I will not talk about it here, we only need to know that android reads the event information from the device nodes in the/dev/input/directory, which is the origin of Android event processing, this allows you to know where events such as buttons and touch screens come from, rather than our focus.
First, let's briefly introduce the process of Android event transfer. Events such as buttons and touch screens are obtained through windowmanagerservice and transmitted to viewroot through shared memory and pipelines, viewroot then dispatch the view to the application. When an event is input from a hardware device, system_server notifies viewroot of the event in the pipeline (PIPE) when detecting the event. In this case, viewroot reads the event information from the memory.
As for why Android uses the shared memory instead of the Binder Mechanism for event processing, I guess Google is trying to ensure real-time event response. Therefore, when selecting the method for inter-process event transfer, the mode of high shared memory is selected. Because there is a data management process in the shared memory that basically does not involve data copying in the memory, only two data copies are involved during process read/write, this is an inevitable data copy. Therefore, this method can ensure the system's response to the event, but it is not enough to share the memory, because the communication mode of shared memory does not notify the other party of data updates, Android adds another inter-process communication channel (PIPE) to the event processing process ), pipeline is not as efficient as shared memory, will it affect the real-time processing of events? It doesn't matter. Every time system_serve notifies viewroot that only one character is passed to it, that is, it is lightweight and simple, and the data of one character is copied multiple times. I think Google can still accept it.
Okay. After learning some basic knowledge, we will analyze the event transfer process from the bottom layer to the upper layer. Here, we will first list the structure of the entire event processing for ease of understanding below.
1. Initialization Process of the Event Processing System
As mentioned in the previous article, the Android event processing system, which is called the event transfer system, is more appropriate, because the Android event system is complicated in its transfer process, next we will replace the event processing system with the event passing system. The Android event transfer system implements the transfer by means of communication between the shared memory and pipeline processes. In order to facilitate understanding of its transfer mechanism, the initialization of the event transfer system is very important.
1.1 create an MPS queue connection
The main function of the MPs queue in the event transfer system is to notify viewroot to read the event when the event is stored in the shared memory. Since viewroot and system_server establish pipeline communication, viewroot and windowmanagerservice (responsible for event transmission and running in the system_server process) each need to maintain a file descriptor of the pipeline, in fact, viewroot and windowmanagerservice do not maintain the file descriptor of one pipeline, but two. Of course, these two descriptors do not belong to the same pipeline, in fact, full-duplex pipe communication is implemented between viewroot and windowmanagerservice.
Windowmanagerservice ---> pipe communication in the viewroot direction, indicating that WMS notifies viewroot that a new event is written to the shared memory;
Viewroot --> the MPs queue in the windowmanagerservice direction indicates that viewroot has digested new events in the shared memory. We hereby notify WMS.
The file descriptors of viewroot and windowmanagerservice pipelines are stored in an inputchannel class, which is the carrier of pipeline communication.
First, let's take a look at the establishment of the viewroot pipe.
Setview () @ viewroot. Java
requestLayout(); mInputChannel = new InputChannel(); try { res = sWindowSession.add(mWindow, mWindowAttributes, getHostVisibility(), mAttachInfo.mContentInsets, mInputChannel); } catch (RemoteException e) {
Before viewroot and WMS (windowmanagerservice) establish a connection, an inputchannel object is created, and an inputchannel object is created on the same WMS end, however, the creation process of WMS is called when viewroot calls the add () method. The construction of inputchannel is not performed. Therefore, inputchannel creation in viewroot is not initialized. the initialization process is performed when the WMS Method Add () is called, the above code passes the minputchannel as a parameter to WMS for initialization. Next, go to the WMS code to check the inputchannel initialization process.
Addwindow () @ windowmanagerservice. Java
if (outInputChannel != null) { String name = win.makeInputChannelName(); InputChannel[] inputChannels = InputChannel.openInputChannelPair(name); win.mInputChannel = inputChannels[0]; inputChannels[1].transferToBinderOutParameter(outInputChannel); mInputManager.registerInputChannel(win.mInputChannel); }
Outinputchannel is the inputchannel object passed by viewroot. the main work of the above Code is to create a pair of inputchannels, which implement a set of full-duplex pipelines. When creating an inputchannel pair, the system will apply for shared memory and save a file descriptor for shared memory to the two inputchannel objects. After inputchannel is created, an native inputchannel is assigned to outinputchannel, that is, initialization of the inputchannel object on the viewroot end. As a result, the inputchannel object on both sides of viewroot and WMS is created, the pipeline communication of the event transmission system is also established.
When creating an inputchannel pair and creating a MPs queue, the shared memory application process will no longer list its Code. For details, see openinputchannelpair () @ inputtransport. cpp. Create an inputchannel pair structure for both viewroot and WMS.
1.2 inputchannel registration process
The previous section introduced the inputchannel object creation process, which establishes the pipeline communication, but we need to be clear that one pipeline communication only corresponds to the event processing of one activity, that is, the number of full-duplex pipelines for each activity in the current system. Therefore, the system requires a manager to manage and schedule communication between each pipeline. Therefore, after creating an inputchannel object, you need to register it with this manager.
After understanding the reason why the inputchannel object needs to be registered, let's look at where the inputchannel object on viewroot and WMS needs to be registered? In fact, It is also easy to understand that the two inputchannel objects WMS are the sender for pipeline communication and the viewroot side is the receiver ER (although the full duplex is created, it is only used for its consistent communication at present, communication in the other direction is not yet used), so two inputchannel objects must be managed by two different managers. Generally, viewroot registers a nativeinputqueue object (this is a native object, and the inputqueue class of Java only provides some static methods to communicate with nativeinputqueue ), as long as nativeactivity is used, it will be another processing mechanism. Here we don't care about it, nativeactivity is rarely used after all; WMS is registered in the inputmanager object. In fact, the nativeinputqueue and inputmanager names will be able to understand their respective functions.
1.2.1 register with nativeinputqueue
When the inputchannel object of viewroot registers with nativeinputqueue, three parameters must be registered:
1. Pass the native inputchannel corresponding to the inputchannel object to nativeinputqueue;
2. Pass inputhandler, the member variable of viewroot, to nativeinputqueue. This inputhandler is the event processing function. It is mainly used to clarify the event processing function of viewroot;
3. Another important parameter needs to be passed to nativeinputqueue, which is the messagequeue of the main process of the current application.
In fact, Android uses the polling (poll) Mechanism of thread logoff and messagequeue in implementing event transmission, the polling mechanism is used to check whether a Message notification event occurs in the MPs queue. The logoff mechanism can be used to ensure that the event is immediately known by the application. logoff will analyze it separately.
During the registration process, Android will hand over the file descriptor of the pipeline saved in the inputchannel object to the native loue of messagequeue for listening, and instruct the native loue a callback function. Once an event occurs, native logoff detects the data in the pipeline and calls the indicated callback function. This callback function is handlereceivecallback () @ android_view_inputqueue.cpp.
Of course, there is only one nativeinputqueue object in the system. To manage the event transfer of so many applications, Android defines a subclass connection in the nativeinputqueue class, each inputchannel object creates its own connection object during registration.
The code for this block is in registerinputchannel () @ android_view_inputqueue.cpp
1.2.2 register with inputmanager
Because WMS detects the Linux Input System and viewroot detects the receiver of the pipeline differently, the viewroot end uses the logoff polling mechanism of the application main thread to achieve real-time event response. WMS also has its own logoff, WMS does not reuse its logoff mechanism like viewroot. As for the reason why Android Code does not clearly indicate, my guess is that WMS is the entire system, unlike viewroot, each activity has one set. In order not to affect the overall performance of the system, try not to affect WMS.
Without logoff, inputmanager starts two processes to manage event occurrence and transmission. inputreaderthread and inputdispatcherthread, inputreaderthread, inputreaderthread, and inputdispatcherthread are responsible for event polling. Why do two processes need to be managed? Obviously, if you use one program, the time interval between polling the event in the input system will become longer, and the event may be lost.
Although logoff is not used to poll events, inputdispatcher uses native logoff to poll and check the pipeline communication. This pipeline communication indicates whether inputqueue has digested past events of dispatch. Note that this native logoff is not a WMS thread, but is defined by the thread inputdispatcher. Therefore, inputdispatcher must actively call all polling processes, as shown in figure
Mloce-> pollonce (timeoutmillis); or mloce-> wake ();. Unlike nativeinputqueue, there is no need to worry about logoff operations.
WMS will create such an inputmanager instance during initialization. Of course, it is also the only one in the system. The inputmanager instance at the Java layer does not implement too many businesses. The nativeinputmanager instance is actually a native nativeinputmanager instance. When it is created, establishes the static logic of the entire WMS-side event transfer system, such:
The core of nativeinputmanager's entire business is inputreader and inputdispatcher. The following describes the two modules.
A. inputreader
From the name, inputreader can see that the main task is a read event. Basically, all its services are included in the process () function,
void InputReader::process(const RawEvent* rawEvent) { switch (rawEvent->type) { case EventHubInterface::DEVICE_ADDED: addDevice(rawEvent->deviceId); break; case EventHubInterface::DEVICE_REMOVED: removeDevice(rawEvent->deviceId); break; case EventHubInterface::FINISHED_DEVICE_SCAN: handleConfigurationChanged(rawEvent->when); break; default: consumeEvent(rawEvent); break; }}
Provided by the eventhub module when the process () function inputs parameters,
1. when the eventxx device of the input system is not enabled for eventhub, when inputreader gets an event from eventhub, eventhub first opens all the devices and returns the information of each device to inputreader in the form of a rawevent, that is, the eventhubinterface: device_added type processed in process (). This process creates an inputdevice Based on the DeviceID of each device and creates an inputmapper Based on the classes of the device. As shown in.
2. When all devices are turned on and inputreader returns to eventhub to obtain the event, eventhub returns to the polling event node. If there is an event, inputreader will digest the event consumeevent (rawevent );
B. inputdispatcher
The core business of data transmission management is completed in inputdispatcher. Therefore, the inputchannel object on the WMS end will be registered in inputdispatcher. Similarly, because there is only one inputdispatcher instance in the system, the inputchannel object on the WMS end corresponds to viewroot one by one. Therefore, the inputdispatcher class also defines an internal class connect to manage their inputchannel objects. Unlike the connect class in the nativeinputqueue class, the core business of the Connect class in inputdispatcher is implemented by the inputpublisher object, which writes event information to the shared memory.
The related code is in registerinputchannel () @ inputdispatcher. cpp
2. Event Transfer
After analyzing the initialization process of the event processing system, we have a certain understanding of the overall architecture of the event processing system, so the following event transmission process will be very easy.
2.1 inputreaderthread operations
When an event occurs in the input system, it is poll by the inputreaderthread. inputreader selects inputdevice Based on the device ID of the event, and then selects inputmapper in inputdevice Based on the event type, inputmapper notifies inputdispatcher of the event information;
Currently, adroid implements inputmapper for five device types in inputreader, including slide/flip switchinputmapper, keyboard seek, trackballinputmapper, multitouch screen multitouchinputmapper, and singletouchinputmapper.
Device Type |
Inputmanager |
Eventtype |
Notify inputdispatcher |
Slide/flip |
Switchinputmapper |
Ev_sw |
Yyswitch () |
Keyboard |
Keyboardinputmapper |
Ev_key |
Notifykey () |
Trackball |
Trackballinputmapper |
Ev_key, ev_rel, Ev_syn |
Notifymotion () |
Single Touch Screen |
Singletouchinputmapper |
Ev_key, ev_abs, Ev_syn |
Notifymotion () |
Multi-Point Touch Screen |
Multitouchinputmapper |
Ev_abs, Ev_syn |
Notifymotion () |
Ev_rel is the relative coordinate of the event, ev_abs is the absolute coordinate, and ev_syn indicates that a series of motion actions are completed.
Notify inputdispatcher indicates the function call of inputdispatcher for different event notifications. Although these functions are called by inputreaderthread, they are defined by inputdispatcher.
2.1.1 yyswitch ()
void InputDispatcher::notifySwitch(nsecs_t when, int32_t switchCode, int32_t switchValue, uint32_t policyFlags) {#if DEBUG_INBOUND_EVENT_DETAILS LOGD("notifySwitch - switchCode=%d, switchValue=%d, policyFlags=0x%x", switchCode, switchValue, policyFlags);#endif policyFlags |= POLICY_FLAG_TRUSTED; mPolicy->notifySwitch(when, switchCode, switchValue, policyFlags);}
Switch event processing is relatively simple. This is an activity-independent event, so we do not need to dispatch it to viewroot, so in policyswitch () you can directly send a notification to phonewindowmanager for processing. From the above class diagram, we can see that mpolicy points to nativeinputmanager,
void NativeInputManager::notifySwitch(nsecs_t when, int32_t switchCode, int32_t switchValue, uint32_t policyFlags) {#if DEBUG_INPUT_DISPATCHER_POLICY LOGD("notifySwitch - when=%lld, switchCode=%d, switchValue=%d, policyFlags=0x%x", when, switchCode, switchValue, policyFlags);#endif JNIEnv* env = jniEnv(); switch (switchCode) { case SW_LID: env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.notifyLidSwitchChanged, when, switchValue == 0); checkAndClearExceptionFromCallback(env, "notifyLidSwitchChanged"); break; }}
Nativeinputmanager's yyswitch () will eventually be called to yyswitch () @ phonewindowmanager. Java
2.1.2 policykey ()
void InputDispatcher::notifyKey(nsecs_t eventTime, int32_t deviceId, int32_t source, uint32_t policyFlags, int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState, nsecs_t downTime) {#if DEBUG_INBOUND_EVENT_DETAILS LOGD("notifyKey - eventTime=%lld, deviceId=0x%x, source=0x%x, policyFlags=0x%x, action=0x%x, " "flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, downTime=%lld", eventTime, deviceId, source, policyFlags, action, flags, keyCode, scanCode, metaState, downTime);#endif if (! validateKeyEvent(action)) { return; } policyFlags |= POLICY_FLAG_TRUSTED; mPolicy->interceptKeyBeforeQueueing(eventTime, deviceId, action, /*byref*/ flags, keyCode, scanCode, /*byref*/ policyFlags); bool needWake; { // acquire lock AutoMutex _l(mLock); int32_t repeatCount = 0; KeyEntry* newEntry = mAllocator.obtainKeyEntry(eventTime, deviceId, source, policyFlags, action, flags, keyCode, scanCode, metaState, repeatCount, downTime); needWake = enqueueInboundEventLocked(newEntry); } // release lock if (needWake) { mLooper->wake(); }}
Inputdispatcher handles Keyboard Events as described in the Code above,
First, inputdispatcher intercepts the key event and preferentially digest the event based on the current device status. This process is of course before the event dispatch is sent to viewroot. Like policyswitch (), the process is finally handled by interceptkeybeforequeueing () @ phonewindowmanager. java. Interceptkeybeforequeueing () is mainly used for special handling of some special cases, and judges that this key is sufficient to be passed to viewroot. Set the flag value of policyflags to determine whether to give viewroot, for example, policyflags & policy_flag_pass_to_user.
= 1 should be passed to viewroot. Interceptkeybeforequeueing () special processing is mainly for receiving special key values, such as the volume key or the wake key, when the screen is locked or the screen is not bright. A wake key is an operation that lights up the screen. Next, inputdispatcher stores the key information in a queue (enqueueinboundeventlocked () @ inputdispatcher. cpp ).
Queue<EventEntry> mInboundQueue;
2.1.3 policymotion ()
mPolicy->interceptGenericBeforeQueueing(eventTime, /*byref*/ policyFlags);
First, inputdispatcher intercepts the motion event. The difference is that nativeinputmanager is fully capable of capturing and processing the motion event, so it is not handed over to phonewindowmanager for processing. View the code interceptgenericbeforequeueing () @ com_android_server_inputmanager.cpp.
Next, inputdispatcher stores the motion event information in the minboundqueue Queue (enqueueinboundeventlocked () @ inputdispatcher. cpp ).
2.2 inputdispatcherthread operations
The inputdispatcherthread thread's polling process is dispatchonce () --> dispatchonceinnerlocked (). The inputdispatcherthread thread continuously executes this operation to achieve the purpose of polling, our research focuses on the processing of these two functions.
2.2.1 basic process of inputdispatcherthread
The main operations of inputdispatcherthread are performed in two parts at the same time,
Part of the process is to pre-process the events passed by inputreader by dispatch, such as determining the focus window and processing special Buttons such as home/endcall. After preprocessing, inputdispatcher stores the event to the outboundqueue of the corresponding focus window. The outboundqueue queue is a member function of inputdispatcher: connection, so it is related to viewroot.
In part, it is round-robin For logoff. This round-robin process is to check whether nativeinputqueue has handled the previous event. If nativeinputqueue has handled the event, it will send a message to inputdispatcher to indicate that consume has completed, only nativeinputqueue consume completes an event. inputdispatcher writes another event to the shared memory.
2.2.3 discard event
Not all events sent by inputreader need to be passed to the application, such as the flip/slide events mentioned in the previous section, other buttons, touch screens, trackball (the latter two are uniformly handled by the motion event), some events will also be discarded, and inputdispatcher will always discard some events according to some rules, in which of the following situations do we need to discard some events?
Inputdispatcher. h defines an enumeration that contains the reason for discarding:
enum DropReason { DROP_REASON_NOT_DROPPED = 0, DROP_REASON_POLICY = 1, DROP_REASON_APP_SWITCH = 2, DROP_REASON_DISABLED = 3, };
1. drop_reason_not_dropped
No need to discard
2. drop_reason_policy
When set to drop_reason_policy, there are two main scenarios:
A. Before inputreader implements y inputdispatcher, the policy determines the events that do not need to be passed to the application. As described in the previous section.
B. Before the inputdispatcher dispatch event, phonewindowmanager uses interceptkeybeforedispatching () to remove some key events in advance, as shown in the preceding flowchart.
Interceptkeybeforedispatching () is mainly used for special processing of the home/menu/search buttons. If the buttons can be deleted by consume, they will be discarded in inputdispatcher.
3. drop_reason_app_switch
When an app switch key occurs, such as the home/endcall key, when inputreader transmits the app switch key to inputdispatcher, an app_switch_timeout 0.5s timeout is set, when the inputdispatcher has not been installed with the app switch button, inputdispatcher will discard all the key events in the minboundqueue before the app switch button. The purpose of this operation is to ensure that the app switch button can be processed. The discarded keys are set to drop_reason_app_switch.
4. drop_reason_disabled
This flag indicates that the current inputdispatcher is disable and cannot dispatch any events, for example, used when the system is sleeping or is shutting down.