Android InputEvent(Motion/Key/Sleep....) 流程跟踪

原创 2015年11月20日 16:18:36
  1. ViewRootImpl的setView()函数中(这个函数被调用代表着Activity的界面基本建立)会建立一群InputStage并以职责链模式链接起来进行协同工作:

    • syntheticInputStage
    • viewPostImeStage
    • nativePostImeStage
    • earlyPostImeStage
    • imeStage
    • viewPreImeStage
    • nativePreImeStage
    • 上面的优先级是倒序的,mFirstInputStage = nativePreImeStage是地一个InputStage,而mFirstPostImeInputStage = earlyPostImeStage.
  2. QueuedInputEvent: 代表着一个等待被处理的输入事件,可能是任何输入

    • 一般来说,event都是被串行处理的,分法者每次会向application传递一个Event并且在其处理完成以后投递下一个.
    • 因为application或者IME可以合成或者添加多个event在同一时间内,因此在application端就不再有这样一个队列。
  3. mFirstPostImeInputStage和mFirstInputStage会在deliverInputEvent(…)中被使用: InputStage stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;

  4. ViewRootImpl中的ViewPreImeInputStage extends InputStage

    • onProcess(QueuedInputEvent q)被Override,只有在q.mEvent instanceof KeyEvent时才会调用processKeyEvent(q), 否则直接返回FORWARD将QueuedInputEvent传递下去.
    • processKeyEvent(QueuedInputEvent q), 调用 mView(即为Activity的最外层View,基本就是DecorView).dispatchKeyEventPreIme(event),并返回FINISH_HANDLED代表着处理了该Event,如果dispatchKeyEventPreIme返回false, 那么就返回FORWARD以在onProcess中将事件传递下去.
  5. ViewRootHandler extends Handler 中的handleMessage对MSG_DISPATCH_INPUT_EVENT/MSG_DISPATCH_KEY_FROM_IME会进行enqueueInputEvent的处理,

  6. PhoneWindow的injectInputEvent(…) 会调用到ViewRootImpl的dispatchInputEvent. 但是一般来说应该是WindowInputEventReceiver(构造时传入了UIThread 的Handler)接受到onInputEvent并将其enqueue

  7. WindowInputEventReceiver extends InputEventReceiver, 其onInputEvent(…)会被dispatchInputEvent(…)(该方法没有被WindowInputEventReceiver Override)调用,而后者则会在native层被调用.

    • NativeInputEventReceiver::consumeEvents(…)会调用Java层的dispatchInputEvent(…).
    • consumeEvents(…)的调用者则是handleEvent(int receiveFd, int events, void* data)调用.
    • native层的nativeInit(…)则是Java层的nativeInit(…)的native实现.
    • nativeInit(…)在InputEventReceiver构造时就会被调用(传递的第一个参数是指向自己的弱引用, 第二个参数则是channel,第三个参数则是Looper的MessageQueue). native层会根据这些信息在native层也构造一个NativeInputEventReceiver.
    • 输入的Channel会被存在mInputConsumer中.
    • nativeInit中会调用initialize(), 这个函数做了关键的工作: 调用了setFdEvents(ALOOPER_EVENT_INPUT)
    • setFdEvents获取给定的Channel的fd,并将这个fd和要set的Events根据events是否为非0来addFd(…)/removeFd(…). addFd(…)代表着将此fd加入到对某个event的监听中,并且会传入this作为回调的接口,这也是其继承LooperCallback的原因
    • addFd()的实现在core/libutils/Looper.cpp中: 其内部实现就是使用了epoll(epoll_ctl这个系统调用将被监听的描述符添加到epoll句柄或从epool句柄中删除或者对监听事件进行修改)来监听相应的event并回调
    • 再看一下fd怎么获得的: setFdEvents时会mInputConsumer.getChannel()->getFd():
    • InputConsumer在..frameworks/native/include/input/InputTransport.h中被定义, InputChannel也是,其返回的mFd是在构造时传入的.

    • InputConsumer的构造是在InputEventReceiver的nativeInit(…)中调用 sp inputChannel = android_view_InputChannel_getInputChannel(env, inputChannelObj)根据java层传入的InputChannel对象来决定的,其定义在android_view_InputChannel.cpp中.

    • NativeInputChannel* nativeInputChannel = android_view_InputChannel_getNativeInputChannel(env, inputChannelObj)获得一个对象,然后返回其nativeInputChannel->getInputChannel()
    • android_view_InputChannel_getNativeInputChannel的操作则是取得java层传入的InputChannel对象的IntField, GetIntField(inputChannelObj, gInputChannelClassInfo.mPtr)将其强转为NativeInputChannel*并返回(reinterpret_cast), 转化的就是Java层InputChannel对象的mPtr值
    • 不过Java层的InputChannel的mPtr其实也是在自己构造时在native层生成并回传的. android_view_InputChannel.cpp中的android_view_InputChannel_setNativeInputChannel会设置mPtr的值.
    • 在构造时调用android_view_InputChannel_setNativeInputChannel的函数是android_view_InputChannel_createInputChannel, 而后者则是被android_view_InputChannel_nativeOpenInputChannelPair调用, 再进一步被Java层的openInputChannelPair(String name)(注意是一个static的函数)调用.
    • InputChannel的openInputChannelPair(…)调用点很多,其中一个是在InputManagerService中的monitorInput(String inputChannelName), 后者进一步在WindowManagerService的构造函数中被调用: mPointerEventDispatcher = new PointerEventDispatcher(mInputManager.monitorInput(TAG)), TAG值是”WindowManager”
    • 得到InputChannelName以后回到android_view_InputChannel_**nativeOpenInputChannelPair**中调用openInputChannelPair来获取serverChannel和clientChannel
    • nativeOpenInputChannelPair的实现在InputTransport.cpp中:

    • 创建两个socket, socketpair(…)申请一对socket pair并保留fd
    • 设置socket的属性
    • 然后根据两个socket fd创建两个InputChannel: 分别赋予不同的ChannelName: append(” (server)”)/(” (client)”)
    • 上面的socketpair的两个fd就是channel的fd., 就是上面epoll所监听的fd
  8. 回到WindowManagerService, 在addWIndow时,ViewRootImpl会传入自己new的InputChannel, 这个Channel以RPC的形式传递给WindowManagerService, 在addWindow(…)中调用了InputChannel.openInputChannelPair得到一对建立了联接的InputChannel以后(inputChannels),会调用win(一WindowState对象).setInputChannel(inputChannels[0]); inputChannels[1].transferTo(outInputChannel(ViewRootImpl传递过来的));

  9. 后面接着调用了mInputManager.registerInputChannel(win.mInputChannel(之前传入的inputChannels[0]), win.mInputWindowHandle);
  10. mInputManager即是InputManagerService, 最终调用到native层的nativeRegisterInputChannel(…), 进一步到natiev层的NativeInputManager的registerInputChannel(…)
  11. 进一步的mInputManager->getDispatcher()->registerInputChannel(…)
  12. InputManager.cpp->InputDispatcher.cpp->registerInputChannel(…)->mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);, 还会根据channel信息建立一个Connection并加入到mConnectionsByFd中
  13. addFd在之前已经分析过了,基本就是加入到epoll.
  14. InputDispatcher.cpp的dispatchEventLocked(..)会根据输入的inputTarget.inputChannel从mConnectionsByFd中找到合适的Connection来传递Event,<-dispatchMotionLocked(..) <- dispatchOnceInnerLocked() <- dispatchOnce() <- InputDispatcherThread::threadLoop()
  15. InputDispatcherThread会在InputManager::initialize()中被new出来,并且在构造时传入一个InputDispatcher对象, 在InputManager::initialize()中会将InputDispatcherThread run起来,其线程优先级是PRIORITY_URGENT_DISPLAY
  16. Thread基类是system/core/include/utils/Thread.h定义的,关键的虚函数threadLoop(),其使用,如果返回true, 那么继续loop,返回false则线程完成任务结束,而在InputDispatcherThread中的threadLoop()一直返回true代表永不结束
  17. 对于inputEvent的预处理的关键实现在dispatchOnceInnerLocked中, 主要是在处理mPendingEvent, 有可能是从mInboundQueue中取出的,而对mInboundQueue的操作则主要是两个函数:
    • enqueueInboundEventLocked(EventEntry* entry), 放入, notifyKey(…)和notifyMotion(…)应该是外部调用来传递事件的主要接口
    • drainInboundQueueLocked(),取出,
  18. 从上面的分析以及其名字可以看出来,InputDispatcherThread其实只是在dispatch event而不负责产生event,因此一定有一个负责产生event的其他类型对象,并且InputDispatcherThread应该是以listener的形式挂接在上面,从其集成的接口也可以看出来: class InputDispatcher : public InputDispatcherInterface, 而lass InputDispatcherInterface : public virtual RefBase, public InputListenerInterface, 注意InputListenerInterface, 其会在InputListener.cpp和InputReader.cpp中被作为Listener使用
  19. 对应的产生Event的类就是InputReader,其也有一个对应的InputReaderThread, 对于触摸类事件的相关函数是dispatchPointerSimple(…),InputReaderThread的threadLoop()实现是mReader->loopOnce(),返回是true,代表永不结束
  20. loopOnce()是主体循环函数, 会调用mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE)来获取新的event存放在mEventBuffer中并返回event的个数. 然后调用processEventsLocked(mEventBuffer, count)来处理mEventBuffer中的事件.
  21. InputReader也是在InputManager的构造函数中被实例化的, 传入的eventHub直接使用了InputManager构造时传入的eventHub(注意因为是C++,因此都是sp), InputReader也可以作为InputManager的构造参数传入.
  22. 进一步的, InputManager的构造是在NativeInputManager(frameworks/base/services**/jni**/com_android_server_input_InputManagerService.cpp)的构造函数中, 而eventHub的实例化也在这里: sp eventHub = new EventHub().
  23. EventHub的getEvents函数,其主体系统调用epoll_wait(mEpollFd, …..), 其中的mEpollFd是在构造时通过epoll_create(EPOLL_SIZE_HINT)创建的该 函数生成一个epoll专用的文件描述符。它其实是在内核申请一空间,用来存放你想关注的socket fd上是否发生以及发生了什么事件。size(EPOLL_SIZE_HINT)就是你在这个epoll fd上能关注的最大socket fd数. 构造时还会有epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem), mINotifyFd = inotify_init(),inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE) 监视DEVICE_PATH这个目录
  24. EventHub::openDeviceLocked(const char *devicePath)会打开对某个设备的监听,并且int fd = open(devicePath, O_RDWR | O_CLOEXEC) 打开这个设备并获得fd,然后将设置fd最后将其加入到epoll的监听中,epoll_ctl(mEpollFd, EPOLL_CTL_ADD(加入), fd, &eventItem(这里面是要监听这个fd的哪些事件)), 添加了以后,在getEvents中的epoll_wait(…)在有想要的事件发生时,就会返回,然后将此事件加入到InputDisptacher的队列中,等待InputDisptacher通过InputEventReceiver分发到对应的channel,进一步到最开始的ViewRootIpl中的WindowInputEventReceiver再到InputStage
  25. openDeviceLocked(…)会在scanDirLocked(const char *dirname)/readNotifyLocked()中被调用, 打开并开始监听相关的输入设备.scanDevicesLocked()会在getEvents中会进行一次scan,之后再调用getEvents就不会再进行(one-shot)
  26. inotify_add_watch的目录是/dev/input, scanDevicesLocked中调用也是scanDirLocked(/dev/input), 在adb shell中用getevet这个函数就可以看到当前所有的input device以及触发的相应事件,下面就是genymotion虚拟机上的例子:

    add device 1: /dev/input/event7
    name: “Genymotion Virtual Input”
    could not get driver version for /dev/input/mouse3, Not a typewriter
    add device 2: /dev/input/event6
    name: “Genymotion via VirtualBox seamless mouse”
    could not get driver version for /dev/input/mouse2, Not a typewriter
    add device 3: /dev/input/event5
    name: “Genymotion via VirtualBox PS/2 mouse”
    could not get driver version for /dev/input/mouse1, Not a typewriter
    add device 4: /dev/input/event4
    name: “VirtualBox mouse integration”
    add device 5: /dev/input/event1
    name: “Sleep Button”
    add device 6: /dev/input/event0
    name: “Power Button”
    could not get driver version for /dev/input/mouse0, Not a typewriter
    add device 7: /dev/input/event3
    name: “ImExPS/2 Generic Explorer Mouse”
    add device 8: /dev/input/event2
    name: “AT Translated Set 2 keyboard”
    could not get driver version for /dev/input/mice, Not a typewriter
    一次屏幕点击触发的事件
    /dev/input/event7: 0001 014a 00000001
    /dev/input/event7: 0003 003a 00000001
    /dev/input/event7: 0003 0035 0000013c
    /dev/input/event7: 0003 0036 0000019a
    /dev/input/event7: 0000 0002 00000000
    /dev/input/event7: 0000 0000 00000000
    /dev/input/event7: 0001 014a 00000000
    /dev/input/event7: 0003 003a 00000000
    /dev/input/event7: 0003 0035 0000013c
    /dev/input/event7: 0003 0036 0000019a
    /dev/input/event7: 0000 0002 00000000
    /dev/input/event7: 0000 0000 00000000

