android dispatch input输入子系统,Android 输入设备子系统架构

Android TV开发过程中,主要的输入设备是摇控器(IR),键盘(keypad),以及一些USB的HID输入设备,本文主要讲讲这块的工作流程,使用的是AN5版本。

流程

首先,Linux kernel 驱动层得到硬件设备按键的原始扫描码,触摸,移动等各种事件信息,按键码会被转化为Linux 标准的KEY (uapi/linux/input.h)。最终,kernel会把设备的事件转换成一个标准的Linux Input Event (linux/input.h) ,抛给上面的系统。

接下来, Andorid Framework 层的 EventHub (native/services/inputflinger) 通过读取 /dev/input/ 目录下的设备文件,得到kernel层抛出来的 Linux Input Event,把它转化成 Android Input Event。这个转换过程,系统需要查找一些配置文件,大概有这么几类:

.kl文件: Key Layout ,TV方案最常见就是这类文件,主要是KEY的映射。

.kcm文件: Key Character ,用于 Virtual Keyboard,把几个Android的组合键,变成一个输出键,比如,输入 shift + a,则输出大写的 A

.idc:Input Device Configuration,基本上不需要,标准的输入设备,像HID键盘,鼠标等,系统会自动识别。

搜索路径一般是 /data/usr 和 /system/usr,其中 /data 和 /system 是读取的系统属性,

/sytem = getenv("ANDROID_ROOT")

/data = getenv("ANDROID_DATA")

如果,你找不到相应的配置文件,不妨先读取一下相应的环境参数。

文件的命名规则是 Vendor_XXXX_Product_XXXX.kl,后面还可以带上版本号--Vendor_XXXX_Product_XXXX_Version_XXXX.kl,总之,根据Vendor id和Product id,就能确定配置文件。 如: Vendor=5f5f Product=6f6A ,那么文件名是 Vendor_5f5f_Product_6f6A.kl

如果,不知道输入设备的ID号,可以通过下面命令得到Vendor和Product ID.

cat /proc/bus/input/devices

0

现在, 以KeyEvent为例 ,从源码级别,来说明一下大概的流程。

Kernel里面的就不多说了,标准的Linux驱动结构,先从EventHub开始说起,它是整个转换流程的开端,第一步当然是要扫描,读取设备文件,scanDevicesLocked负责处理。

EventHub::scanDevicesLocked

扫描"/dev/input" 目录,读取所有的设备文件,逐一打开每个设备文件,读取设备文件的“元数据”--设备名,驱动版本号,设备标识等,如下图所示:

0

有了vendor 和 product ID 号, EventHub 就会去尝试加载配置文件,主要的工作是在 native/libs/input/InputDevice.cpp 里面完成的。

EventHub::loadConfigurationLocked(Device* device)

-->InputDevice.cpp:getInputDeviceConfigurationFilePathByDeviceIdentifier

到这里为止,EventHub基本准备就序,接下来就是事件轮循:用户按键--》转换按键--》分发AN KeyEvent--》下一次用户按键。它需要轮循检测所有设备文件的事件,这个工作在

getEvents 里面来完成。

EventHub::getEvents

它使用epoll API来处理设备文件事件--等待,读取设备文件数据。

注册:

mINotifyFd = inotify_init();

int result = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);

struct epoll_event eventItem;

memset(&eventItem, 0, sizeof(eventItem));

eventItem.events = EPOLLIN;

eventItem.data.u32 = EPOLL_ID_INOTIFY;

result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);

等待:

InputReaderThread::threadLoop --> InputReader::loopOnce() --> EventHub::getEvents

int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);

kernel抛出事件后, epoo_wait 由"阻塞"变成"就绪", 可以读取Linux 事件了。

读取:

int32_t readSize = read(device->fd, readBuffer,

sizeof(struct input_event) * capacity);

…..

struct input_event& iev = readBuffer[i];

ALOGV("%s got: time=%d.%06d, type=%d, code=%d, value=%d",

device→path.string(), (int) iev.time.tv_sec, (int) iev.time.tv_usec,

iev.type, iev.code, iev.value);

读取设备文件数据,得到具体的Linux事件后, EventHub相关的工作就结束了,InputReader会把后面的工作,派遣到 InputDevice::process

InputDevice::process

主要是管理设备配置文件,比如前面提到的.kl文件, 触摸屏设备配置文件等。 Linux Input Event 通过它就转变成了 Android Input Event,然后分发到各个监听器。

注册:

device->addMapper(new KeyboardInputMapper(device, keyboardSource, keyboardType));

映射: 根据.kl文件,得到了Android 的 KeyCode.

if (getEventHub()->mapKey(getDeviceId(), scanCode, usageCode, &keyCode, &flags)) {

keyCode = AKEYCODE_UNKNOWN;

flags = 0;

}

分发:

NotifyKeyArgs args(when, getDeviceId(), mSource, policyFlags,

down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,

AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, newMetaState, downTime);

getListener()->notifyKey(&args);

APK应该就可以收到KeyEvent 事件了。

工具

最后简单介绍下几个工具,在开发和调试过程中,非常好用。

getevent

Android系统自带的控制台程序,它监控并读取设备文件,是非常好的调试工具。

输入不同的控制参数,可以得到我们想要的各种信息,常用的配置如下:

[缺省]: 输出Linux Event的原始数据。

[-l]:把原始数据解析可读的信息。

[-p]:查看设备支持的所有按键。

getevent: 不带任何参数,可以打印所有设备文件事件

/dev/input/event0: 0000 0000 00000000

/dev/input/event2: 0001 001e 00000001

getevent -l:  我们就能看得懂了

/dev/input/event0: EV_KEY KEY_0 UP

/dev/input/event0: EV_SYN SYN_REPORT 00000000

/dev/input/event2: EV_KEY KEY_LDOWN

getevent[-l]/dev/input/event0 : 打印指定设备的事件。

0000 0000 00000000

0001 0072 00000001

EV_KEY KEY_VOLUMEUP UP

getevent -lt /dev/input/event4: 带上时间戳。

[ 16374.318450] EV_MSC MSC_SCAN 000700e1

[ 16374.318450] EV_KEY KEY_LEFTSHIFT DOWN

getevent -[l]p:得到KEY值表

KEY 0001): 0001 0002 0003 0004 0005 0006 0007 0008

[-l]KEY (0001): KEY_ESC KEY_1 KEY_2 KEY_3

input

可以使用input 命令来发送虚拟键, 如:input keyevent 256

validatekeymaps

一个主机工具,用来校验配置文件格式的正确性。

位置:fameworks/base/tools/validatekeymaps,可能需要手动编译下。

实战

Q: 蓝牙摇控器OK键不起作用?

直接打开getevent,发现按OK键的时候,触发的是触摸事件, 而不是 KEY_ENTER 的按键事件。

最终在APK里面打了一个小补丁

@Override public boolean dispatchTouchEvent(MotionEvent ev) {

if (ev.getAction() == MotionEvent.ACTION_DOWN) {

Log.e(TAG, "dispatchTouchEvent: btnstatus:" + ev.getButtonState());

if (ev.getButtonState() == xxxx) do ...

}

return true;

}

所以,有些蓝牙摇控器ok键会触发触摸的事件,而不是按键事件。

参考文献:

https://source.android.com/devices/input/

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值