toolbox/getevent
Getevent是android工具集中的一员。Getevent列出设备上所有输入设备的信息,并且dump 输入事件。
源代码位于:system/core/toolbox。
toolbox/sendevent
Sendevent是android工具集中的一员。Sendevent用来模拟底层的输入事件。
源代码位于:system/core/toolbox。
libinput
源代码位于:frameworks/native/libs/input。
头文件位于:
frameworks/native/include/android、frameworks/native/include/input。
其中android/keycodes.h定义了android的系统键值android/input.h定义了相关flags。
设备配置文件有三类:
- 配置文件(idc/*.idc)
- 布局映射文件(keylayout/*.kl)
- 字符映射文件(keychars/*.kcm)
键值映射文件:
frameworks/base/data/keyboards/Generic.kl
frameworks/base/data/keyboards/Generic.kcm
参考:
KeyMap管理一个设备的配置文件,并读取配置到内部KeyLayoutMap、KeyCharacterMap对象中。配置文件的搜索使用下面的顺序:
- ByName (name来自设备属性“keyboard.layout”或者“keyboard.characterMap”)
- /system/usr/<dir>/<name>.<suffix>
- /data/system/devices/<dir>/<name>.<suffix>
- ByDeviceIdentifier
- ByName(“Vendor_%04x_Product_%04x_Version_%04x”)
- ByName(“Vendor_%04x_Product_%04x”)
- ByName(“Generic”)
- ByName(“Virtual”)
比如:标准键盘的名字是qwerty,所以系统里面一般都有下面两个文件:
/system/usr/keylayout/qwerty.kl和/system/usr/keychars/qwerty.kcm。
InputDeviceIdentifier描述一个输入设备,比如名称、GUID、厂商、产品代码、版本、描述等。
InputDeviceInfo描述输入设备的特性(characteristics)和能力(capabilities)。
KeyLayoutMap管理一个布局映射。查找key的时候先查找usage码(HID),再查找scan码(LKC)返回Android码(AKC)和flags。被EventHub使用。
KeyCharacterMap管理一个字符映射,也有键击(Key)事件布局映射的功能(没有flags定义),支持用另一个KeyCharacterMap覆盖生成新的KeyCharacterMap。被java层的KeyCharacterMap类封装。(参见:PhoneWindowManager.dispatchUnhandledKey)
InputEvent继承AInputEvent,定义了输入事件。有KeyEvent、MotionEvent两个派生类。
InputEventFactoryInterface定义了生成InputEvent的工厂接口。
PreallocatedInputEventFactory实现InputEventFactoryInterface,对各种类型的InputEvent分别有一个预先申请的对象。
PooledInputEventFactory是InputEventFactoryInterface接口的另一个实现,用对象池处理InputEvent的申请释放。
InputMessage定义了一个进程间传递的输入事件结构,有键盘、移动、“完成”三种类型。“完成”用于反向应答事件处理情况。
InputChannel用于进程之间传递输入事件。两端各有一个InputChannel。使用socketpair(SOCK_SEQPACKET)实现。
InputConsumer从InputChannel读取InputMessage转换为InputEvent,支持批处理。
InputPublisher向InputChannel发送InputMessage,读取完成事件。
libinputservice
libinputservice实现输入事件的读取、分派,作为动态链接库被libandroid_servers库链接,最终被SystemServer进程中加载。
源代码位于:frameworks/base/services/input。
几个关键类:InputManager、InputDispatcher、InputReader、EventHub。
EventHub的核心在一个epoll的使用上,这个epoll包含下面这些文件描述符:
- mINotifyFd(监视/dev/input/目录变化,使用inotify)
- mWakeReadPipeFd(与mWakeWritePipeFd一起,是一个pipe,用于唤醒epoll)
- Device::fd(每个设备的文件描述符)
EventHub的getEvents会收集最多外面指定数量的输入事件,包括设备“增加”、“删除”事件。如果没有事件并且没有被唤醒,则通过epoll_wait等待。epoll事件(注意与输出事件区分)被缓存在mPendingEventItems数组中。为了在关闭设备文件描述符之前读取输入事件,mINotifyFd对应的epoll事件需要延迟处理。
EventHub内部的私有Device类,保存了设备的基本信息、事件类型、分类信息,配置、键值映射。EventHub负责维护这些信息,布局映射转换、按键状态查询。Device类还保存了字符映射与外面指定的一个字符映射(overlayKeyMap)的合并字符映射(combinedKeyMap)。
InputMapper将原始输入事件分类转换,不同类型的InputMapper处理不同类型的输入事件。InputMapper通过process方法处理原始事件,处理完成后通知到InputListener,不同类型的事件调用InputListener对应的回调方法。
InputDevice保存设备信息和多个InputMapper。InputDevice的process方法会遍历所有InputMapper,调用其process方法。
NotifyArgs是一个基类,调用InputListener不同的notify接口时,传递的是对应的NotifyArgs派生类对象。NotifyArgs定义了notify虚方法,用来给InputListener发送notify事件。不同的NotifyArgs的派生类在notify方法实现中调用InputListener相应的notify方法。
InputReader的loopOnce首先通过EventHub 的getEvents接口获取输入事件。如果是合成事件(SYNTHETIC_EVENT),比如DEVICE_ADDED事件,那么创建一个InputDevice,并根据设备的classes属性配置多个InputMapper。如果是普通事件,将连续同一个设备的事件一起处理,调用InputDevice的process接口。为了防止InputListener在加锁状态下回调InputReader,增加了QueuedInputListener来临时缓存事件。
InputReader::QueuedInputListener继承并且封装另一个InputListenerInterface,将接收到的事件排队,在最后flush时发送给真正的Listener。排队事件是NotifyArgs对象。
InputReader::InputReaderContext将InputReader的mPolicy、mQueuedListener、mEventHub,已经一些方法打包,传递给InputDevice和InputMapper。
InputWindowInfo描述窗口的属性。
InputWindowHandle是一个句柄,内部包含InputWindowInfo指针。
InputTarget定义输入事件提交到一个窗口的方式,包含一个InputChannel、控制flags、超时时间、坐标。
InputDispatcher继承InputListenerInterface,接收InputReader输入的事件,插入到队列中,在另一个线程loopOnce分派这些事件。InputDispatcher管理事件连接Connection、窗口句柄InputWindowHandle。
InputDispatcher::Connection管理着一个InputChannel相关的状态,包括InputState、输出队列、等待应答队列。
InputDispatcher::InputState跟踪已经分派的键击、移动状态,以便取消事件能够同步。
InputManager创建InputDispatcher和InputReader,并创建InputReaderThread循环调用InputReader 的loopOnce,创建InputDispatcherThread循环调用InputDispatcher的dispatchOnce。