相关文章推荐

Key event 分发流程研究心得

http://my.eoe.cn/10407/archive/1176.html Key event 分发流程研究心得 0 作者:powq更新于 02月27日...

Android 中input event的分析

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

struct input_event详解

查看/dev/input/eventX是什么类型的事件, cat /proc/bus/input/devices 设备有着自己特殊的按键键码,我需要将一些标准的按键,比如0-9,X-Z等模拟...

android input进程(模拟按键)

我们可以在手机adb shell中,使用input来模拟按键,和之前的sm类似,input也是一个进程,在framework/base/cmds目录下。 一、Input源码 下面我们先看下input的...

Android 模拟系统事件(一)

使用系统自带类来实现系统事件注入,需要使用的类如下: android.os.ServiceManager android.view.IWindowManager 很不幸,笔者在引用他的使用,发现已经被...

android跨进程事件注入(程序模拟用户输入)

http://zuoshu.iteye.com/blog/1775606 转载请注明出处 早想写这篇,一直没空,现在总结下。 需求: 需要在程序内模拟用户输入,比如点击屏幕...

Android 4.0 事件输入(Event Input)系统

1. TouchScreen功能在Android4.0下不工作        原来在Android2.3.5下能正常工作的TouchScreen功能,移植到Android 4.0就不能正常工作了。凭...
  • MyArrow
  • MyArrow
  • 2011年12月21日 13:54
  • 46675

Android App层通过JNI从驱动获取Input Event

1 概述   尝试在App层直接读取驱动的Input Event,获取触屏事件(本文获取的是电磁笔触屏事件),而不通过Android的Input Framework.     2 架构 3 ...
  • xnwyd
  • xnwyd
  • 2014年12月29日 11:13
  • 7207

Android 4.0 事件输入(Event Input)系统

1. TouchScreen功能在Android4.0下不工作        原来在Android2.3.5下能正常工作的TouchScreen功能,移植到Android 4.0就不能正常工作了...

在 Android 通过 get_event 获得 input 设备 上报event

Android 本身有一个genevnet 和 sendevent 工具用来从内核获取event事件和向内核发送event事件,具体可以参考  o get_evnet 是一个可以获得注册成inpu...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Android InputEvent(Motion/Key/Sleep....) 流程跟踪
举报原因:
原因补充:

(最多只允许输入30个字)