IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> 安卓IMS 原理解析(二、IMS之InputReader事件获取) -> 正文阅读

[移动开发]安卓IMS 原理解析(二、IMS之InputReader事件获取)

目录

1.EventHub->getEvents()

2.processEventsLocked()

3.mQueuedListener->flush()


前面讲到,在start()后会启动InputThread线程不断的从EventHub中抽取原始输入事件并进行加工处理,代码如下:

frameworks/native/services/inputflinger/reader/InputReader.cpp

status_t InputReader::start() {
    if (mThread) {
        return ALREADY_EXISTS;
    }
    mThread = std::make_unique<InputThread>(
            "InputReader", [this]() { loopOnce(); }, [this]() { mEventHub->wake(); });
    return OK;
}

threadLoop中执行的是loopOnce函数
frameworks/native/services/inputflinger/reader/InputReader.cpp

void InputReader::loopOnce() {
	...
    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE); //1

    { // acquire lock
       std::scoped_lock _l(mLock);
       mReaderIsAliveCondition.notify_all();

       if (count) {
           processEventsLocked(mEventBuffer, count); //2
     }
       ...
         mQueuedListener->flush();//3
}

InputReader一次线程循环,即执行一次loopOnce(),主要执行了三项工作:

注释一:从EventHub中获取未处理的事件列表,读取的结果存储在参数mEventBuffer中,返回值表示事件的个数;当EventHub中无事件可抽取时,此函数的调用将会阻塞直到事件到来或者超时;事件信息主要有两种,一种是设备节点的增删事件:如添加/移除设备的事件,一种是原始输入事件:如触摸屏幕或者按物理按键触发的事件。

注释二:通过processEventsLocked()对事件进行处理(两种事件都处理),对于原始输入事件,进行转译、封装与加工后将结果暂存到mQueuedListener中;

注释三:发布事件,所有事件处理完毕后,调用mQueuedListener.flush()将所有暂存的输入事件一次性地交付给InputDispatcher;

接下来对这三件事逐一分析:

1.EventHub->getEvents()

调用EventHub的getEvents()方法来获取事件列表,EventHub是如何工作的呢?

EventHub的直译是事件集线器,它将所有的输入事件通过一个接口getEvents()把从多个输入设备节点中读取的事件交给InputReader,它是输入系统最底层的一个组件,使用了INotify与Epoll两套机制,通过构造方法就可以看到:

/frameworks/native/services/inputflinger/reader/EventHub.cpp

283  EventHub::EventHub(void)
284        : mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD),
285          mNextDeviceId(1),
286          mControllerNumbers(),
287          mOpeningDevices(nullptr),
288          mClosingDevices(nullptr),
289          mNeedToSendFinishedDeviceScan(false),
290          mNeedToReopenDevices(false),
291          mNeedToScanDevices(true),
292          mPendingEventCount(0),
293          mPendingEventIndex(0),
294          mPendingINotify(false) {
295      ensureProcessCanBlockSuspend();

296  //1.使用epoll_create()函数创建一个epoll对象。EPOLL_SIZE_HINT指定最大监听个数为8,这个 
     // epoll对象将用来监听设备节点是否有数据可读(有无事件)
297      mEpollFd = epoll_create1(EPOLL_CLOEXEC);
298      LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance: %s", strerror(errno));
299      
         //2.创建一个inotify对象。这个inotify对象将被用来监听设备节点的增删事件
300      mINotifyFd = inotify_init();

     //将存储设备节点的路径/dev/input作为监听对象添加到inotify对象中。当此文件夹下的设备节点 
     //发生创建与删除事件时,
     //都可以通过mINotifyFd读取事件的详细信息
301      mInputWd = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);
302      LOG_ALWAYS_FATAL_IF(mInputWd < 0, "Could not register INotify for %s: %s", DEVICE_PATH,
303                          strerror(errno));
304      if (isV4lScanningEnabled()) {
305          mVideoWd = inotify_add_watch(mINotifyFd, VIDEO_DEVICE_PATH, IN_DELETE | IN_CREATE);
306          LOG_ALWAYS_FATAL_IF(mVideoWd < 0, "Could not register INotify for %s: %s",
307                              VIDEO_DEVICE_PATH, strerror(errno));
308      } else {
309          mVideoWd = -1;
310          ALOGI("Video device scanning disabled");
311      }


    //3.将mINotifyFd作为epoll的一个监控对象。当inotify事件到来时,epoll_wait()将立即返回,
    //EventHub便可以从mINotifyFd中读取设备节点的增删信息,并进行相应处理
