Skip to content

Instantly share code, notes, and snippets.

@jieyu
Created December 11, 2012 21:40
Show Gist options
  • Save jieyu/4262418 to your computer and use it in GitHub Desktop.
Save jieyu/4262418 to your computer and use it in GitHub Desktop.
How a user input event is propagate in the Android framework and how it is delivered to the corresponding event handler.

Android Input Event Propagation

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.

@jichu4n
Copy link

jichu4n commented Mar 1, 2014

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 :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment