Android Input Event 子系统的来龙去脉。

 http://blog.csdn.net/learnrose/archive/2011/03/10/6236890.aspx

Android 系统里面有很多小工具,运行这些工具,我们对它们有一个感性的认识,进而阅读和分析这些小工具源代码,再顺藤摸瓜,就可以把整个子系统的来龙去脉弄清楚。

 

1.运行toolboxgetevent 工具。

 

# 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。从上面的输出来看,系统有5input 子系统。它们分别是

 

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是否也是一样的原理呢??猜测应该是一样的才对,不然这个工具就没有调试的价值了。

 

我们来阅读和分析frameworkinput 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文件得知,在下面的函数中调用了EventHubgetEvent函数

 

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 eventFramework的流程全部走完了。WindowManagerService是属于System server进程里面起的一个Service.一开机就会运行,当然其构造函数一开机就能会运行。

 

至此,整个流程如下:

 

               WindowManagerService

 

                             |

 

                             |

 

                            \/

 

                         KeyQ

 

                             |

 

                             |

 

                            \/

 

                        KeyInputQueue

 

                             |

 

                             |

 

                            \/

 

                        EventHub

 

                             |

 

                             |

 

                            \/

 

                         Kernel device (/dev/input)

 

后续的文章将介绍/dev/inputKernel中的实现。

 

 

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/learnrose/archive/2011/03/10/6236890.aspx

更多相关文章

  1. Android(安卓)Widget实例日期和计时器
  2. Android(安卓)Camera HAL3中预览preview模式下的控制流
  3. Linux下使用android ndk编译FFmpeg(最新版FFmpeg 4.4.1)
  4. android studio 查找替换
  5. (转载)Android(安卓)耗时代码(ANR)的查找检测和分析解决 TraceView
  6. getevent 工具 和 Android(安卓)中input event的分析
  7. Android音频系统之AudioFlinger(三)
  8. 如何让Android系统或Android应用执行shell脚本
  9. Android中打印调用栈

随机推荐

  1. android理解程度考察
  2. Android调用平台功能具体技巧分享
  3. Android(安卓)native/C/C++ 监控文件夹变
  4. Android组件化方案
  5. android filter
  6. Android(安卓)Studio 1.3 正式发布
  7. RelativeLayout常用布局属性
  8. Android中查看和管理sqlite数据库
  9. android 布局属性大全
  10. Android中使用Build获取当前系统SDK版本