313      struct epoll_event eventItem = {};
314      eventItem.events = EPOLLIN | EPOLLWAKEUP;
315      eventItem.data.fd = mINotifyFd;
       //将对mINotifyFd的监听注册到epoll对象中
316      int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);
317      LOG_ALWAYS_FATAL_IF(result != 0, "Could not add INotify to epoll instance.  errno=%d", errno);
318      //创建一个名为wakeFds的匿名管道
319      int wakeFds[2];
320      result = pipe(wakeFds);
321      LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe.  errno=%d", errno);
322  
323      mWakeReadPipeFd = wakeFds[0];
324      mWakeWritePipeFd = wakeFds[1];
325  
326      result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);
327      LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake read pipe non-blocking.  errno=%d",
328                          errno);
329  
330      result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK);
331      LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake write pipe non-blocking.  errno=%d",
332                          errno);
333  
334      eventItem.data.fd = mWakeReadPipeFd;
    //将管道读取端的描述符的可读事件注册到epoll对象中。因为InputReader在执行getEvents()时会因无事件而导致其线程阻塞在
    //epoll_wait()的调用里,然而有时希望能够立刻唤醒InputReader线程使其处理一些请求。此时只需向wakeFds管道的写入端写入任意数据,
    //此时读取端有数据可读,使得epoll_wait()得以返回,从而达到唤醒InputReader线程的目的
335      result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem);
336      LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake read pipe to epoll instance.  errno=%d",
337                          errno);
338  }

在上面的构造方法内,对Epoll和INotify进行处理,后续有事件到来时就可以获取了,反正知道会监视目录文件? /dev/input就可以了。接下来看一下getEvents()的逻辑处理:

/frameworks/native/services/inputflinger/reader/EventHub.cpp

size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
    .......
    for (;;) {
        .......
        //处理未被InputReader取走的输入事件与设备事件。epoll_wait()所取出的epoll_event存储在mPendingEventItems中,
        //mPendingEventCount指定mPendingEventItems数组所存储的事件个数。而mPendingEventIndex指定尚未处理的epoll_event的索引
        while (mPendingEventIndex < mPendingEventCount) {
            const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];
            //在这里分析每一个epoll_event 如果表示设备节点可读,则读取原始事件并放置到buffer中。
            //如果表示mINotifyEd可读,则设置mPendingINotify为true,当InputReader将现有的输入事件都取出后读取mINotifyEd中的事件,并加装与卸载相应的设备。
            //另外,如果此epoll_event表示wakeFds的读取端有数据可读,则设置awake标志为true,
            //此时无论此次getEvents()调用是否取到事件,都不会调用epoll_wait()进行事件等待。
            if (eventItem.data.u32 == EPOLL_ID_INOTIFY) {
                if (eventItem.events & EPOLLIN) {
                    mPendingINotify = true;
                }
                continue;
            }

            if (eventItem.data.u32 == EPOLL_ID_WAKE) {
                if (eventItem.events & EPOLLIN) {
                    char buffer[16];
                    ssize_t nRead;
                    do {
                        nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer));
                    } while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer));
                }
            }
    }
       .....
       //如果此次getEvents()调用成功获取了一些事件,或者要求唤醒InputReader,则退出循环并结束getEvents()的调用,
       //使InputReader可以立刻处理事件
       if (event != buffer || awoken) {
           break;
       }

        //如果此次getEvents()调用没能获取事件,说明mPendingEventItems中没有事件可用。
        //于是执行epoll_wait()函数等待新的事件到来,将结果存储到mPendingEventItems里,并重置mPendingEventIndex为0
        mPendingEventIndex = 0;
        int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);

        if (pollResult == 0) {
            // Timed out.
            mPendingEventCount = 0;
            break;
        }

        if (pollResult < 0) {
            // An error occurred.
            mPendingEventCount = 0;
            if (errno != EINTR) {
                ALOGW("poll failed (errno=%d)\n", errno);
                usleep(100000);
            }
        } else {
            //从epoll_wait()中得到新的事件后,重新循环,对新事件进行处理
            mPendingEventCount = size_t(pollResult);
        }
    }

    //返回本次getEvents()调用所读取的事件数量
    return event - buffer;
}

