分析Android 的Input Event 子系统的来龙去脉。 Android 系统里面有很多小工具,运行这些工具,我们对它们有一个感性的认识,进而阅读和分析这些小工具源代码,再顺藤摸瓜,就可以把整个子系统的来龙去脉弄清楚。 1.运行toolbox的getevent 工具。 # getevent -help getevent -help Usage: getevent [-t] [-n] [-s switchmask] [-S] [-v [mask]] [-p] [-q] [-c count] [-r] [device] -t: show time stamps -n: don't print newlines -s: print switch states for given bits -S: print all switch states -v: verbosity mask (errs=1, dev=2, name=4, info=8, vers=16, pos. events=32) -p: show possible events (errs, dev, name, pos. events) -q: quiet (clear verbosity mask) -c: print given number of events then exit -r: print rate events are received # getevent -c 20 getevent -c 20 add device 1: /dev/input/event4 name: "sensor-input" add device 2: /dev/input/event3 name: "88pm860x_hook" add device 3: /dev/input/event2 name: "88pm860x_on" add device 4: /dev/input/event1 name: "88pm860x-touch" add device 5: /dev/input/event0 name: "pxa27x-keypad" /dev/input/event0: 0001 0066 00000001 /dev/input/event0: 0000 0000 00000000 /dev/input/event0: 0001 0066 00000000 /dev/input/event0: 0000 0000 00000000 /dev/input/event1: 0003 0000 00000c48 /dev/input/event1: 0003 0001 00000751 /dev/input/event1: 0001 014a 00000001 /dev/input/event1: 0000 0000 00000000 /dev/input/event1: 0003 0000 00000c67 /dev/input/event1: 0003 0001 000006f9 /dev/input/event1: 0000 0000 00000000 /dev/input/event1: 0003 0000 00000c9e /dev/input/event1: 0003 0001 0000069e /dev/input/event1: 0000 0000 00000000 /dev/input/event1: 0003 0000 00000cc4 /dev/input/event1: 0003 0001 00000620 /dev/input/event1: 0000 0000 00000000 /dev/input/event1: 0003 0000 00000ce8 /dev/input/event1: 0003 0001 000005ba /dev/input/event1: 0000 0000 00000000 运行这个工具,然后按键或者滑动触摸屏,会看到程序会实时打印event。从上面的输出来看,系统有5个input 子系统。它们分别是 add device 1: /dev/input/event4 name: "sensor-input" #Sensor input 子系统 add device 2: /dev/input/event3 name: "88pm860x_hook" #耳机Hook键子系统。可支持接电话挂电话的耳机上面有一个按键,对应的就是这个input 子系统。 add device 3: /dev/input/event2 name: "88pm860x_on" #开机键 input 子系统 add device 4: /dev/input/event1 name: "88pm860x-touch" #Touch Screen input 子系统 add device 5: /dev/input/event0 name: "pxa27x-keypad" #按键子系统,包括Home/Menu/Back等按键。 可以尝试多种event,实际感觉一下出来的log。 2.阅读getevent的代码。代码为./core/toolbox/getevent.c 从代码中,我们知道,程序在while(1)的一个死循环里,不断地在读取 (select 操作)/dev/input 下面的文件,检查是否Kernel往里面更新内容,如果有内容更新,就把它打印出来。并且从代码中,我们还知道,任何一个event都有三种属性,type,code,value. while(1) { pollres = poll(ufds, nfds, -1); //printf("poll %d, returned %d/n", nfds, pollres); if(ufds[0].revents & POLLIN) { read_notify(device_path, ufds[0].fd, print_flags); } for(i = 1; i < nfds; i++) { if(ufds[i].revents) { if(ufds[i].revents & POLLIN) { res = read(ufds[i].fd, &event, sizeof(event)); if(res < (int)sizeof(event)) { fprintf(stderr, "could not get event/n"); return 1; } if(get_time) { printf("%ld-%ld: ", event.time.tv_sec, event.time.tv_usec); } if(print_device) printf("%s: ", device_names[i]); printf("%04x %04x %08x", event.type, event.code, event.value); if(sync_rate && event.type == 0 && event.code == 0) { int64_t now = event.time.tv_sec * 1000000LL + event.time.tv_usec; if(last_sync_time) printf(" rate %lld", 1000000LL / (now - last_sync_time)); last_sync_time = now; } printf("%s", newline); if(event_count && --event_count == 0) return 0; } } } 3.问题来了,Android Framework是否也是一样的原理呢??猜测应该是一样的才对,不然这个工具就没有调试的价值了。 我们来阅读和分析framework中input event的相关代码。 我们从Kernel层往上看,先看看Framework中,直接操纵/dev/input设备的代码。 在.frameworks/base/libs/ui/EventHub.cpp 中,我们看到跟getevent工具类似的代码。 bool EventHub::getEvent(int32_t* outDeviceId, int32_t* outType, int32_t* outScancode, int32_t* outKeycode, uint32_t *outFlags, int32_t* outValue, nsecs_t* outWhen) { .... while(1) { .... release_wake_lock(WAKE_LOCK_ID);
pollres = poll(mFDs, mFDCount, -1);
acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
if (pollres <= 0) { if (errno != EINTR) { LOGW("select failed (errno=%d)/n", errno); usleep(100000); } continue; } .... // mFDs[0] is used for inotify, so process regular events starting at mFDs[1] for(i = 1; i < mFDCount; i++) { if(mFDs[i].revents) { LOGV("revents for %d = 0x%08x", i, mFDs[i].revents); if(mFDs[i].revents & POLLIN) { res = read(mFDs[i].fd, &iev, sizeof(iev)); if (res == sizeof(iev)) { LOGV("%s got: t0=%d, t1=%d, type=%d, code=%d, v=%d", mDevices[i]->path.string(), .... } 4.那么framework中那个模块再调用EventHub呢,接着往下查。 在framework目录中,输入下面的命令查找 # find . -name "*.cpp" |grep -v EventHub | xargs grep EventHub ./base/services/jni/com_android_server_KeyInputQueue.cpp:#include ./base/services/jni/com_android_server_KeyInputQueue.cpp:static sp gHub; ./base/services/jni/com_android_server_KeyInputQueue.cpp: sp hub = gHub; ./base/services/jni/com_android_server_KeyInputQueue.cpp: hub = new EventHub; ./base/services/jni/com_android_server_KeyInputQueue.cpp: sp hub = gHub; ./base/services/jni/com_android_server_KeyInputQueue.cpp: hub = new EventHub; 5.从查找结果中得知,在jni文件com_android_server_KeyInputQueue.cpp文件中有对EventHub进行调用。 打开并阅读com_android_server_KeyInputQueue.cpp文件得知,在下面的函数中调用了EventHub的getEvent函数 static jboolean android_server_KeyInputQueue_readEvent(JNIEnv* env, jobject clazz, jobject event) { gLock.lock(); sp hub = gHub; if (hub == NULL) { hub = new EventHub; gHub = hub; } gLock.unlock();
int32_t deviceId; int32_t type; int32_t scancode, keycode; uint32_t flags; int32_t value; nsecs_t when; bool res = hub->getEvent(&deviceId, &type, &scancode, &keycode, &flags, &value, &when);
env->SetIntField(event, gInputOffsets.mDeviceId, (jint)deviceId); env->SetIntField(event, gInputOffsets.mType, (jint)type); env->SetIntField(event, gInputOffsets.mScancode, (jint)scancode); env->SetIntField(event, gInputOffsets.mKeycode, (jint)keycode); env->SetIntField(event, gInputOffsets.mFlags, (jint)flags); env->SetIntField(event, gInputOffsets.mValue, value); env->SetLongField(event, gInputOffsets.mWhen, (jlong)(nanoseconds_to_milliseconds(when)));
return res; } 6.根据jni的调用规则,在本文件中查找对于的java函数。 static JNINativeMethod gInputMethods[] = { /* name, signature, funcPtr */ { "readEvent", "(Landroid/view/RawInputEvent;)Z", (void*) android_server_KeyInputQueue_readEvent }, .... 7. 接着顺藤摸瓜,找到对应的java文件,base/services/java/com/android/server/KeyInputQueue.java private static native boolean readEvent(RawInputEvent outEvent); 在一个线程中会调用readEvent函数。 Thread mThread = new Thread("InputDeviceReader") { public void run() { if (DEBUG) Slog.v(TAG, "InputDeviceReader.run()"); android.os.Process.setThreadPriority( android.os.Process.THREAD_PRIORITY_URGENT_DISPLAY);
RawInputEvent ev = new RawInputEvent(); while (true) { try { InputDevice di;
// block, doesn't release the monitor readEvent(ev);
boolean send = false; boolean configChanged = false;
if (false) { Slog.i(TAG, "Input event: dev=0x" + Integer.toHexString(ev.deviceId) + " type=0x" + Integer.toHexString(ev.type) + " scancode=" + ev.scancode + " keycode=" + ev.keycode + " value=" + ev.value); } 8.那是谁启动这个线程呢???查找mThread变量,得知在KeyInputQueue的构造函数中会启动这个线程。 KeyInputQueue(Context context, HapticFeedbackCallback hapticFeedbackCallback) { if (MEASURE_LATENCY) { lt = new LatencyTimer(100, 1000); }
Resources r = context.getResources(); BAD_TOUCH_HACK = r.getBoolean(com.android.internal.R.bool.config_filterTouchEvents);
JUMPY_TOUCH_HACK = r.getBoolean(com.android.internal.R.bool.config_filterJumpyTouchEvents);
mHapticFeedbackCallback = hapticFeedbackCallback;
readExcludedDevices();
PowerManager pm = (PowerManager)context.getSystemService( Context.POWER_SERVICE); mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "KeyInputQueue"); mWakeLock.setReferenceCounted(false);
mFirst = new QueuedEvent(); mLast = new QueuedEvent(); mFirst.next = mLast; mThread.start(); } 9.那这个KeyInputQueue是在哪里被实例化呢? 而且查看KeyInputQueue类的声明,得知它是一个abstract class. public abstract class KeyInputQueue { ..... } 说明它肯定会被某个类继承.接着查找。 /frameworks$ find . -name "*.java" |grep -v KeyInputQueue | xargs grep KeyInputQueue ./policies/base/phone/com/android/internal/policy/impl/KeyguardViewMediator.java: * {@link com.android.server.KeyInputQueue}'s and {@link android.view.WindowManager}'s. ./base/services/java/com/android/server/PowerManagerService.java: && !"KeyInputQueue".equals(tag))) { ./base/services/java/com/android/server/WindowManagerService.java:import com.android.server.KeyInputQueue.QueuedEvent; ./base/services/java/com/android/server/WindowManagerService.java: implements Watchdog.Monitor, KeyInputQueue.HapticFeedbackCallback { ./base/services/java/com/android/server/WindowManagerService.java: return KeyInputQueue.getSwitchState(sw); ./base/services/java/com/android/server/WindowManagerService.java: return KeyInputQueue.getSwitchState(devid, sw); ./base/services/java/com/android/server/WindowManagerService.java: return KeyInputQueue.hasKeys(keycodes, keyExists); ./base/services/java/com/android/server/WindowManagerService.java: private class KeyQ extends KeyInputQueue ./base/services/java/com/android/server/WindowManagerService.java: implements KeyInputQueue.FilterCallback { ./base/services/java/com/android/server/InputDevice.java: // For use by KeyInputQueue for keeping track of the current touch ./base/services/java/com/android/server/InputDevice.java: if (KeyInputQueue.BAD_TOUCH_HACK) { ./base/services/java/com/android/server/InputDevice.java: Slog.i("KeyInputQueue", "Updating: " + currentMove); ./base/services/java/com/android/server/InputDevice.java: Slog.i("KeyInputQueue", "Updating: " + currentMove); 10.从上面的查找结果得知,会在WindowManagerService.java中有一个KeyQ类继承KeyInputQueue类,再在这个文件中查找KeyQ类在哪里定义并实例化的,找到在其构造函数里实例化的。 private WindowManagerService(Context context, PowerManagerService pm, boolean haveInputMethods) { if (MEASURE_LATENCY) { lt = new LatencyTimer(100, 1000); } .... mQueue = new KeyQ();
mInputThread = new InputDispatcherThread();
PolicyThread thr = new PolicyThread(mPolicy, this, context, pm); ... } 至此,基本上把Input event的Framework的流程全部走完了。WindowManagerService是属于System server进程里面起的一个Service.一开机就会运行,当然其构造函数一开机就能会运行。 至此,整个流程如下: WindowManagerService | | // KeyQ | | // KeyInputQueue | | // EventHub | | // Kernel device (/dev/input) |