3【Android 12】InputReader读取事件

在这里插入图片描述

在Input相关服务的创建和启动中,我们知道了InputManager在start函数中会创建一个InputReader,其内部有一个线程会循环调用InputReader.loopOnce,实现InputReader对输入事件的持续读取,那么我们以此为起点,分析一下InputReader读取事件流程。

void InputReader::loopOnce() {
    ......

    // 1. 从EventHub读取原始事件
    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);

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

        // 2. InputReader.processEventsLocked处理事件。
        if (count) {
            processEventsLocked(mEventBuffer, count);
        }

    ......
        
    // 3. QueuedInputListener分发事件
    // Flush queued events out to the listener.
    // This must happen outside of the lock because the listener could potentially call
    // back into the InputReader's methods, such as getScanCodeState, or become blocked
    // on another thread similarly waiting to acquire the InputReader lock thereby
    // resulting in a deadlock.  This situation is actually quite plausible because the
    // listener is actually the input dispatcher, which calls into the window manager,
    // which occasionally calls into the input reader.
    mQueuedListener->flush();
}

可以分为三部分:

1)、EventHub.getEvent获取输入事件。

2)、InputReader.processEventsLocked处理事件。

3)、QueuedInputListener分发事件。

一、从EventHub读取原始事件

    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);

EventHub.getEvents函数内容很庞大,这里先挖个坑暂不分析,总结就是如果此时有输入事件,那么通过EventHub.getEvent来读取事件,将输入事件封装为RawEvent对象。否则,InputReader读取线程就会睡眠在EventHub.getEvent函数上:

        int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);

epoll_wait,等待epoll文件描述符上的I / O事件。

最终将从EventHub中读取的原始事件保存到mEventBuffer中,count代表了原始事件的个数。

二、加工原始事件

Key和Motion分发流程类似,因此在这里以Key事件为例看一下整个流程时序图:

在这里插入图片描述

上一步中将从EventHub中读取到的原始信息保存在了RawEvent类型的mEventBuffer中,接着使用InputReader.processEventsLocked方法进一步处理事件。

1 InputReader.processEventsLocked

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) {
			......
            processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
        } else {
            switch (rawEvent->type) {
                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;
    }
}

这里将普通的事件输入事件和设备的输入事件分开处理:

1)、输入设备事件的定义在EventHub.h:

    // Synthetic raw event type codes produced when devices are added or removed.
    enum {
        // Sent when a device is added.
        DEVICE_ADDED = 0x10000000,
        // Sent when a device is removed.
        DEVICE_REMOVED = 0x20000000,
        // Sent when all added/removed devices from the most recent scan have been reported.
        // This event is always sent at least once.
        FINISHED_DEVICE_SCAN = 0x30000000,

        FIRST_SYNTHETIC_EVENT = DEVICE_ADDED,
    };

DEVICE_ADDED对应一次设备添加事件,DEVICE_REMOVED对应一次设备删除事件,FINISHED_DEVICE_SCAN对应一次所有设备的扫描事件。

2)、如果事件type小于FIRST_SYNTHETIC_EVENT,那么此次不是一次增加/删除/扫描设备事件,调用InputReader.processEventsForDeviceLocked,让对应设备去处理本次输入事件。

2 InputReader.processEventsForDeviceLocked

void InputReader::processEventsForDeviceLocked(int32_t eventHubId, const RawEvent* rawEvents,
                                               size_t count) {
    auto deviceIt = mDevices.find(eventHubId);
	......

    std::shared_ptr<InputDevice>& device = deviceIt->second;
    ......

    device->process(rawEvents, count);
}

从mDevices中获取当前事件对应的设备对象InputDevice,然后调用InputDevice.process处理RawEvent。

3 InputDevice.process

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 (mDropUntilNextSync) {
            ......
        } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) {
            ......
        } else {
            for_each_mapper_in_subdevice(rawEvent->deviceId, [rawEvent](InputMapper& mapper) {
                mapper.process(rawEvent);
            });
        }
        --count;
    }
}