?getEvents()函数的本质就是读取并处理Epoll事件与INotify事件:包括设备插拔及各种触摸、按钮事件等,可以看做是一个不同设备的集线器,主要面向的是/dev/input目录下的设备节点,比如说/dev/in,通过EventHubput/event0上的事件就是输入事件的getEvents()就可以监听并获取该事件,getEvents()将它们封装为RawEvent结构体,并放入mEventBuffer中供InputReader进行处理。如果没有输入事件将调用epoll_wait()函数阻塞等待。

2.processEventsLocked()

?当通过EventHub获取到事件即count>0时,对事件进行加工处理,此时调用到processEventsLocked()方法:

frameworks/native/services/inputflinger/reader/InputReader.cpp

void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {
    for (const RawEvent* rawEvent = rawEvents; count;) {
        int32_t type = rawEvent->type;
        size_t batchSize = 1;
        if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) { //1
            int32_t deviceId = rawEvent->deviceId;
            while (batchSize < count) {
                if (rawEvent[batchSize].type >= EventHubInterface::FIRST_SYNTHETIC_EVENT ||
                    rawEvent[batchSize].deviceId != deviceId) {
                    break;
                }
                batchSize += 1;
            }
            if (DEBUG_RAW_EVENTS) {
                ALOGD("BatchSize: %zu Count: %zu", batchSize, count);
            }
            processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
        } else {
            switch (rawEvent->type) { //2
                case EventHubInterface::DEVICE_ADDED://增加设备
                    addDeviceLocked(rawEvent->when, rawEvent->deviceId);
                    break;
                case EventHubInterface::DEVICE_REMOVED://移除设备
                    removeDeviceLocked(rawEvent->when, rawEvent->deviceId);
                    break;
                case EventHubInterface::FINISHED_DEVICE_SCAN://扫描设备结束
                    handleConfigurationChangedLocked(rawEvent->when);
                    break;
                default:
                    ALOG_ASSERT(false); // can't happen
                    break;
            }
        }
        count -= batchSize;
        rawEvent += batchSize;
    }
}

processEventsLocked()函数中会遍历mEventBuffer中存储的输入事件,处理rawEvent分成两类:注释1处理原始输入事件,注释2处理设备事件。

1.先看处理设备事件:

对于添加、删除设备的event事件处理比较简单,只需要增加和删除mDevices列表中Device对象就完成了,而对于扫描设备结束的Event还需调用handleConfigurationChangedLocked()函数来处理设备的配置文件。mDevices的定义,是一个map集合,保存着eventHubId和InputDevice对象

2.对于原始输入事件:

调用processEventsForDeviceLocked函数进行处理代码如下:

frameworks/native/services/inputflinger/reader/InputReader.cpp

