This document discusses how an input event from user (e.g. user key stroke) is propagated in Android framework, and how it is delivered to the corresponding event handler in the application.
This document is based on the code of Android 4.1.1_r6.1 (Jelly Bean).
For each application, a ViewRootImpl
object is created to handle communications with the remote system WindowManagerService
object. The communication is through a Linux pipe which is encapsulated in an InputChannel
object (mInputChannel
field in class ViewRootImpl
). The ViewRootImpl
object also registers an instance of InputEventReceiver
when the first View
object is registered with it.
frameworks/base/core/java/android/view/ViewRootImpl.java +482
482 public void setView(View view, ...) {
...
626 mInputEventReceiver = new WindowInputEventReceiver(mInputChannel, Looper.myLooper());
...
644 }
The constructor of class WindowInputEventReceiver
(class WindowManagerService
extends from class InputEventReceiver
) calls a native methond nativeInit(...)
:
frameworks/base/core/java/android/view/InputEventReceiver.java +58
58 public InputEventReceiver(InputChannel inputChannel, Looper looper) {
...
66 mInputChannel = inputChannel;
67 mMessageQueue = looper.getQueue();
68 mReceiverPtr = nativeInit(this, inputChannel, mMessageQueue);
...
71 }
Three parameters are passed to the native function nativeInit
: 1) The receiver object itself; 2) The InputChannel
object passed from the ViewRootImpl
object. 3) The main message queue (an object of class MessageQueue
) of the application.
The native function nativeInit
is shown as follows:
frameworks/base/core/jni/android_view_InputEventReceiver.cpp +227
227 static jint nativeInit(JNIEnv* env, jclass clazz, jobject receiverObj,
228 jobject inputChannelObj, jobject messageQueueObj) {
229 sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
230 inputChannelObj);
...
236 sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
...
242 sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,
243 receiverObj, inputChannel, messageQueue);
244 status_t status = receiver->initialize();
...
254 }
The function first gets a pointer to a NativeInputChannel
object (line 229) and a pointer to a NativeMessageQueue
object (line 236), then creates and initializes a NativeInputEventReceiver
object (line 242-244) using these two pointers and the receiver object itself. In the initialize()
function of class NativeInputEventReceiver
, the file descriptor of the Linux pipe (which is passed from ViewRootImpl
in the object of InputChannel
) is sent to the looper for polling. When the file descriptor becomes readable which means the system WindowManagerService
object has detected an input event from the kernel, a callback function will be invoked.
frameworks/base/core/jni/android_view_InputEventReceiver.cpp +92
92 status_t NativeInputEventReceiver::initialize() {
93 int receiveFd = mInputConsumer.getChannel()->getFd();
94 mMessageQueue->getLooper()->addFd(receiveFd, 0, ALOOPER_EVENT_INPUT, this, NULL);
95 return OK;
96 }
The callback function is the handleEvent(...)
function in the same class NativeInputEventReceiver
:
frameworks/base/core/jni/android_view_InputEventReceiver.cpp +119
119 int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) {
...
132 JNIEnv* env = AndroidRuntime::getJNIEnv();
133 status_t status = consumeEvents(env, false /*consumeBatches*/, -1);
134 mMessageQueue->raiseAndClearException(env, "handleReceiveCallback");
135 return status == OK || status == NO_MEMORY ? 1 : 0;
136 }
138 status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
139 bool consumeBatches, nsecs_t frameTime) {
...
187 inputEventObj = android_view_KeyEvent_fromNative(env,
188 static_cast<KeyEvent*>(inputEvent));
...
208 env->CallVoidMethod(mReceiverObjGlobal,
209 gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);
...
224 }
As shown above, function handleEvent(...)
invokes a Java function InputEventReceiver.dispatchInputEvent(...)
(line 208), which will eventually invoke onInputEvent(...)
in class WindowInputEventReceiver
:
frameworks/base/core/java/android/view/ViewRootImpl.java +4223
4223 public void onInputEvent(InputEvent event) {
4224 enqueueInputEvent(event, this, 0, true);
4225 }
This function will eventually call doProcessInputEvents()
(line 4147) which will then call deliverInputEvent(...)
(line 3109) which will then call deliverKeyEvent(...)
(line 3495). Finally, the event will be dispatch to the View
object by calling mView.dispatchKeyEvent(event)
(line 3575).
Now, the question is how the system WindowManagerService
object detects events from the kernel? It is actually quite simple, it polls several device files (e.g. /dev/input/eventX) exposed by the kernel. For more details, refer to this blog.
Hi - the link at the very bottom has been moved to http://seasonofcode.com/posts/internal-input-event-handling-in-the-linux-kernel-and-the-android-userspace.html . Thanks for linking to my blog :)