这里出现了一个新概念,InputMapper:

 /* An input mapper transforms raw input events into cooked event data.
 * A single input device can have multiple associated input mappers in order to interpret
 * different classes of events.

InputMapper用来将原始的输入事件转换为处理过的输入数据,一个输入设备可能对应多个InputMapper。

InputMapper有多个子类,TouchInputMapper、KeyboardInputMapper、SensorInputMapper等等,用来处理不同类型的输入事件。

上一步中,我们找到了一个输入设备可以处理当前输入事件,那么这一步就是遍历调用该InputDevice对应的所有InputMapper,调用InputMapper的proces函数,去处理输入事件。

4 InputMapper处理原始输入事件

我们重点分析键盘事件和触摸屏事件。

4.1 键盘事件

键盘设备对应的InputMapper是KeyboardInputMapper。

4.1.1 KeyboardInputMapper.process
void KeyboardInputMapper::process(const RawEvent* rawEvent) {
    switch (rawEvent->type) {
        case EV_KEY: {
            int32_t scanCode = rawEvent->code;
            int32_t usageCode = mCurrentHidUsage;
            mCurrentHidUsage = 0;

            if (isKeyboardOrGamepadKey(scanCode)) {
                processKey(rawEvent->when, rawEvent->readTime, rawEvent->value != 0, scanCode,
                           usageCode);
            }
            break;
        }
        case EV_MSC: {
            ......
        }
        case EV_SYN: {
            ......
        }
    }
}

查阅资料,Linux系统中总共有这几种输入事件:

EV_SYN 0x00 同步事件

EV_KEY 0x01 按键事件,如KEY_VOLUMEDOWN

EV_REL 0x02 相对坐标, 如shubiao上报的坐标

EV_ABS 0x03 绝对坐标,如触摸屏上报的坐标

EV_MSC 0x04 其它

EV_LED 0x11 LED

EV_SND 0x12 声音

EV_REP 0x14 Repeat

EV_FF 0x15 力反馈

EV_PWR 电源

EV_FF_STATUS 状态

暂不分析其他事件,那么对于键盘事件中的按键事件,调用KeyboardInputMapper.processKey函数继续进行处理。

4.1.2. KeyboardInputMapper.processKey
void KeyboardInputMapper::processKey(nsecs_t when, nsecs_t readTime, bool down, int32_t scanCode,
                                     int32_t usageCode) {
    int32_t keyCode;
    int32_t keyMetaState;
    uint32_t policyFlags;

    ......

    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类型,然后通知listener。

4.2 触摸屏事件

触摸屏设备对应的InputMapper是TouchInputMapper。

4.2.1 TouchInputMapper.process
void TouchInputMapper::process(const RawEvent* rawEvent) {
    mCursorButtonAccumulator.process(rawEvent);
    mCursorScrollAccumulator.process(rawEvent);
    mTouchButtonAccumulator.process(rawEvent);

    if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
        sync(rawEvent->when, rawEvent->readTime);
    }
}

1)、mCursorButtonAccumulator用来处理鼠标和触摸盘按键事件,mCursorScrollAccumulator用来处理鼠标滑动事件,mTouchButtonAccumulator用来处理手写笔和之类的事件。

2)、调用TouchInputMapper.sync函数进行事件的同步。

4.2.2 TouchInputMapper.sync
4.2.3 TouchInputMapper.processRawTouches
4.2.4 TouchInputMapper.cookAndDispatch
4.2.5 TouchInputMapper.dispatchTouches
4.2.6 TouchInputMapper.dispatchMotion
void TouchInputMapper::dispatchMotion(nsecs_t when, nsecs_t readTime, uint32_t policyFlags,
                                      uint32_t source, int32_t action, int32_t actionButton,
                                      int32_t flags, int32_t metaState, int32_t buttonState,
                                      int32_t edgeFlags, const PointerProperties* properties,
                                      const PointerCoords* coords, const uint32_t* idToIndex,
                                      BitSet32 idBits, int32_t changedId, float xPrecision,
                                      float yPrecision, nsecs_t downTime) {
    ......

    const int32_t deviceId = getDeviceId();
    std::vector<TouchVideoFrame> frames = getDeviceContext().getVideoFrames();
    std::for_each(frames.begin(), frames.end(),
                  [this](TouchVideoFrame& frame) { frame.rotate(this->mSurfaceOrientation); });
    NotifyMotionArgs args(getContext()->getNextId(), when, readTime, deviceId, source, displayId,
                          policyFlags, action, actionButton, flags, metaState, buttonState,
                          MotionClassification::NONE, edgeFlags, pointerCount, pointerProperties,
                          pointerCoords, xPrecision, yPrecision, xCursorPosition, yCursorPosition,
                          downTime, std::move(frames));
    getListener()->notifyMotion(&args);
}

将原始的输入数据封装为NotifyMotionArgs类型,然后通知listener。

5 QueuedInputListener.notifyKey & QueuedInputListener.notifyMotion

从上面的分析可以看到:

1)、对于键盘事件,通过KeyboardInputMapper的处理,最终将原始输入事件封装为NotifyKeyArgs对象,然后通过getListener获取到传递对象,调用notifyKey将封装好的事件传过去。

2)、对于触摸屏事件,通过TouchInputMapper的处理,最终将原始输入事件封装为NotifyMotionArgs对象,然后通过getListener获取到传递对象,调用notifyMotion将封装好的事件传过去。

这里直接放结论,InputMapper.getListener函数返回了InputReader.mQueuedListener。

InputListenerInterface* InputReader::ContextImpl::getListener() {
    return mReader->mQueuedListener.get();
}

那么上面无论是键盘事件:

getListener()->notifyKey(&args);

或者触摸事件:

getListener()->notifyMotion(&args);

调用的都是QueuedInputListener的相关函数:

void QueuedInputListener::notifyKey(const NotifyKeyArgs* args) {
    traceEvent(__func__, args->id);
    mArgsQueue.push_back(new NotifyKeyArgs(*args));
}

void QueuedInputListener::notifyMotion(const NotifyMotionArgs* args) {
    traceEvent(__func__, args->id);
    mArgsQueue.push_back(new NotifyMotionArgs(*args));
}

最终的结果是,将封装的NotifyKeyArgs和NotifyMotionArgs加入到了QueuedInputListener的成员变量mArgsQueue中。

三、通知InputDispatcher有新事件发生

Key和Motion分发流程类似,因此在这里以Key事件为例看一下整个流程时序图:

在这里插入图片描述

    // Flush queued events out to the listener.
    // This must happen outside of the lock because the listener could potentially call
    // back into the InputReader's methods, such as getScanCodeState, or become blocked
    // on another thread similarly waiting to acquire the InputReader lock thereby
    // resulting in a deadlock.  This situation is actually quite plausible because the
    // listener is actually the input dispatcher, which calls into the window manager,
    // which occasionally calls into the input reader.
    mQueuedListener->flush();

上一步中,调用了InputReader.processEventsLocked函数,将所有原始事件加工为了NotifyArgs类型,并且加入到了mQueuedListener.mArgsQueue,那么这一步就是调用QueuedInputListener.flush将加工好的事件分发给InputReader相应的listener。

1 QueuedInputListener.flush

void QueuedInputListener::flush() {
    size_t count = mArgsQueue.size();
    for (size_t i = 0; i < count; i++) {
        NotifyArgs* args = mArgsQueue[i];
        args->notify(mInnerListener);
        delete args;
    }
    mArgsQueue.clear();
}

这里的mArgsQueue队列中已经有了从EventHub中读取到的并且已经被加工为NotifyArgs类型的事件了,接着调用NotifyArgs.notify函数。

2 NotifyKeyArgs.notify & NotifyMotionArgs.notify

void NotifyKeyArgs::notify(const sp<InputListenerInterface>& listener) const {
    listener->notifyKey(this);
}

......
    
void NotifyMotionArgs::notify(const sp<InputListenerInterface>& listener) const {
    listener->notifyMotion(this);
}

NotifyArgs是NotifyKeyArgs和NotifyMotionArgs的基类,根据事件类型分别调用不同子类的notify函数。

这里传入的listener是QueueInputListener.mInnerListener,在之前的分析中我们知道它对应的是一个InputClassifier对象。

3 InputClassifier.notifyKey & InputClassifier.notifyMotion

void InputClassifier::notifyKey(const NotifyKeyArgs* args) {
    // pass through
    mListener->notifyKey(args);
}

void InputClassifier::notifyMotion(const NotifyMotionArgs* args) {
    std::scoped_lock lock(mLock);
    // MotionClassifier is only used for touch events, for now
    const bool sendToMotionClassifier = mMotionClassifier && isTouchEvent(*args);
    if (!sendToMotionClassifier) {
        mListener->notifyMotion(args);
        return;
    }

    NotifyMotionArgs newArgs(*args);
    newArgs.classification = mMotionClassifier->classify(newArgs);
    mListener->notifyMotion(&newArgs);
}

在之前的分析我们知道了,InputClassifier.mListener指向了一个InputDispatcher,那么这里调用的自然也是InputDispatcher的相关函数。

3.1 InputDispatcher.notifyKey

void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {
    ......

    KeyEvent event;
    event.initialize(args->id, args->deviceId, args->source, args->displayId, INVALID_HMAC,
                     args->action, flags, keyCode, args->scanCode, metaState, repeatCount,
                     args->downTime, args->eventTime);

    android::base::Timer t;
    mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags);
    if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) {
        ALOGW("Excessive delay in interceptKeyBeforeQueueing; took %s ms",
              std::to_string(t.duration().count()).c_str());
    }

    bool needWake;
    { // acquire lock
        ......

        std::unique_ptr<KeyEntry> newEntry =
                std::make_unique<KeyEntry>(args->id, args->eventTime, args->deviceId, args->source,
                                           args->displayId, policyFlags, args->action, flags,
                                           keyCode, args->scanCode, metaState, repeatCount,
                                           args->downTime);

        needWake = enqueueInboundEventLocked(std::move(newEntry));
        mLock.unlock();
    } // release lock

    if (needWake) {
        mLooper->wake();
    }
}

1)、根据传入的NotifyKeyArgs的信息封装一个KeyEvent对象,在将输入事件传给Activity之前,调用NativeInputManager.interceptKeyBeforeQueueing对KeyEvent进行预处理,最终会调用到PhoneWindowManager#interceptKeyBeforeQueueing中,决定是否要把输入事件发送给用户。

2)、根据传入的NotifyKeyArgs的信息封装一个KeyEntry对象,然后调用InputDispatcher.InputDispatcher.enqueueInboundEventLocked。

3)、如果InputDispatcher.InputDispatcher.enqueueInboundEventLocked返回true,那么说明需要唤醒InputDispatcher的分发线程。

3.2 InputDispatcher.notifyMotion

void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) {
    ......

    uint32_t policyFlags = args->policyFlags;
    policyFlags |= POLICY_FLAG_TRUSTED;

    android::base::Timer t;
    mPolicy->interceptMotionBeforeQueueing(args->displayId, args->eventTime, /*byref*/ policyFlags);
    if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) {
        ALOGW("Excessive delay in interceptMotionBeforeQueueing; took %s ms",
              std::to_string(t.duration().count()).c_str());
    }

    bool needWake;
    { // acquire lock
        mLock.lock();

		......

        // Just enqueue a new motion event.
        std::unique_ptr<MotionEntry> newEntry =
                std::make_unique<MotionEntry>(args->id, args->eventTime, args->deviceId,
                                              args->source, args->displayId, policyFlags,
                                              args->action, args->actionButton, args->flags,
                                              args->metaState, args->buttonState,
                                              args->classification, args->edgeFlags,
                                              args->xPrecision, args->yPrecision,
                                              args->xCursorPosition, args->yCursorPosition,
                                              args->downTime, args->pointerCount,
                                              args->pointerProperties, args->pointerCoords, 0, 0);

        needWake = enqueueInboundEventLocked(std::move(newEntry));
        mLock.unlock();
    } // release lock

    if (needWake) {
        mLooper->wake();
    }
}