269  void InputReader::processEventsForDeviceLocked(int32_t eventHubId, const RawEvent* rawEvents,
270                                                 size_t count) {
271      auto deviceIt = mDevices.find(eventHubId);
272      if (deviceIt == mDevices.end()) {
273          ALOGW("Discarding event for unknown eventHubId %d.", eventHubId);
274          return;
275      }
276  
277      std::shared_ptr<InputDevice>& device = deviceIt->second;
278      if (device->isIgnored()) {
279          // ALOGD("Discarding event for ignored deviceId %d.", deviceId);
280          return;
281      }
282  
283      device->process(rawEvents, count);
284  }

注释1处根据eventHubId查询map集合中是否有该key,如果没有则返回,有则在注释2处将value值取出,是InputDevice类型的。在注释3处调用它的process函数执行处理

frameworks/native/services/inputflinger/reader/InputDevice.cpp

void InputDevice::process(const RawEvent* rawEvents, size_t count) {
    // Process all of the events in order for each mapper.
    // We cannot simply ask each mapper to process them in bulk because mappers may
    // have side-effects that must be interleaved.  For example, joystick movement events and
    // gamepad button presses are handled by different mappers but they should be dispatched
    // in the order received.
    for (const RawEvent* rawEvent = rawEvents; count != 0; rawEvent++) {
        if (DEBUG_RAW_EVENTS) {
            ALOGD("Input event: device=%d type=0x%04x code=0x%04x value=0x%08x when=%" PRId64,
                  rawEvent->deviceId, rawEvent->type, rawEvent->code, rawEvent->value,
                  rawEvent->when);
        }

        if (mDropUntilNextSync) { //1
            if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
                mDropUntilNextSync = false;
                if (DEBUG_RAW_EVENTS) {
                    ALOGD("Recovered from input event buffer overrun.");
                }
            } else {
                if (DEBUG_RAW_EVENTS) {
                    ALOGD("Dropped input event while waiting for next input sync.");
                }
            }
        } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) {
            ALOGI("Detected input event buffer overrun for device %s.", getName().c_str());
            mDropUntilNextSync = true;
            reset(rawEvent->when);
        } else {
            for_each_mapper_in_subdevice(rawEvent->deviceId, [rawEvent](InputMapper& mapper) {
                mapper.process(rawEvent); //2
            });
        }
        --count;
    }
}

注释1处的mDropUntilNextSync的值默认为false,如果设备的输入缓冲区溢出,该值会被设置为true,如果恢复,会重新置为false。原始输入事件类型有很多,如键盘输入事件、触摸输入事件等,这些类别都会由单独的InputMapper的子类来进行处理。

在这里插入图片描述

?注释2处遍历所有的Mapper类型,选择合适的Mapper处理事件,for_each_mapper_in_subdevice是一个内联函数,用于遍历所有的Mapper,InputReader将输入事件交由合适的InputMapper处理,至于是哪个InputMapper,InputReader并不关心。这里以键盘输入事件为例,执行的是KeyboardInputMapper的process函数:

frameworks/native/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp

void KeyboardInputMapper::process(const RawEvent* rawEvent) {
    switch (rawEvent->type) {
        case EV_KEY: { //1按键事件的处理
            int32_t scanCode = rawEvent->code;
            int32_t usageCode = mCurrentHidUsage;
            mCurrentHidUsage = 0;

            if (isKeyboardOrGamepadKey(scanCode)) {
                processKey(rawEvent->when, rawEvent->readTime, rawEvent->value != 0, scanCode,
                           usageCode); //2
            }
            break;
        }
        case EV_MSC: { //3其他事件的处理
            if (rawEvent->code == MSC_SCAN) {
                mCurrentHidUsage = rawEvent->value;
            }
            break;
        }
        case EV_SYN: { //4同步事件的处理
            if (rawEvent->code == SYN_REPORT) {
                mCurrentHidUsage = 0;
            }
        }
    }
}

注释3处为其他事件的处理,注释4处为同步事件的处理。主要还是关注注释1处,按键类型事件,调用注释2处processKey函数进行处理。

