• Android 11 inputflinger分析(触摸优先级)


    Android11 touch的传递流程主要涉及到以下内容:

    1. ViewRootImpl 注册InputChannel 2
    2. InputFlinger 从kernel读取touch数据(重点)
    3. InputFlinger把touch数据发送到View(重点)
    4. ViewRootImp接收touch数据发送到app

    概括:app往wms那边添加窗口的同时,wms创建一对socket pair,用InputChannel封装,一个给app,一个给InputFlinger,之后InputFlinger通过这个unix socket fd把touch数据发给app。
    此文主要介绍InputFlinger其它内容请看,https://blog.csdn.net/goodnight1994/article/details/119328739

    inputflinger 介绍

    在Android系统中,framwork/native/service/inputflinger会进行framwork层各种输入事件(按键,触摸等)的收集、处理、和分发。

    inputflinger 主要组件

    InputManger.cpp是inputflinger的入口类。也就是inputflinger第一个执行的文件。
    在这里插入图片描述
    结合上图可以看出inputflinger主要包括两个很重要的类,来完成所有事件的读取和派发

    1. InputReader: 这个类主要从input读取所有设备产生的input事件(包括,单点触摸,多点触摸,按键,电磁笔等)
    2. InputDispatcher :这个类负责事件的分发工作。

    它们对应两个干活的线程(也是两个类):
    InputReaderThread :这个线程负责不停的调用inputReader事件,进行数据的收集。
    InuptDispatcherThread: 这个线程会不停的轮训事件队列是否有数据需要处理。

    详细解析:

    1 首先系统启动以后,上面两个线程就跑起来了,而且会一直跑。

    看一下`InputReaderThread`:
    
    	bool InputReaderThread::threadLoop() {
        mReader->loopOnce();
        return true;
       }
    

    2 这个loopOnce()这个函数(也是一直在运行的)就在InputReader.cpp这个类里面,这就是InputReader从kernel读出数据的流程也是接收读取输入事件的一个主要的函数,如下所示:

    在这里插入图片描述
    可以明显的看出,这个函数比较重要的作用有两个:

    1. 通过EvenetHub获取输入的事件,将输入事件存到保存输入事件的Buffer里面,并返回输入事件的个数。
      EventHub 解析:
      (1)EventHub 构造函数 在这里插入图片描述
      解释:这里通过inotify的方式监听device/input ,然后把inotify添加到epoll上,利用epoll比轮询效率更高。
      (2)EventHub核心代码
      int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
      //监听并等待事件到来(这就是上面添加到epoll_wait)
      
      在这里插入图片描述
      这就是主要的读取device设备按键事件或者输入事件的核心代码
    2. 如果有输入事件(count!=0),那么就调用processEvenetLocked()进行事件原始的处理,这样上层才能更好的使用。比如多点触摸,电磁触摸,单点触摸等
      processEventslocked()解析: 在这里插入图片描述
        void InputReader::processEventsForDeviceLocked(int32_t deviceId,
            const RawEvent* rawEvents, size_t count) {
        ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
        if (deviceIndex < 0) {
            ALOGW("Discarding event for unknown deviceId %d.", deviceId);
            return;
        }
    
        InputDevice* device = mDevices.valueAt(deviceIndex);
        if (device->isIgnored()) {
            //ALOGD("Discarding event for ignored deviceId %d.", deviceId);
            return;
        }
    
        device->process(rawEvents, count);
        //这里就是对不同设备的事件进行不同的处理了,在不同的android版本上这里的实现也不太一样
    }
    

    3 InputDispatcher

    //frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
    status_t InputDispatcher::start() {
        if (mThread) {
            return ALREADY_EXISTS;
        }
        mThread = std::make_unique<InputThread>(
                "InputDispatcher", [this]() { dispatchOnce(); }, [this]() { mLooper->wake(); });
        return OK;
    }
     
    //创建了一个线程去跑dispatchOnce()函数,
    //没事件时睡下去,有事件时被前面的入队事件唤醒后开始工作
    void InputDispatcher::dispatchOnce() {
    //.......
         dispatchOnceInnerLocked(&nextWakeupTime);
    //.......
    }
     
    //接着看
    void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
    //.......
        done = dispatchMotionLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
    //......
    }
     
    bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, MotionEntry* entry,
                                               DropReason* dropReason, nsecs_t* nextWakeupTime) {
    //.......                                           
       //TODO 这边会去选出到底是要把touch数据给到哪个View,选完装到inputTargets里边
       //具体咋选的后面再单独研究下
       injectionResult =
             findTouchedWindowTargetsLocked(currentTime, *entry, inputTargets, nextWakeupTime,
                                                   &conflictingPointerActions); 
    //......   
       //选完View后,接着继续发数据
       dispatchEventLocked(currentTime, entry, inputTargets);                                        
    //......                                           
    }
     
    void InputDispatcher::dispatchEventLocked(nsecs_t currentTime, EventEntry* eventEntry,
                                              const std::vector<InputTarget>& inputTargets) {
    //.......
        //根据inputTarget取出前面note13 注册的connection,其中里边包含用来和View通信的InputChannel
        sp<Connection> connection =
               getConnectionLocked(inputTarget.inputChannel->getConnectionToken());
               
        prepareDispatchCycleLocked(currentTime, connection, eventEntry, inputTarget);
                                               
    //.......                                          
    }
     
    void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
                                                     const sp<Connection>& connection,
                                                     EventEntry* eventEntry,
                                                     const InputTarget& inputTarget) {
     //.......                                                
         //从这进去,接着看
         enqueueDispatchEntriesLocked(currentTime, connection, splitMotionEntry, inputTarget);                                                
    //......                                                 
    }
     
    void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime,
                                                       const sp<Connection>& connection,
                                                       EventEntry* eventEntry,
                                                       const InputTarget& inputTarget) {
     //.......                                                  
         //从这进去                                          
         startDispatchCycleLocked(currentTime, connection);                                                  
    }
     
    void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
                                                   const sp<Connection>& connection) {
    //......                                               
        status =connection->inputPublisher.publishKeyEvent(dispatchEntry->seq, dispatchEntry->resolvedEventId,
                                                     keyEntry->deviceId, keyEntry->source,
                                                     keyEntry->displayId, std::move(hmac),
                                                     dispatchEntry->resolvedAction,
                                                     dispatchEntry->resolvedFlags, keyEntry->keyCode,
                                                     keyEntry->scanCode, keyEntry->metaState,
                                                     keyEntry->repeatCount, keyEntry->downTime,
                                                     keyEntry->eventTime);
    //....
    }
     
    //走到InputPublisher去了,看下
    //frameworks/native/libs/input/InputTransport.cpp
    status_t InputPublisher::publishKeyEvent(uint32_t seq, int32_t eventId, int32_t deviceId,
                                             int32_t source, int32_t displayId,
                                             std::array<uint8_t, 32> hmac, int32_t action,
                                             int32_t flags, int32_t keyCode, int32_t scanCode,
                                             int32_t metaState, int32_t repeatCount, nsecs_t downTime,
                                             nsecs_t eventTime) {
    //.......                                         
         //好了到底了,这边通过InputChannel用unix socket把数据发给View
         return mChannel->sendMessage(&msg);
    }                                      
     
    //至于这把mChannel是怎么来的为何是前面app通过wms传过来的InputChannel,
    //咱们从note13  再看下
    //frameworks/native/services/inputflinger/dispatcher/Connection.cpp
    Connection::Connection(const sp<InputChannel>& inputChannel, bool monitor,
                           const IdGenerator& idGenerator)
          : status(STATUS_NORMAL),
            inputChannel(inputChannel),
            monitor(monitor),
            inputPublisher(inputChannel),
            inputState(idGenerator) {}
     
    //frameworks/native/libs/input/InputTransport.cpp
    InputPublisher::InputPublisher(const sp<InputChannel>& channel) :
            mChannel(channel) {
    }
    

    主要就是从队列里面去读取并把它们分发出去。

    最后附上一张解析图可以看一下inputflinger 流程解析巩固之前的东西

    在这里插入图片描述

    inputflinger 示例(电磁笔优先)

    我自己主要是项目需求做了一个优先级的需求,就是在手指触摸和电磁笔同时进行输入时,让电磁笔优先进行输入。
    主要的思路就是:
    在inputReader.cpp处理事件里
    1 设置一个static的clock,去进行时间计数,在该时间内关闭其它输入只允许电磁笔输入
    2 根据vendorid和productid去过滤相关的事件。只上报电磁笔产生的事件。从而实现输入过滤。

  • 相关阅读:
    vasp频率计算出错Intel MKL error
    Linux 中的 chroot 命令及示例
    “Flex弹性布局、轮播图mock遍历数据和首页布局解析与实践“
    【网络安全】XSS跨站脚本攻击专题讲解
    【LeetCode热题100】--74.搜索二维矩阵
    YOLO目标检测——烟雾检测数据集下载分享【含对应voc、coco和yolo三种格式标签】
    Redux-状态管理组件
    django3.2.14之docker下主从分离【亲测可用】
    vs2019+boost库(boost_1_67_0)安装
    SpringBoot整合Redis,缓存批量删除 redisTemplate.keys(pattern)模糊查询找不到keys,“ “ 通配符无效
  • 原文地址:https://blog.csdn.net/yingLGG/article/details/126375622