内容和InputDispatcher.notifyKey相似:

1)、在将输入事件传给Activity之前,将传入的NotifyMotionArgs的信息发送给NativeInputManager.interceptMotionBeforeQueueing进行预处理,最终会调用PhoneWindowManager#interceptMotionBeforeQueueing,由PhoneWindowManager决定是否要拦截此次事件。

2)、根据传入的NotifyMotionArgs的信息封装一个MotionEntry对象,然后调用InputDispatcher.InputDispatcher.enqueueInboundEventLocked。

3)、如果InputDispatcher.InputDispatcher.enqueueInboundEventLocked返回true,那么说明需要唤醒InputDispatcher的分发线程。

3.3 InputDispatcher.enqueueInboundEventLocked

不管是Key事件还是Motion事件,在经过PhoneWindowManager预处理后,如果PhoneWindowManager不拦截此次事件,那么接下来就是调用InputDispatcher.enqueueInboundEventLocked函数继续分发事件给相关窗口。

bool InputDispatcher::enqueueInboundEventLocked(std::unique_ptr<EventEntry> newEntry) {
    bool needWake = mInboundQueue.empty();
    mInboundQueue.push_back(std::move(newEntry));
    EventEntry& entry = *(mInboundQueue.back());
    traceInboundQueueLengthLocked();

    switch (entry.type) {
        case EventEntry::Type::KEY: {
			......
        }

        case EventEntry::Type::MOTION: {
			......
        }
        case EventEntry::Type::FOCUS: {
            LOG_ALWAYS_FATAL("Focus events should be inserted using enqueueFocusEventLocked");
            break;
        }
        case EventEntry::Type::CONFIGURATION_CHANGED:
        case EventEntry::Type::DEVICE_RESET:
        case EventEntry::Type::SENSOR:
        case EventEntry::Type::POINTER_CAPTURE_CHANGED:
        case EventEntry::Type::DRAG: {
            // nothing to do
            break;
        }
    }

    return needWake;
}

