一个懒惰的文艺工程师!
分类: LINUX
2013-11-08 21:47:16
原文地址:Android对触摸屏、键盘、轨迹球事件的响应过程 作者:zyhualove
最近做一个触摸屏驱动,了解一下Android对触摸屏、键盘、轨迹球事件的响应过程是必须了,在网上查了资料,再结合自身学习体会,对此做点总结。理解不到位的地方,还望赐教!
事件的传入是从EventHub开始的,EventHub是事件的抽象结构,维护着系统设备的运行情况,设备类型包括Keyboard、Touchscreen、TraceBall等。它在系统启动的时候会通过open_device()方法将系统提供的输入设备都增加到这个抽象结构中,并维护一个所有设备的文件描述符,如果输入设备是键盘的话还会读取XXXXX-Keypad.kl对应的键盘设备的映射文件,另外getEvent()方法是对EventHub中的设备文件描述符使用poll操作等待驱动事件的发生,如果发生的事件是键盘事件,则调用KeyLayoutMap的map()方法按照映射文件转换成相应的键值并将扫描码和键码返回给KeyInputQueue。
frameworks/base/libs/ui/KeyLayoutMap.cpp主要是读取键盘映射文件并将键盘扫描码和键码进行转换。
frameworks/base/services/jni/com_android_server_KeyInputQueue.cpp是EventHub和KeyInputQueue的JNI接口层。framework/base/services/java/com/android/server/KeyInputQueue.java通过jni调用framework/base/services/jni/com_android_server_KeyInputQueue.cpp调用EventHub.cpp里的文件。
KeyInputQueue里创建了InputDeviceReader线程,在线程InputDeviceReader中会根据事件的类型及事件值进行判断处理,从而确定这个事件对应的设备状态是否发生了改变并改变对这个设备的描述的描述结构InputDevice。
frameworks/base/services/java/com/android/server/WindowManagerService.java,进程WindowManager会创建一个线程InputDispatcherThread,在这个线程里从事队列中读取发生的事件,QueuedEvent ev = mQueue.getEvent(。。。),并根据读取的事件类型的不同分成三类(KEYBOARD、TOUCHSCREEN、TRACKBALL),分别进行处理,例如键盘事件会调用dispatchKey((KeyEvent)ev.event, 0, 0)以将事件通过Binder发送给具有焦点的窗口应用程序,然后调用mQueue.recycleEvent(ev)继续等待键盘事件的发生;如果是触摸屏事件则调用dispatchPointer(ev, (MotionEvent)ev.event, 0, 0),这里会根据事件的种类(UP、DOWN、MOVE、OUT_SIDE等)进行判断处理,如取消或将事件发送到具有权限的指定的窗口中去。
EventHub对输入设备进行了封装。输入设备驱动程序对用户空间应用程序提供一些设备文件,这些设备文件放在/dev/input里面。
EventHub扫描/dev/input下所有设备文件,并打开它们。(res = scan_dir(device_path))
EventHub对外提供了一个函数用于从输入设备文件中读取数据。该接口通过frameworks/base/services/jni/com_android_server_KeyInputQueue.cpp的android_server_KeyInputQueue_readEvent封装后成为
frameworks/base/libs/ui/KeyLayoutMap.cpp的本地readEvent()方法。
KeyInputQueue.java进入死循环先执行readEvent()方法,实际调用EventHub.cpp中的getEvent()等待输入。EventHub.cpp中的getEvent进入死循环,执行pollres = poll(mFDs, mFDCount, -1)等待设备结点处有数据写入(也就是有键按下),当有数据写入时执行下面的for循环,找出是哪个fd中有内容写入,并读出写入的数据res = read(mFDs[i].fd, &iev, sizeof(iev))。这里只读出了TYPE和Scancode(),而不会有Keycode,硬件层只能向设备文件写入Scancode,而Keycode是上层要用的,由map得到。接下来执行err = mDevices[i]->layoutMap->map(iev.code, outKeycode, outFlags)map出Scancode对应的Keycode和Flags。map是在KeyLayoutMap.cpp中实现的,是在m_keys中根据scanCode找到这两个值。而m_keys是在KeyLayoutMap.cpp这个文件中load时,读取kl(一般在手机的/system/usr/keylayout文件夹下,代码中在src/vendor/ste/xxxx_refd/XXXX-Keypad.kl)文件,解析出所有的scanCode对应的keyCode和flag来并加到m_keys中得到的。kl文件中的类似宏的字符如POWER是在frameworks/base/include/ui/KeycodeLabels.h中对应出来的。