系列文章目录
目录
2.3.3 inputreader线程阻塞等待设备添加事件发生
2.3.5 EventHub::readNotifyLocked
2.3.7 registerDeviceForEpollLocked
2.3.9 EventHub::getEvents的第三次for循环
2.3.15 handleConfigurationChangedLocked
2.3.16 notifyInputDevicesChanged
2.3.17 QueuedInputListener::flush
2.3.18 NotifyConfigurationChangedArgs::notify
2.3.19 notifyConfigurationChanged
2.3.21 dispatchOnceInnerLocked
2.3.22 dispatchConfigurationChangedLocked
2.3.23 runCommandsLockedInterruptible
2.3.24 doNotifyConfigurationChangedInterruptible
2.3.25 notifyConfigurationChanged
2.3.26 notifyConfigurationChanged
3.3.3 inputreader线程阻塞等待设备删除事件发生
3.3.5 EventHub::readNotifyLocked
3.3.9 unregisterDeviceFromEpollLocked
3.3.10 releaseControllerNumberLocked
3.3.18 handleConfigurationChangedLocked
3.3.19 notifyInputDevicesChanged
3.3.21 NotifyDeviceResetArgs::notify
3.3.24 dispatchOnceInnerLocked
3.3.25 dispatchDeviceResetLocked
3.3.26 synthesizeCancelationEventsForAllConnectionsLocked
1.简介
从第三篇input启动,我们可以知道,当input系统第一次启动时,会扫描/dev/input下的所有设备节点,然后读取所有设备节点的信息,生成对应的InputDevice对象并保存起来,并且将这些设备节点注册到Epoll中开始监听设备节点下的增删事件。
本篇主要介绍当input启动后,如果存在新的设备增加或设备的删除,input会产生什么消息,做些什么?
2.设备的添加
2.1 流程介绍
1.inputreader线程会阻塞在epoll_wait中,epoll和Notify机制监听/dev/input路径下是否有新设备的增加
2.当新设备添加时,epoll返回,Notify机制会将相应的事件信息写入到inotifyFd所描述的inotify对象中。
3.从Notify中读取消息,然后打开设备,读取设备的厂商信息,布局信息,生成对应的Device类对象。同时注册此设备到epoll监听此设备的输入事件。
4.inputreader会生成设备添加事件和设备完成扫描事件。
5.设备添加事件会根据Device类对象用于生成inputdevice类对象和配置mapper。
6.设备完成扫描事件,主要有两个作用,一个是通知上层监听设备add和删除的listener,另一个是用于生成args然后通知给wms,wms会通知与此输入设备相关联的显示器。
2.2 时序图
为了完整的画出所有时序,较为模糊,读者可保存照片到本地放大观看。
2.3 全源码分析
2.3.1 前情回顾
首先,在第三篇启动篇EventHub的构造函数中会创建Epoll和iNotify对象,用于监听/dev/input节点下是否有设备增删。然后inputreader线程会在EventHub::getEvents中epoll_wait进行等待,当发生设备的增删事件和原始输入事件时,才会继续向下执行。
2.3.2 /dev/input设备节点的监听
//frameworks/native/services/inputflinger/EventHub.cpp
EventHub::EventHub(void) :
mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD), mNextDeviceId(1), mControllerNumbers(),
mOpeningDevices(0), mClosingDevices(0),
mNeedToSendFinishedDeviceScan(false),
mNeedToReopenDevices(false), mNeedToScanDevices(true),
mPendingEventCount(0), mPendingEventIndex(0), mPendingINotify(false)
//参数分析:
//mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD),NO_BUILT_IN_KEYBOARD代表默认无内置键盘。
//mNextDeviceId,代表下一个设备id
//mOpeningDevices最近一次打开的设备,0代表最近无设备打开
//mClosingDevices,最近一次关闭的设备,0代表最近无设备关闭
//mNeedToSendFinishedDeviceScan,表示当设备扫描完成后,是否需要发送设备扫描完成的信号,默认为false
//mNeedToReopenDevices,表示是否需要重新打开设备
//mNeedToScanDevices,表示是否需要扫描设备,此处为是
//mPendingEventCount目前还未处理,正在等待处理的事件数量
//mPendingEventCount目前还未处理,马上要处理的下一个事件的索引。事件是由mPendingEventItems结构体数组存储。
//mPendingINotify,代表待定的通知
{
mEpollFd = epoll_create(EPOLL_SIZE_HINT);//创建epoll对象,mEpollFd为此对象的描述符
mINotifyFd = inotify_init();//创建inotify对象,mINotifyFd为此对象的描述符
int result = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);//将一个用于监听输入设备节点的创建与删除的watch对象,
//添加到inotify对象中,此处DEVICE_PATH为"/dev/input",监听该目录下的设备节点创建与删除操作。
//当/dev/input/下的设备节点发生创建与删除操作时,都会将相应的事件信息写入到inotifyFd所描述的inotify对象中,
//此时可以通过read()函数从inotifyFd描述符中将事件信息读取出来。
struct epoll_event eventItem;//创建epoll_event结构体,此结构体
memset(&eventItem, 0, sizeof(eventItem));//初始值为0
eventItem.events = EPOLLIN;//可读,代表监听可读事件
eventItem.data.u32 = EPOLL_ID_INOTIFY;//EPOLL_ID_INOTIFY代表用于与设备无关的epoll通知的ID。此处为Epoll的通知id
result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);//添加刚才监听目录下设备创建和删除的INotify到epoll实例,EPOLL_CTL_ADD表示增加事件
//epoll_ctl将事件监听添加到epoll对象中去。
//当/dev/input/下的设备节点发生创建与删除操作时,首先会将相应的事件信息写入到inotifyFd所描述的inotify对象中,又因为该inotifyFd被epoll监听可读,故直接生成eventItem事件
}
2.3.3 inputreader线程阻塞等待设备添加事件发生
void InputReader::loopOnce() {
int32_t oldGeneration;
int32_t timeoutMillis;
bool inputDevicesChanged = false;
Vector<InputDeviceInfo> inputDevices;
{ // acquire lock
AutoMutex _l(mLock);
oldGeneration = mGeneration;//新的值
timeoutMillis = -1;
size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);//通过EventHub的getEvents函数获取事件,
//并存放在mEventBuffer中。
//参数分析:timeoutMillis=-1,代表等待超时的时间,感觉如果数字为0代表立即执行
//mEventBuffer是一个存放从eventhub中读取的rawevent结构体类型的数组,源代码是:RawEvent mEventBuffer[EVENT_BUFFER_SIZE];
//EVENT_BUFFER_SIZE值是256,代表最大可以读取256个原始事件,查看833行-840行,RawEvent结构体如下
.......
}
即此时阻塞在getEvents函数的epoll_wait中。
//第一次for循环,会阻塞在epoll_wait中,等待设备增加事件的到来。
size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
ALOG_ASSERT(bufferSize >= 1);
AutoMutex _l(mLock);
struct input_event readBuffer[bufferSize];//bufferSize值为传入的256
RawEvent* event = buffer;//event指针指向传入的buffer首地址,每存入一个事件,event指针向后移动一个元素
size_t capacity = bufferSize;//capacity表示buffer中剩余端元素数量,capacity为0,表示buffer已满
bool awoken = false;
for (;;) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);//获取当前时间
mPendingEventIndex = 0;//此时mPendingEventItems数组中未处理的事件已经处理完了,将mPendingEventIndex设置为0
mLock.unlock();
release_wake_lock(WAKE_LOCK_ID);
int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);//此时读线程会阻塞在此处,等待有消息的事件发生
......
}
2.3.4 当设备添加时
此时/dev/input下添加新设备发生,即当/dev/input/下的设备节点发生创建与删除操作时,
都会将相应的事件信息写入到inotifyFd所描述的inotify对象中,由于epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem)对监听/dev/input的mINotifyFd进行了监听,所以此时mINotifyFd发生变化。故epoll_wait函数不再阻塞。epoll_wait函数会将设备增加事件写入存储eventItem结构体的mPendingEventItems数组中。
此时EventHub::getEvents函数往下执行。
//此时仍在第一次for循环中
size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
ALOG_ASSERT(bufferSize >= 1);
AutoMutex _l(mLock);
struct input_event readBuffer[bufferSize];//bufferSize值为传入的256
RawEvent* event = buffer;//event指针指向传入的buffer首地址,每存入一个事件,event指针向后移动一个元素
size_t capacity = bufferSize;//capacity表示buffer中剩余端元素数量,capacity为0,表示buffer已满
bool awoken = false;
for (;;) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);//获取当前时间
mPendingEventIndex = 0;//此时mPendingEventItems数组中未处理的事件已经处理完了,将mPendingEventIndex设置为0
mLock.unlock();
release_wake_lock(WAKE_LOCK_ID);
int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);//此时读线程会阻塞在此处,等待有消息的事件发生
acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
mLock.lock(); // reacquire lock after poll, must be after acquire_wake_lock
///if (pollResult < 0)
{
} else {
// 发生了设备增加事件
mPendingEventCount = size_t(pollResult);//值是1
}
}
/// 此时并没有返回而是开启了下一次for死循环。全部完成后,返回我们读取的事件数。
///return event - buffer;
}
第二次for循环会取出epoll_event事件,然后判断是add设备事件,然后执行readNotifyLocked函数,此函数内部会调用openDeviceLocked函数。
//第二次for循环
size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
for (;;) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);//获取当前时间
//如果存在未处理的事件,则从mPendingEventItems数组中取出事件
bool deviceChanged = false;
while (mPendingEventIndex < mPendingEventCount) {//此时会执行,mPendingEventIndex=0,mPendingEventCount=1
const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];//取出第一个epoll_event类型的事件
if (eventItem.data.u32 == EPOLL_ID_INOTIFY) {//判断epll返回的事件类型是否是EPOLL通知类型,添加和删除设备就是此类型
if (eventItem.events & EPOLLIN) {//add设备事件是EPOLLIN
mPendingINotify = true;//设置为true
}
continue;//退出本次while循环,继续循环的取出mPendingEventItems数组中的事件
}
}
//readNotify()将修改device 列表,必须在处理完所有其他事件后执行此操作,保证在关闭设备之前读取所有剩余事件。
if (mPendingINotify && mPendingEventIndex >= mPendingEventCount) {//会执行
mPendingINotify = false;
readNotifyLocked();//会调用openDeviceLocked函数
deviceChanged = true;
}
//添加或删除设备,立即结束本次循环,执行下一次for (;;)
if (deviceChanged) {
continue;
}//执行第三次for循环
}
}
2.3.5 EventHub::readNotifyLocked
当/dev/input/下的设备节点发生创建与删除操作时,都会将相应的事件信息写入到inotifyFd所描述的inotify对象中,此时可以通过read()函数从inotifyFd描述符中将事件信息读取出来。
因此,此函数的作用是:
1.通过read从inotifyFd描述符中将设备增加事件读取出来。
2.解析数据为inotify_event格式,此格式会携带设备节点的名称,添加/dev/input/的前缀。假设此时设备名称是5,则此输入设备的完整路径是/dev/input/0。
3.调用openDeviceLocked打开/dev/input/0的设备。
因为会用到inotify_event结构体,因此我们先看一下此结构体的含义。
struct inotify_event
{
int wd; /* 事件对应的Watch对象的描述符. */
uint32_t mask; /* 事件类型,例如文件被删除,此处值为IN_DELETE. */
uint32_t cookie; /* 同步两个事件的缓存 */
uint32_t len; /* name字段的长度 */
char name __flexarr;
// 可变长的字段,用于存储产生此事件的文件路径 * /
};
status_t EventHub::readNotifyLocked() {
int res;
char devname[PATH_MAX];//最大是4096字节
char *filename;//设备节点的名称
char event_buf[512];
int event_size;
int event_pos = 0;
struct inotify_event *event;
ALOGV("EventHub::readNotify nfd: %d\n", mINotifyFd);
res = read(mINotifyFd, event_buf, sizeof(event_buf));//从监听/dev/input的设备fd下读取数据到event_buf中
if(res < (int)sizeof(*event)) {//read返回的是读取的字节数,如果小于inotify_event大小,则数据错误
if(errno == EINTR)
return 0;
ALOGW("could not get event, %s\n", strerror(errno));
return -1;
}
strcpy(devname, DEVICE_PATH);//拷贝"/dev/input"值给devname
filename = devname + strlen(devname);//移动filename指针指向devname数组的最后一位
*filename++ = '/';//即此时devname的值为/dev/input/
while(res >= (int)sizeof(*event)) {//循环取出可能存在的多个设备添加或者删除的事件
event = (struct inotify_event *)(event_buf + event_pos);//取出一个inotify_event结构体的信息
//printf("%d: %08x \"%s\"\n", event->wd, event->mask, event->len ? event->name : "");
if(event->len) {
strcpy(filename, event->name);//拷贝event->name到filename及其后面的地址,即此时devname的值为/dev/input/设备名称
if(event->mask & IN_CREATE) {//如果是设备添加,则openDeviceLocked
openDeviceLocked(devname);//然后打开指定的设备节点
}
else {//如果是设备删除,则closeDeviceByPathLocked
ALOGI("Removing device '%s' due to inotify event\n", devname);
closeDeviceByPathLocked(devname);
}
}
event_size = sizeof(*event) + event->len;
res -= event_size;
event_pos += event_size;//修改偏移量,下次循环取出下一个消息
}
return 0;
}
2.3.6 openDeviceLocked
主要作用为:
1.打开/dev/input/xxxx的设备,然后获取设备的驱动版本,设备产品,设备供应商,设备物理地址,设备标识符将信息保存到InputDeviceIdentifier类的identifier对象中,此对象主要用于存储设备厂商的所有信息。
2.假设此时有一个设备/dev/input/event0,根据其dentifier对象生成对应的Device类对象。然后加载设备的配置文件到device类对象的PropertyMap中。
配置文件主要有三类。
一.idc文件,主要用于触摸屏配置。
二.kl文件,主要用于键盘的扫描码和keycode的转化。
三.kcm文件,主要作用是将 Android按键代码与修饰符的组合映射到 Unicode。
3.从设备的fd中读取数据判断其设备类型为EV_KEY、EV_ABS、EV_REL、EV_SW、EV_LED、EV_FF类型。
一:EV_KEY,按键类型的事件。能够上报这类事件的设备有键盘、鼠标、手柄、手写板
等一切拥有按钮的设备(包括手机上的实体按键)。在Device结构体中,对应的事件
位掩码keyBitmask描述了设备可以产生的按键事件的集合。按键事件的全集包括字
符按键、方向键、控制键、鼠标键、游戏按键等。
二:EV_ABS,绝对坐标类型的事件。这类事件描述了在空间中的一个点,触控板、触
摸屏等使用绝对坐标的输入设备可以上报这类事件。事件位掩码absBitmask描述了
设备可以上报的事件的维度信息(ABS_X、ABS_Y、ABS_Z),以及是否支持多点
事件。
三:EV_REL,相对坐标类型的事件。这类事件描述了事件在空间中相对于上次事件
的偏移量。鼠标、轨迹球等基于游标指针的设备可以上报此类事件。事件位掩码
relBitmask描述了设备可以上报的事件的维度信息(REL_X、REL_Y、REL_Z)。
四:EV_SW,开关类型的事件。这类事件描述了若干固定状态之间的切换。手机上的静
音模式开关按钮、模式切换拨盘等设备可以上报此类事件。事件位掩码swBitmask表
示了设备可切换的状态列表。
五:EV_LED,光反馈类型事件。ledBitmask描述了设备是否支持光
六:EV_FF,力反馈类型,ffBitmask则描述了设备是否支持力反馈。
4.根据上诉大类型,生成更细分的类型——INPUT_DEVICE_CLASS_xxx类型。如L按键事件可再通过有无X,y比特位,区分是键盘还是蓝牙手柄。
一:INPUT_DEVICE_CLASS_KEYBOARD,可以上报鼠标按键以外的EV_KEY类型事件的设备都属于此类。如键盘、机身按钮(音量键、电源键等)。
二:INPUT_DEVICE_CLASS_ALPHAKEY,可以上报字符按键的设备,例如键盘。此类 型的设备必定同时属于KEYBOARD。
三:INPUT_DEVICE_CLASS_DPAD,可以上报方向键的设备。例如键盘、手机导航键
等。这类设备同时也属于KEYBOARD。
四:INPUT_DEVICE_CLASS_GAMEPAD,可以上报游戏按键的设备,如游戏手柄。这类设备同时也属于KEYBOARD。
五:INPUT_DEVICE_CLASS_TOUCH,可以上报EV_ABS类型事件的设备都属于此类,如触摸屏和触控板。
六:INPUT_DEVICE_CLASS_TOUCH_MT,可以上报EV_ABS类型事件,并且其事件位掩码指示其支持多点事件的设备属于此类。例如多点触摸屏。这类设备同时也属于TOUCH类型。
七:INPUT_DEVICE_CLASS_CURSOR,可以上报EV_REL类型的事件,并且可以上报BTN_MOUSE子类的EV_KEY事件的设备属于此类,例如鼠标和轨迹球。
八:INPUT_DEVICE_CLASS_SWITCH,可以上报EV_SW类型事件的设备。
九:INPUT_DEVICE_CLASS_JOYSTICK,属于GAMEPAD类型,并且属于TOUCH类型的设备。
十:INPUT_DEVICE_CLASS_VIBRATOR,支持力反馈的设备。
十一:INPUT_DEVICE_CLASS_VIRTUAL,虚拟设备。
十二:INPUT_DEVICE_CLASS_EXTERNAL,外部设备,即非内建设备。如外接鼠标、键盘、游戏手柄等。
5.然后注册设备节点到epoll中,开始监听各个设备节点下是否有可读事件,即监听此设备的原始输入事件。
status_t EventHub::openDeviceLocked(const char *devicePath) {
char buffer[80];
int fd = open(devicePath, O_RDWR | O_CLOEXEC | O_NONBLOCK);//以读写,非阻塞,原子方式打开设备
//打开设备节点的文件描述符,用于获取设备信息以及读取原始输入事件
InputDeviceIdentifier identifier;//存储厂商的信息
//接下来的代码通过ioctl()函数从设备节点中获取输入设备的厂商信息
if(ioctl(fd, EVIOCGNAME(sizeof(buffer) - 1), &buffer) < 1) {//通过设备的fd,向驱动发送EVIOCGNAME指令,
//获取设备名称,写入buffer中
} else {
buffer[sizeof(buffer) - 1] = '\0';
identifier.name.setTo(buffer);//从buffer中取出名称信息,赋值给identifier的name属性
}
// 检查获取的设备名称是否在排除列表中下
for (size_t i = 0; i < mExcludedDevices.size(); i++) {// 检查获取的设备名称是否在排除列表中下,
//如果在mExcludedDevices列表中,则关闭此设备,并返回,mExcludedDevices列表在setExcludedDevices方法中赋值的,
//而setExcludedDevices方法是在inputreader构造函数中的refreshConfigurationLocked方法中调用的,
//同时mExcludedDevices最后是调用到java层ims中获取的。
const String8& item = mExcludedDevices.itemAt(i);
if (identifier.name == item) {
ALOGI("ignoring event id %s driver %s\n", devicePath, item.string());
close(fd);
return -1;
}
}
//获取设备驱动版本
int driverVersion;
if(ioctl(fd, EVIOCGVERSION, &driverVersion)) {//从驱动中获取的信息赋值给driverVersion
ALOGE("could not get driver version for %s, %s\n", devicePath, strerror(errno));
close(fd);
return -1;
}
//获取设备标识符
struct input_id inputId;
if(ioctl(fd, EVIOCGID, &inputId)) {//赋值给inputId
ALOGE("could not get device input id for %s, %s\n", devicePath, strerror(errno));
close(fd);
return -1;
}
identifier.bus = inputId.bustype;
identifier.product = inputId.product;//设备产品
identifier.vendor = inputId.vendor;//供应商
identifier.version = inputId.version;//版本
// 获取设备物理地址
if(ioctl(fd, EVIOCGPHYS(sizeof(buffer) - 1), &buffer) < 1) {//放于buffer中
//fprintf(stderr, "could not get location for %s, %s\n", devicePath, strerror(errno));
} else {
buffer[sizeof(buffer) - 1] = '\0';
identifier.location.setTo(buffer);//赋值给location
}
//获取设备唯一的id
if(ioctl(fd, EVIOCGUNIQ(sizeof(buffer) - 1), &buffer) < 1) {
//fprintf(stderr, "could not get idstring for %s, %s\n", devicePath, strerror(errno));
} else {
buffer[sizeof(buffer) - 1] = '\0';
identifier.uniqueId.setTo(buffer);//赋值给uniqueId
}
assignDescriptorLocked(identifier);//将identifier信息填充到fd,分配唯一标识设备的设备描述符,主要作用是填写identifier.nonce字段。
// 分配device,device对象获取fd的所有权
int32_t deviceId = mNextDeviceId++;//mNextDeviceId初始值为1,故deviceId=1
Device* device = new Device(fd, deviceId, String8(devicePath), identifier);
//参数分析:
//此处结合是getevent工具分析的
//fd是dev/input/event0的设备的描述符
//deviceid=1
//devicePath=dev/input/event0
//identifier存储了此设备所有的厂商信息
loadConfigurationLocked(device);//加载device的配置文件。主要是例如:device.type和device.internal等信息
// 详细事件报告的类型,从fd对应的device中读取各种信息,并保存到device的keyBitmask或absBitmask中,
//例如:如果是键盘设备,则device->keyBitmask有数据,而device->absBitmask无数据
ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(device->keyBitmask)), device->keyBitmask);//按键类型
ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(device->absBitmask)), device->absBitmask);//绝对坐标类型
ioctl(fd, EVIOCGBIT(EV_REL, sizeof(device->relBitmask)), device->relBitmask);//相对坐标类型,只要用于motion
ioctl(fd, EVIOCGBIT(EV_SW, sizeof(device->swBitmask)), device->swBitmask);//开关类型
ioctl(fd, EVIOCGBIT(EV_LED, sizeof(device->ledBitmask)), device->ledBitmask);//led等类型
ioctl(fd, EVIOCGBIT(EV_FF, sizeof(device->ffBitmask)), device->ffBitmask);//力反馈类型
ioctl(fd, EVIOCGPROP(sizeof(device->propBitmask)), device->propBitmask);
//Device结构体的事件位掩码描述了6种类型的输入事件:
//EV_KEY,按键类型的事件。能够上报这类事件的设备有键盘、鼠标、手柄、手写板
//等一切拥有按钮的设备(包括手机上的实体按键)。在Device结构体中,对应的事件
//位掩码keyBitmask描述了设备可以产生的按键事件的集合。按键事件的全集包括字
//符按键、方向键、控制键、鼠标键、游戏按键等。
//EV_ABS,绝对坐标类型的事件。这类事件描述了在空间中的一个点,触控板、触
//摸屏等使用绝对坐标的输入设备可以上报这类事件。事件位掩码absBitmask描述了
//设备可以上报的事件的维度信息(ABS_X、ABS_Y、ABS_Z),以及是否支持多点
//事件。
//EV_REL,相对坐标类型的事件。这类事件描述了事件在空间中相对于上次事件
//的偏移量。鼠标、轨迹球等基于游标指针的设备可以上报此类事件。事件位掩码
relBitmask描述了设备可以上报的事件的维度信息(REL_X、REL_Y、REL_Z)。
//EV_SW,开关类型的事件。这类事件描述了若干固定状态之间的切换。手机上的静
//音模式开关按钮、模式切换拨盘等设备可以上报此类事件。事件位掩码swBitmask表
//示了设备可切换的状态列表。
//EV_LED,光反馈类型事件。ledBitmask描述了设备是否支持光
//EV_FF,力反馈类型,ffBitmask则描述了设备是否支持力反馈。
//INPUT_DEVICE_CLASS_KEYBOARD,可以上报鼠标按键以外的EV_KEY类型事件的设备都属于此类。如键
//盘、机身按钮(音量键、电源键等)。
//INPUT_DEVICE_CLASS_ALPHAKEY,可以上报字符按键的设备,例如键盘。此类型的设备必定同时属于KEYBOARD。
//INPUT_DEVICE_CLASS_DPAD,可以上报方向键的设备。例如键盘、手机导航键等。这类设备同时也属于KEYBOARD。
//INPUT_DEVICE_CLASS_GAMEPAD,可以上报游戏按键的设备,如游戏手柄。这类设备同时也属于KEYBOARD。
//INPUT_DEVICE_CLASS_TOUCH,可以上报EV_ABS类型事件的设备都属于此类,如触摸屏和触控板。
//INPUT_DEVICE_CLASS_TOUCH_MT,可以上报EV_ABS类型事件,并且其事件位掩码指示其支持多点事件
//的设备属于此类。例如多点触摸屏。这类设备同时也属于TOUCH类型。
//INPUT_DEVICE_CLASS_CURSOR,可以上报EV_REL类型的事件,并且可以上报BTN_MOUSE子类的
//EV_KEY事件的设备属于此类,例如鼠标和轨迹球。
//INPUT_DEVICE_CLASS_SWITCH,可以上报EV_SW类型事件的设备。
//INPUT_DEVICE_CLASS_JOYSTICK,属于GAMEPAD类型,并且属于TOUCH类型的设备。
//INPUT_DEVICE_CLASS_VIBRATOR,支持力反馈的设备。
//INPUT_DEVICE_CLASS_VIRTUAL,虚拟设备。
//INPUT_DEVICE_CLASS_EXTERNAL,外部设备,即非内建设备。如外接鼠标、键盘、游戏手柄等。
//通过查看device->keyBitmask是否有数据,查看此设备是不是键盘类型
bool haveKeyboardKeys = containsNonZeroByte(device->keyBitmask, 0, sizeof_bit_array(BTN_MISC))
|| containsNonZeroByte(device->keyBitmask, sizeof_bit_array(KEY_OK),
sizeof_bit_array(KEY_MAX + 1));
bool haveGamepadButtons = containsNonZeroByte(device->keyBitmask, sizeof_bit_array(BTN_MISC),
sizeof_bit_array(BTN_MOUSE))
|| containsNonZeroByte(device->keyBitmask, sizeof_bit_array(BTN_JOYSTICK),
sizeof_bit_array(BTN_DIGI));
if (haveKeyboardKeys || haveGamepadButtons) {//如果是键盘类型或者游戏按键类型,
//则设置device->classes为INPUT_DEVICE_CLASS_KEYBOARD
device->classes |= INPUT_DEVICE_CLASS_KEYBOARD;
}
//通过查看device->keyBitmask中是否有BTN_MOUSE对应的比特位和device->relBitmask是否对应的REL_X和REL_Y比特位,确认这是不是一个光标设备,如轨迹球或鼠标。
if (test_bit(BTN_MOUSE, device->keyBitmask)
&& test_bit(REL_X, device->relBitmask)
&& test_bit(REL_Y, device->relBitmask)) {
device->classes |= INPUT_DEVICE_CLASS_CURSOR;
}
// 查看是否是旋转编码器类型的设备.
String8 deviceType = String8();
if (device->configuration &&
device->configuration->tryGetProperty(String8("device.type"), deviceType)) {
if (!deviceType.compare(String8("rotaryEncoder"))) {
device->classes |= INPUT_DEVICE_CLASS_ROTARY_ENCODER;
}
}
//通过查看device->absBitmask,查看这是不是触摸板。
if (test_bit(ABS_MT_POSITION_X, device->absBitmask)
&& test_bit(ABS_MT_POSITION_Y, device->absBitmask)) {
if (test_bit(BTN_TOUCH, device->keyBitmask) || !haveGamepadButtons) {//尝试确认设备确实触摸屏。
device->classes |= INPUT_DEVICE_CLASS_TOUCH | INPUT_DEVICE_CLASS_TOUCH_MT;
}
// 查看这是不是旧式的单触的驱动
} else if (test_bit(BTN_TOUCH, device->keyBitmask)
&& test_bit(ABS_X, device->absBitmask)
&& test_bit(ABS_Y, device->absBitmask)) {
device->classes |= INPUT_DEVICE_CLASS_TOUCH;
// 查看不是外部手写笔
} else if ((test_bit(ABS_PRESSURE, device->absBitmask) ||
test_bit(BTN_TOUCH, device->keyBitmask))
&& !test_bit(ABS_X, device->absBitmask)
&& !test_bit(ABS_Y, device->absBitmask)) {
device->classes |= INPUT_DEVICE_CLASS_EXTERNAL_STYLUS;
device->classes &= ~INPUT_DEVICE_CLASS_KEYBOARD;//有的事件键盘会和手写笔要配合执行一些操作。
}
//查看这个设备是否是操纵杆。
//假设操纵杆总是有游戏板按钮,以便与其他设备(如也有绝对轴的加速计)区分开来。
if (haveGamepadButtons) {
uint32_t assumedClasses = device->classes | INPUT_DEVICE_CLASS_JOYSTICK;
for (int i = 0; i <= ABS_MAX; i++) {
if (test_bit(i, device->absBitmask)
&& (getAbsAxisUsage(i, assumedClasses) & INPUT_DEVICE_CLASS_JOYSTICK)) {
device->classes = assumedClasses;
break;
}
}
}
//查看此device是否有开关
for (int i = 0; i <= SW_MAX; i++)