【android 9】【input】【5.设备的增删事件】

系列文章目录

可跳转到下面链接查看下表所有内容https://blog.youkuaiyun.com/handsomethefirst/article/details/138226266?spm=1001.2014.3001.5501文章浏览阅读2次。系列文章大全https://blog.youkuaiyun.com/handsomethefirst/article/details/138226266?spm=1001.2014.3001.5501


目录

系列文章目录

1.简介

2.设备的添加

2.1 流程介绍

2.2 时序图

2.3 全源码分析

2.3.1 前情回顾

2.3.2 /dev/input设备节点的监听

 2.3.3 inputreader线程阻塞等待设备添加事件发生

2.3.4 当设备添加时

2.3.5 EventHub::readNotifyLocked

 2.3.6 openDeviceLocked

2.3.7 registerDeviceForEpollLocked

 2.3.8 addDeviceLocked

 2.3.9 EventHub::getEvents的第三次for循环

 2.3.10 InputReader::loopOnce

 2.3.11 processEventsLocked

 2.3.12 addDeviceLocked

2.3.13 createDeviceLocked

2.3.14 configure

2.3.15 handleConfigurationChangedLocked

2.3.16 notifyInputDevicesChanged

 2.3.17 QueuedInputListener::flush

2.3.18 NotifyConfigurationChangedArgs::notify

 2.3.19 notifyConfigurationChanged

2.3.20 dispatchOnce

 2.3.21 dispatchOnceInnerLocked

2.3.22 dispatchConfigurationChangedLocked

 2.3.23 runCommandsLockedInterruptible

2.3.24 doNotifyConfigurationChangedInterruptible

 2.3.25  notifyConfigurationChanged

 2.3.26 notifyConfigurationChanged

2.4 总结

3.设备的删除

3.1 流程介绍

3.2 时序图

3.3 全源码分析

3.3.1 前情回顾

3.3.2 /dev/input设备节点的监听

 3.3.3 inputreader线程阻塞等待设备删除事件发生

3.3.4 当设备删除时

3.3.5 EventHub::readNotifyLocked

 3.3.6 closeDeviceByPathLocked

 3.3.7 getDeviceByPathLocked

 3.3.8 closeDeviceLocked

3.3.9 unregisterDeviceFromEpollLocked

3.3.10 releaseControllerNumberLocked

 3.3.11 close

 3.3.12 第三次for循环

 3.3.13 loopOnce

 3.3.14 processEventsLocked

 3.3.15 removeDeviceLocked

 3.3.16 reset

 3.3.17 notifyReset

 3.3.18 handleConfigurationChangedLocked

3.3.19 notifyInputDevicesChanged

3.3.20 flush

 3.3.21 NotifyDeviceResetArgs::notify

 3.3.22 notifyDeviceReset

 3.3.23 dispatchOnce

3.3.24 dispatchOnceInnerLocked

3.3.25 dispatchDeviceResetLocked

3.3.26 synthesizeCancelationEventsForAllConnectionsLocked

3.3.27 情况一

3.3.28 情况二

3.3.29 设备扫描完成事件

3.4 总结


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++)
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值