首先判断mInboundQueue是否为空,将本次处理的EventEntry加入到mInboundQueue中。接着判断是否要唤醒InputDispatcher的分发线程,有三种唤醒情况:

1)、mInboundQueue为空。

2)、mInboundQueue不为空,此时为按键事件。如果当前按键事件将触发App切换,并且如果此时该按键抬起,需要丢弃之前所有的事件,并且唤醒InputDispatcher分发线程。

3)、mInboundQueue不为空,此时为触摸事件。如果当前触摸事件之前的事件需要被删除,那么唤醒InputDispatcher分发线程。

4 InputDispatcher分发线程的唤醒

将输入事件加入InputDispatcher.mInboundQueue中,下一步就是唤醒InputDispatcher分发线程。

先来补充一下Looper的睡眠和唤醒机制:

C++类Looper中的睡眠和唤醒机制是通过pollOnce和wake函数提供的,它们又是利用操作系统(Linux内核)的epoll机制来完成的。

在Looper的构造函数中,会创建一个管道,然后调用epoll_create获取一个epoll的实例的描述符,最后将管道读端描述符作为一个事件报告项添加给epoll。

Looper的pollOnce函数将最终调用到其pollInner函数。在后者里面,将调用epoll_wait睡眠等待其监控的文件描述符是否有可I/O事件的到来,若有(哪怕只有一个),epoll_wait将会醒来。

