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第一个执行的文件。
Android 11 inputflinger分析(触摸优先级)_第1张图片
结合上图可以看出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读出数据的流程也是接收读取输入事件的一个主要的函数,如下所示:

Android 11 inputflinger分析(触摸优先级)_第2张图片
可以明显的看出,这个函数比较重要的作用有两个:

  1. 通过EvenetHub获取输入的事件,将输入事件存到保存输入事件的Buffer里面,并返回输入事件的个数。
    EventHub 解析:
    (1)EventHub 构造函数 Android 11 inputflinger分析(触摸优先级)_第3张图片
    解释:这里通过inotify的方式监听device/input ,然后把inotify添加到epoll上,利用epoll比轮询效率更高。
    (2)EventHub核心代码
    int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
    //监听并等待事件到来(这就是上面添加到epoll_wait)
    
    Android 11 inputflinger分析(触摸优先级)_第4张图片
    这就是主要的读取device设备按键事件或者输入事件的核心代码
  2. 如果有输入事件(count!=0),那么就调用processEvenetLocked()进行事件原始的处理,这样上层才能更好的使用。比如多点触摸,电磁触摸,单点触摸等
    processEventslocked()解析: Android 11 inputflinger分析(触摸优先级)_第5张图片
    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 流程解析巩固之前的东西

Android 11 inputflinger分析(触摸优先级)_第6张图片

inputflinger 示例(电磁笔优先)

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

你可能感兴趣的:(android,java,开发语言,c++,嵌入式)