frameworks/native/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp

void KeyboardInputMapper::processKey(nsecs_t when, nsecs_t readTime, bool down, int32_t scanCode,
                                     int32_t usageCode) {
	...
	NotifyKeyArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
                       getDisplayId(), policyFlags,
                       down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
                       AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime);
  getListener().notifyKey(&args);
}

这里会对键盘输入事件进行加工,并将加工后的数据封装到NotifyKeyArgs类型的对象中。然后调用getListener()函数得到InputListenerInterface类型的对象。InputDispatcherListener继承了InputListenerInterface,而InputDispatcher继承了InputDispatcherListener,所以这里调用的实际是InputDispatcher的notifyKey函数。此处先不分析notifyxx()逻辑,稍后一起分析:

3.mQueuedListener->flush()

???在porceeEventsLocked()后,接下来会执行mQueuedListener->flush()来结束loopOnce()这个方法,mQueuedListener是QueuedInputListener实例,看看里面的逻辑:

/frameworks/native/services/inputflinger/InputListener.cpp

 
326  void QueuedInputListener::notifyConfigurationChanged(
327          const NotifyConfigurationChangedArgs* args) {
328      traceEvent(__func__, args->id);
329      mArgsQueue.push_back(new NotifyConfigurationChangedArgs(*args));
330  }
331  
332  void QueuedInputListener::notifyKey(const NotifyKeyArgs* args) {
333      traceEvent(__func__, args->id);
334      mArgsQueue.push_back(new NotifyKeyArgs(*args));
335  }
336  
337  void QueuedInputListener::notifyMotion(const NotifyMotionArgs* args) {
338      traceEvent(__func__, args->id);
339      mArgsQueue.push_back(new NotifyMotionArgs(*args));
340  }
341  
342  void QueuedInputListener::notifySwitch(const NotifySwitchArgs* args) {
343      traceEvent(__func__, args->id);
344      mArgsQueue.push_back(new NotifySwitchArgs(*args));
345  }
346  
347  void QueuedInputListener::notifySensor(const NotifySensorArgs* args) {
348      traceEvent(__func__, args->id);
349      mArgsQueue.push_back(new NotifySensorArgs(*args));
350  }
351  
352  void QueuedInputListener::notifyVibratorState(const NotifyVibratorStateArgs* args) {
353      traceEvent(__func__, args->id);
354      mArgsQueue.push_back(new NotifyVibratorStateArgs(*args));
355  }
356  
357  void QueuedInputListener::notifyDeviceReset(const NotifyDeviceResetArgs* args) {
358      traceEvent(__func__, args->id);
359      mArgsQueue.push_back(new NotifyDeviceResetArgs(*args));
360  }
361  
362  void QueuedInputListener::notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) {
363      traceEvent(__func__, args->id);
364      mArgsQueue.push_back(new NotifyPointerCaptureChangedArgs(*args));
365  }
366  
367  void QueuedInputListener::flush() {
368      size_t count = mArgsQueue.size();
369      for (size_t i = 0; i < count; i++) {
370          NotifyArgs* args = mArgsQueue[i];
371          args->notify(mInnerListener);
372          delete args;
373      }
374      mArgsQueue.clear();
375  }
376  
377  }

可以看到,在第二项工作中调用notifyKey()后,执行了队列的push,只是将该事件args压入队列中;最后通过flush()来遍历队列,执行对应args的notify()方法.........(省略一万字)。

?????????QueuedInputListener避免了在原始事件的加工过程中向InputDispatcher进行事件提交,而是将事件信息缓存起来,在InputReader::loopOnce()函数的末尾,也就是InputReader处理完抽取自EventHub的所有原始输入事件之后,QueuedInputListener::flush()函数的调用将缓存的事件信息取出,并提交给InputDispatcher,事件派发就由此开始了。

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-10-22 21:26:57  更:2022-10-22 21:30:06 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/19 22:40:00-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码