Looper的wake函数用于向管道中写入字符,pollOnce将会从epoll_wait中唤醒。

我们知道InputDispatcher内部有一个的分发线程,在循环调用dispatchOnce来进行事件的分发,但即使是循环调用,这个线程也不可能真的无限制在一直分发,而是当有事件可读时,才进行分发,其他时间分发线程进入休眠状态。

InputDispatcher.dispatchOnce函数的最后,在事件分发完成后会调用Looper.pollOnce将当前线程睡眠:

void InputDispatcher::dispatchOnce() {
	......
    // 上面是分发事件的具体内容    

    // Wait for callback or timeout or wake.  (make sure we round up, not down)
    nsecs_t currentTime = now();
    int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);
    mLooper->pollOnce(timeoutMillis);
}

那么上面在InputDispatcher.notifyKey或者是InputDispatcher.notifyMotion中调用Looper.wake的时候:

    if (needWake) {
        mLooper->wake();
    }

InputDispatcher.dispatchOnce中的Looper.pollOnce函数就可以返回,上一次的InputDispatcher.dispatchOnce函数才会全部执行完成并且返回,进而可以循环进行下一次的InputDispatcher.dispatchOnce,开启新一轮事件分发。

四、总结

1)、InputReader通过EventHub.getEvents读取原始事件RawEvent。

2)、InputReader调用InputReader.processEventsLocked函数将原始事件加工为NotifyArgs类型,然后存储到InputReader的QueuedInputListener类型的成员变量mQueuedListener内部的mArgsQueue中进行排队等待分发。

3)、InputReader调用QueuedInputListener.flush函数,将QueuedInputListener.mArgsQueue存储的事件一次性发送到InputDispatcher,最终事件是被封装成了EventEntry类型,添加到了InputDispatcher.mInboundQueue中,InputReader并且唤醒了InputDispatcher的分发线程,后续InputDispatcher分发线程会重新调用InputDispatcher.dispatchOnce函数来进行事件的分发。

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值