Android 源码分析 - 输入 - Native层

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。

        设备配置文件有三类:

  1. 配置文件(idc/*.idc)
  2. 布局映射文件(keylayout/*.kl)
  3. 字符映射文件(keychars/*.kcm)

        键值映射文件:

        frameworks/base/data/keyboards/Generic.kl

        frameworks/base/data/keyboards/Generic.kcm

        参考:

        KeyMap管理一个设备的配置文件,并读取配置到内部KeyLayoutMap、KeyCharacterMap对象中。配置文件的搜索使用下面的顺序:

  1. ByName (name来自设备属性“keyboard.layout”或者“keyboard.characterMap”)
    1. /system/usr/<dir>/<name>.<suffix>
    2. /data/system/devices/<dir>/<name>.<suffix>
  2. ByDeviceIdentifier
    1. ByName(“Vendor_%04x_Product_%04x_Version_%04x”)
    2. ByName(“Vendor_%04x_Product_%04x”)
  3. ByName(“Generic”)
  4. 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包含下面这些文件描述符:

  1. mINotifyFd(监视/dev/input/目录变化,使用inotify)
  2. mWakeReadPipeFd(与mWakeWritePipeFd一起,是一个pipe,用于唤醒epoll)
  3. 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。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Fighting Horse

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值