Android InputManager分析

栏目: IOS · Android · 发布时间: 5年前

内容简介:本篇为鸡生蛋系列第二篇文章主要讲一下inputmanager相关的,即驱动把数据上报到用户空间后,用户空间到应用这么个流程,在上一遍讲内核的input子系统时候,我们采取的反向分析,即由驱动出发,最后到input core,input子系统架构这么个由点到面的分析方法,

本篇为鸡生蛋系列第二篇文章

  1. Linux input系统数据上报流程 https://segmentfault.com/a/11...
  2. Android InputManager分析

主要讲一下inputmanager相关的,即驱动把数据上报到用户空间后,用户空间到应用这么个流程,

在上一遍讲内核的input子系统时候,我们采取的反向分析,即由驱动出发,最后到input core,input子系统架构这么个由点到面的分析方法,

那分析inputmanager是否可采用这种方法如何呢?实际上,对于Android上层(Native framework/framework, c++/java)的分析,我一般

采用的是由上而下的分析,即从其初始化(main,构造,onFirstRef())开始, 通常在其初始化时候,会重一些很重要的上下层的连接,如果由下往上看,会麻烦点,

然后再结合实例,看看他的数据流向是如何的,或者一些重要的API, 例如对于Audio来说,可以结合播放音乐流程来分析整个系统架构。

简单说来,input到应用的流程为

EventHub监控并读取/dev/input下数据 --> 给InputReader 加工处理 --> 到InputDispacher --> 找到focused窗口并通过input channel发出去

参考文档:

十分钟了解Android触摸事件原理(InputManagerService)

https://www.jianshu.com/p/f05...

android控件系统:输入事件在控件树中的传递

https://blog.csdn.net/renshug...

https://blog.csdn.net/renshug...

InputManagerService分析一:IMS的启动与事件传递

https://blog.csdn.net/lilian0...

相关代码目录:

Android 9.0 http://androidxref.com/9.0.0_r3/

frameworks/base/services/java/com/android/server/SystemServer.java

frameworks/base/services/core/java/com/android/server/input/InputManagerService.java

frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp

frameworks/native/services/inputflinger/

1.1 初始化

frameworks/base/services/java/com/android/server/SystemServer.java
startOtherServices() {
    inputManager = new InputManagerService(context);
    ....
    wm = WindowManagerService.main(context, inputManager,
    ServiceManager.addService(Context.INPUT_SERVICE, inputManager,
            /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL);
    ....
    inputManager.setWindowManagerCallbacks(wm.getInputMonitor());
    inputManager.start();
    ......
}

IMS(InputManagerService)的初始化,是从SystemServer开始的,通过搜索代码(如上),我们可以看到构造了一个实例,

并做为参数传给了WMS, 由此我们也猜想,会和WMS有紧密的关系,然后

IMS设置了setWindowManagerCallbacks()并通过start()函数启动了,

SystemServer里有关IMS的就这么几个地方,我们再看下构造和start()具体的流程,与WMS的关联不分析。

frameworks/base/services/core/java/com/android/server/input/InputManagerService.java
// Pointer to native input manager service object.
private final long mPtr;

public InputManagerService(Context context) {
    this.mContext = context;
    this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper());

    // config_useDevInputEventForAudioJack配置为true, 耳机事件可通过input上报
    mUseDevInputEventForAudioJack =
            context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack);
......
    mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());
......
    LocalServices.addService(InputManagerInternal.class, new LocalService());
}

public void start() {
    Slog.i(TAG, "Starting input manager");
    nativeStart(mPtr);
....
}

InputManagerService构造和start()主要也是调到JNI的 nativeInit() nativeStart().

frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
static jlong nativeInit(JNIEnv* env, jclass /* clazz */,
        jobject serviceObj, jobject contextObj, jobject messageQueueObj) {
....
    NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,
            messageQueue->getLooper());
    im->incStrong(0);
    return reinterpret_cast<jlong>(im);
}

static void nativeStart(JNIEnv* env, jclass /* clazz */, jlong ptr) {
......
    status_t result = im->getInputManager()->start();
......
}

nativeInit()又构造了一个 NativeInputManager(),该类可认为是上层 JAVA 和下层EventHub InputManager的桥梁,

nativeStart()通过 NativeInputManager最终调到 InputManager 的 start()方法

NativeInputManager::NativeInputManager(jobject contextObj,
        jobject serviceObj, const sp<Looper>& looper) :
        mLooper(looper), mInteractive(true) {
......
    sp<EventHub> eventHub = new EventHub();
    mInputManager = new InputManager(eventHub, this, this);
}

NativeInputManager()的构造又new了 EventHub 和 InputManager , 其中

eventHub做为参数传给了 InputManager()

frameworks/native/services/inputflinger/EventHub.cpp
EventHub::EventHub(void) :
......{
    acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);

    mEpollFd = epoll_create(EPOLL_SIZE_HINT); // epoll机制
    LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance.  errno=%d", errno);

    mINotifyFd = inotify_init(); // inotify机制
    int result = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE); // 利用inotify监控 DEVICE_PATH(/dev/input)创建和删除
......
    eventItem.events = EPOLLIN;
    eventItem.data.u32 = EPOLL_ID_INOTIFY;
    result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem); // 将inotify的fd添加到Epoll监控中
    LOG_ALWAYS_FATAL_IF(result != 0, "Could not add INotify to epoll instance.  errno=%d", errno);

    int wakeFds[2];
    result = pipe(wakeFds); //读写pipe, InputReader有事件时唤醒
    LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe.  errno=%d", errno);

    mWakeReadPipeFd = wakeFds[0];
    mWakeWritePipeFd = wakeFds[1];
......
    result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem);
......
}

EventHub相当于一个集线器,把底层的USB, TOUCH,鼠标等事件统一收集上来,再给上层。

其构造函数当中利用inotify机制监控"/dev/input" 目录下设备的创建和删除,这样当有设备变更时就可以收到通知了,

构造函数也创建了所需要的mEpollFd,这个作为IO多路复用的机制,不清楚的可以查下如何使用,

构造里将mINotifyFd添加到了epoll里,在后续input设备创建的时候,也会把input设备的fd添加进去,这样当有数据或者设备变化时,

EventHub就可获取这些事件,进一步处理。

构造还创建了两个pipe,作为wakeup的读端和写端,当InputReader.cpp有事件(配置变更,monitor, 超时请求等)唤醒EventHub处理。

InputManager::InputManager(
        const sp<EventHubInterface>& eventHub,
        const sp<InputReaderPolicyInterface>& readerPolicy,
        const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
    mDispatcher = new InputDispatcher(dispatcherPolicy);
    mReader = new InputReader(eventHub, readerPolicy, mDispatcher); // eventHub又传给了 InputReader,最终他们俩是紧密联系在一起的
    initialize(); // eventHub又传给了
}

void InputManager::initialize() {
    mReaderThread = new InputReaderThread(mReader);
    mDispatcherThread = new InputDispatcherThread(mDispatcher);
}

InputManager(),创建了InputDispatcher和InputReader实例并与对应的InputDispatcherThread InputReaderThread 线程关联

具体的我们不往下跟了,有兴趣的可以再看看,

至此,初始化流程告一段落。

InputManagerService.java的 start方法,最终到InputManager::start(),

status_t InputManager::start() {
    status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
......
    result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
......
}

start() 方法目的就是让这两个线程跑起来,这样就可以不断的获取,处理消息了。

1.2 小结

startOtherServices()/SystemServer.java
    + new InputManagerService(context) --> nativeInit(...) --> new NativeInputManager(...)
    +                                                              + new EventHub() --> inotify监控/dev/input + epoll + wake pipe
    +                                                              + new InputManager(eventHub,...)
    +                                                                  +  new InputDispatcher()
    +                                                                  +  new InputReader(eventHub,...)
    +                                                                  +  initialize()
    +                                                                          + new InputReaderThread(mReader)
    +                                                                          + new InputDispatcherThread(mDispatcher)
    +
    +                                                                                          ^
    +                                                                                          +
    + inputManager.start() --> nativeStart(mPtr) --> im->getInputManager()->start() --> mDispatcherThread->run() mReaderThread->run()

2. 读取数据

bool InputReaderThread::threadLoop() {
    mReader->loopOnce();
    return true;
}

bool InputDispatcherThread::threadLoop() {
    mDispatcher->dispatchOnce();
    return true;
}

上一小节讲到IMS通过start()函数,最终让InputReaderThread InputDispatcherThread两个线程跑起来了,

线程跑起来后,他们因为返回值为true, 所以他们会不断的loop, 即不断的读取,分发,读取,分发……

看上面几行代码,觉得整个过程很简单清晰,然而当我们继续跟下去看细节的时候,你能 哇~~哇~~哇~~

这一节我们看看 mReader->loopOnce(), 下一节继续看Dispatcher过程

void InputReader::loopOnce() {
......
    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
......
        if (count) {
            processEventsLocked(mEventBuffer, count);
        }
......// 通知dispather分发
    mQueuedListener->flush();
}

InputReader的loopOnce()通过

EventHub getEvents()

获得元数据,然后通过

processEventsLocked()

进一步的处理,

然后再通过

mQueuedListener->flush()

通知InputDispatcher有数据了,该处理了

2.1 InputReader::loopOnce()之 EventHub->getEvents()

size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
......
   for (;;) {
       nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);

       // Reopen input devices if needed.
......
       // Report any devices that had last been added/removed.
       while (mClosingDevices) {
......
       }

       // 扫描设备
       if (mNeedToScanDevices) {
           mNeedToScanDevices = false;
           scanDevicesLocked();
           mNeedToSendFinishedDeviceScan = true;
       }
......

       // Grab the next input event.
       bool deviceChanged = false;
       while (mPendingEventIndex < mPendingEventCount) {
           const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];
......
           ssize_t deviceIndex = mDevices.indexOfKey(eventItem.data.u32);
......
           Device* device = mDevices.valueAt(deviceIndex);
           if (eventItem.events & EPOLLIN) { //epoll事件
               // 读取数据
               int32_t readSize = read(device->fd, readBuffer,
                       sizeof(struct input_event) * capacity);
......
                       event->deviceId = deviceId; // <-- 设备id
                       event->type = iev.type;
                       event->code = iev.code;
                       event->value = iev.value;
                       event += 1;
......
       // Return now if we have collected any events or if we were explicitly awoken.
       if (event != buffer || awoken) {
           break;
       }

       // Poll for events.  Mind the wake lock dance!
......
        int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
......
    return event - buffer;
}

getEvents()会检查是否需要扫描设备,如果需要的话,则会建立设备KeyedVector向量表,

之后若有数据到来则通过read()函数读取数据, 返回RawEvent* buffer给processEventsLocked()进行下一步处理,

若啥事都没有通过epoll_wait()阻塞等待。

本来数据的读取(read())比较简单, 这里只列下设备扫描流程,作为个人笔记,有兴趣的可以看下

EventHub::scanDevicesLocked() --> scanDirLocked(DEVICE_PATH) "/dev/input" --> while处理 openDeviceLocked() -->

status_t EventHub::openDeviceLocked(const char *devicePath) {
......
    int fd = open(devicePath, O_RDWR | O_CLOEXEC | O_NONBLOCK);
......//一大堆ioctl的信息获取
    if(ioctl(......)) {
......//生成唯一的 deviceId,和device, 做为mdevices的 key, value. 以后的操作会常用到这个deviceId
    // Allocate device.  (The device object takes ownership of the fd at this point.)
    int32_t deviceId = mNextDeviceId++;
    Device* device = new Device(fd, deviceId, String8(devicePath), identifier);
......
    // Load the configuration file for the device.
    // 加载这个设备的 idc(Input Device Configuration)配置文件
    loadConfigurationLocked(device);

......//能力获取和分类
    // Figure out the kinds of events the device reports.
    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);
...... // 设备分类
        device->classes |= ......;
......
    //加入到epoll当中
    if (registerDeviceForEpollLocked(device) != OK) {
......
    configureFd(device);
......//加入mDevices并更新 mOpeningDevices 链表
    addDeviceLocked(device);
    return OK;
}
// 对于我们的触屏来说class为
// See if this is a touch pad.
// Is this a new modern multi-touch driver?
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;
    }
......//之后还会加载虚拟key.
// Configure virtual keys.
if ((device->classes & INPUT_DEVICE_CLASS_TOUCH)) {
    // Load the virtual keys for the touch screen, if any.
    // We do this now so that we can make sure to load the keymap if necessary.
    status_t status = loadVirtualKeyMapLocked(device);

status_t EventHub::loadVirtualKeyMapLocked(Device* device) {
    // The virtual key map is supplied by the kernel as a system board property file.
......
    path.append("/sys/board_properties/virtualkeys.");
    path.append(device->identifier.name);
......
    return VirtualKeyMap::load(path, &device->virtualKeyMap);

addDeviceLocked()即添加到

EventHub.h KeyedVector<int32_t, Device*> mDevices;

并更新链表mOpeningDevices

void EventHub::addDeviceLocked(Device* device) {
    mDevices.add(device->id, device);
    device->next = mOpeningDevices;
    mOpeningDevices = device;
}

另外要注意一点的是,在scanDevicesLocked()时候也会创建虚拟键盘。

void EventHub::scanDevicesLocked() {
    status_t res = scanDirLocked(DEVICE_PATH);
.......
    // 创建虚拟键盘
    if (mDevices.indexOfKey(VIRTUAL_KEYBOARD_ID) < 0) {
        createVirtualKeyboardLocked();
    }
}

2.2 InputReader::loopOnce()之 processEventsLocked

mEventHub->getEvents(), 反回元数据后,传给 processEventsLocked()进一步处理

元数据的定义如下,主要记录了时间,设备id, type, code, value.

struct RawEvent {
    nsecs_t when;
    int32_t deviceId;
    int32_t type;
    int32_t code;
    int32_t value;
};

其中的deviceId起了个连接作用,用于标识eventhub和iputreader中的设备,

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) {
            int32_t deviceId = rawEvent->deviceId;
......
            processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
......
            case EventHubInterface::DEVICE_ADDED:
                addDeviceLocked(rawEvent->when, rawEvent->deviceId);
            case EventHubInterface::DEVICE_REMOVED:
            ......
            case EventHubInterface::FINISHED_DEVICE_SCAN:
            ......
    }
}

processEventsLocked()函数有个

processEventsForDeviceLocked() 对于对数据的处理,

另外还根据type, 处理了对设备添加移除,扫描的处理,

大家就有点奇怪了,咦,eventhub扫描设备的时候,不是有处理添加设备吗?

咋这儿又有添加设备了? 而且看代码,两者都有个mDevices变量

EventHub.h KeyedVector<int32_t, Device*> mDevices;
InputReader.h KeyedVector<int32_t, InputDevice*> mDevices;

上面可看到两者value类型不同,他们之间的key 即deviceID是相同的,

其实我个人认为EventHub中的Device为设备的本身属性,是下层设备的实例化,

而InputReader中的InputDevice为更高层次的抽象,主要用于往上层处理数据,

addDeviceLocked()过程中还会根据input设备的不同属性设置不同的Mapper事件转换器。

我们先看下processEventsForDeviceLocked()过程:

void InputReader::processEventsForDeviceLocked(int32_t deviceId,
        const RawEvent* rawEvents, size_t count) {
    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
......
    InputDevice* device = mDevices.valueAt(deviceIndex);
......
    device->process(rawEvents, count);
}

void InputDevice::process(const RawEvent* rawEvents, size_t count) {
    // Process all of the events in order for each mapper.
......
    // 可能会有多个mapper
    size_t numMappers = mMappers.size();
    for (const RawEvent* rawEvent = rawEvents; count != 0; rawEvent++) {
......
            for (size_t i = 0; i < numMappers; i++) {
                InputMapper* mapper = mMappers[i];
                mapper->process(rawEvent);
            }
......
}

processEventsForDeviceLocked() --> device->process() --> mapper->process()

最终数据的处理也是通过mapper来处理的,所以我们还得看下mapper咋添加的

Mapper添加

mapper的添加是根据分类来添加的, 以触屏为例

frameworks/native/services/inputflinger/InputReader.cpp
InputReader::processEventsLocked() --> addDeviceLocked() --> createDeviceLocked() --> 
// Touchscreens and touchpad devices.
if (classes & INPUT_DEVICE_CLASS_TOUCH_MT) {
    device->addMapper(new MultiTouchInputMapper(device));
} else if (classes & INPUT_DEVICE_CLASS_TOUCH) {
    device->addMapper(new SingleTouchInputMapper(device));
}

所以触屏的最后数据处理函数会调到

MultiTouchInputMapper的process函数不再详细看

void MultiTouchInputMapper::process(const RawEvent* rawEvent) {
    TouchInputMapper::process(rawEvent);

    mMultiTouchMotionAccumulator.process(rawEvent);
}

2.3 InputReader数据如何到InputDispatcher的?

InputReader::loopOnce() 数据处理完后便调用 mQueuedListener->flush() 通知 InputDispatcher 该处理数据了。

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();
}

flush()方法即把mArgsQueue Vector一个个取出来,然后再调用notify()方法,

那我们肯定想要知道

1. 数据是咋压入 mArgsQueue的?

2. notify() 后续流程咋把数据给到 InputDispatcher

1.

void QueuedInputListener::notifyConfigurationChanged(
        const NotifyConfigurationChangedArgs* args) {
    mArgsQueue.push(new NotifyConfigurationChangedArgs(*args));
}

void QueuedInputListener::notifyKey(const NotifyKeyArgs* args) {
    mArgsQueue.push(new NotifyKeyArgs(*args));
}

void QueuedInputListener::notifyMotion(const NotifyMotionArgs* args) {
    mArgsQueue.push(new NotifyMotionArgs(*args));
}

void QueuedInputListener::notifySwitch(const NotifySwitchArgs* args) {
    mArgsQueue.push(new NotifySwitchArgs(*args));
}

void QueuedInputListener::notifyDeviceReset(const NotifyDeviceResetArgs* args) {
    mArgsQueue.push(new NotifyDeviceResetArgs(*args));
}

在QueuedInputListener中看到 notifyConfigurationChanged() notifyKey() notifyMotion() notifySwitch() notifyDeviceReset()

当有配置变化或事件时,都会新创建个notify args实例(都继承自NotifyArgs),然后push到mArgsQueue,

以触屏事件为例,push流程为:

TouchInputMapper::sync() --> processRawTouches() --> cookAndDispatch() --> dispatchTouches() --> dispatchMotion()
--> NotifyMotionArgs args(...) getListener()->notifyMotion(&args) -->
frameworks/native/services/inputflinger/InputListener.cpp
void QueuedInputListener::notifyMotion(const NotifyMotionArgs* args) {
    mArgsQueue.push(new NotifyMotionArgs(*args));
}
  1. InputReader::loopOnce() --> QueuedInputListener::flush() --> for args->notify(mInnerListener);

以触屏NotifyMotionArgs为例,其调用到

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

注意其 listener 为 InputDispatcher (InputDispatcher 继承自 InputDispatcherInterface class InputDispatcher : public InputDispatcherInterface),

frameworks/native/services/inputflinger/InputManager.cpp
mDispatcher = new InputDispatcher(dispatcherPolicy); mReader = new InputReader(..., ..., mDispatcher); --> InputReader::InputReader(..., ..., ...listener) --> new QueuedInputListener(listener);

所以最终就调到了

InputDispatcher::notifyMotion()

void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) {
......// 合法性检查
    if (!validateMotionEvent(args->action, args->actionButton,
                args->pointerCount, args->pointerProperties)) {
......// 预处理
    mPolicy->interceptMotionBeforeQueueing(args->eventTime, /*byref*/ policyFlags);
......
        if (shouldSendMotionToInputFilterLocked(args)) {
            mLock.unlock();

            MotionEvent event;
            event.initialize(args->deviceId, args->source, args->action, args->actionButton,
                    args->flags, args->edgeFlags, args->metaState, args->buttonState,
                    0, 0, args->xPrecision, args->yPrecision,
                    args->downTime, args->eventTime,
                    args->pointerCount, args->pointerProperties, args->pointerCoords);

            policyFlags |= POLICY_FLAG_FILTERED;
            // 过滤
            if (!mPolicy->filterInputEvent(&event, policyFlags)) {
                return; // event was consumed by the filter
            }

            mLock.lock();
        }

        // Just enqueue a new motion event.
        MotionEntry* newEntry = new MotionEntry(args->eventTime,
                args->deviceId, args->source, policyFlags,
                args->action, args->actionButton, args->flags,
                args->metaState, args->buttonState,
                args->edgeFlags, args->xPrecision, args->yPrecision, args->downTime,
                args->displayId,
                args->pointerCount, args->pointerProperties, args->pointerCoords, 0, 0);

        // 入队
        needWake = enqueueInboundEventLocked(newEntry);
        mLock.unlock();
    } // release lock

    if (needWake) {
        // 唤醒
        mLooper->wake();
    }
}

notifyMotion()会先检查合法性,然后预处理,如果需要过滤则进行过滤处理,

否则构建 MotionEntry,并入队,随后将looper唤醒。

bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) {
    bool needWake = mInboundQueue.isEmpty();
    // 入队
    mInboundQueue.enqueueAtTail(entry);
......
}

InputReader这一侧大至就分析完了,数据从InputReader传到InputDispatcher也清楚了,

接下来看看数据分发。

3. 分发数据

在开头也讲到,InputDispatcherThread里不断的loop,调用dispatchOnce()进行数据的分发。

frameworks/native/services/inputflinger/InputDispatcher.cpp
void InputDispatcher::dispatchOnce() {
......
        // Run a dispatch loop if there are no pending commands.
        // The dispatch loop might enqueue commands to run afterwards.
        if (!haveCommandsLocked()) {
            // 如果命令列队为空, 进行事件分发
            dispatchOnceInnerLocked(&nextWakeupTime);
        }
    //如果looper里没有信息,会阻塞,直到timeoutMillis超时
    mLooper->pollOnce(timeoutMillis);
}

dispatchOnce()里如果命令处理完了,才会调用dispatchOnceInnerLocked()进行事件处理。

void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
.....//如果没有event,抓取一个
  // Ready to start a new event.
  // If we don't already have a pending event, go grab one.
  if (! mPendingEvent) {
      if (mInboundQueue.isEmpty()) {
......
      } else {
          // Inbound queue has at least one entry.
          mPendingEvent = mInboundQueue.dequeueAtHead(); //<---从mInboundQueue队头抓个
          traceInboundQueueLengthLocked();
      }
......//一些错误处理,包括anr时间重置,略过

    switch (mPendingEvent->type) {
    case EventEntry::TYPE_CONFIGURATION_CHANGED: ....
    case EventEntry::TYPE_DEVICE_RESET: ....
    case EventEntry::TYPE_KEY: .....
......//对我们的touch来说是motion事件
    case EventEntry::TYPE_MOTION: {
        MotionEntry* typedEntry = static_cast<MotionEntry*>(mPendingEvent);
        if (dropReason == DROP_REASON_NOT_DROPPED && isAppSwitchDue) {
            dropReason = DROP_REASON_APP_SWITCH;
        }
        if (dropReason == DROP_REASON_NOT_DROPPED
                && isStaleEventLocked(currentTime, typedEntry)) {
            dropReason = DROP_REASON_STALE;
        }
        if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) {
            dropReason = DROP_REASON_BLOCKED;
        }
        //分发事件
        done = dispatchMotionLocked(currentTime, typedEntry,
                &dropReason, nextWakeupTime);
        break;
    }
......
}

dispatchOnceInnerLocked从mInboundQueue队列中取出之前的MotionEntry,

然后错误处理,对于触屏事件做dispatchMotionLocked()

bool InputDispatcher::dispatchMotionLocked(
        nsecs_t currentTime, MotionEntry* entry, DropReason* dropReason, nsecs_t* nextWakeupTime) {
......// 是否为 point event
    bool isPointerEvent = entry->source & AINPUT_SOURCE_CLASS_POINTER;

    // Identify targets.
    Vector<InputTarget> inputTargets;
......
    if (isPointerEvent) {
        // Pointer event.  (eg. touchscreen)
        injectionResult = findTouchedWindowTargetsLocked(currentTime,
                entry, inputTargets, nextWakeupTime, &conflictingPointerActions);
    } else {
        // Non touch event.  (eg. trackball)
        injectionResult = findFocusedWindowTargetsLocked(currentTime,
                entry, inputTargets, nextWakeupTime);
    }
......
    dispatchEventLocked(currentTime, entry, inputTargets);
    return true;
}

对于point event,会先用

findTouchedWindowTargetsLocked() 找到目标窗口,否则用

findFocusedWindowTargetsLocked() 找到目标窗口

对我们的触屏来说,包含有该属性

frameworks/native/include/android/input.h

AINPUT_SOURCE_TOUCHSCREEN = 0x00001000 | AINPUT_SOURCE_CLASS_POINTER,

找到目标窗口后,再用 dispatchEventLocked() 发给目标窗口

dispatchEventLocked() --> prepareDispatchCycleLocked() --> enqueueDispatchEntriesLocked()
void InputDispatcher::dispatchEventLocked(nsecs_t currentTime,
        EventEntry* eventEntry, const Vector<InputTarget>& inputTargets) {
......
    for (size_t i = 0; i < inputTargets.size(); i++) {
        const InputTarget& inputTarget = inputTargets.itemAt(i);

        ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel);
        if (connectionIndex >= 0) {
            sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
            prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget);
......
}

void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
......
    // Not splitting.  Enqueue dispatch entries for the event as is.
    enqueueDispatchEntriesLocked(currentTime, connection, eventEntry, inputTarget);
}
void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime,
        const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget) {
    bool wasEmpty = connection->outboundQueue.isEmpty();

    // Enqueue dispatch entries for the requested modes.
    enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
            InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT);
    enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
            InputTarget::FLAG_DISPATCH_AS_OUTSIDE);
    enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
            InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER);
    enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
            InputTarget::FLAG_DISPATCH_AS_IS);
    enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
            InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT);
    enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
            InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER);

    // If the outbound queue was previously empty, start the dispatch cycle going.
    if (wasEmpty && !connection->outboundQueue.isEmpty()) {
        startDispatchCycleLocked(currentTime, connection);
    }
}

enqueueDispatchEntryLocked()会根据flag mode进行比较,然后加入到connection的outboundQueue里

connection->outboundQueue.enqueueAtTail(dispatchEntry);

然后再调用

startDispatchCycleLocked()最终通过socket把事件发出去

void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
        const sp<Connection>& connection) {
......
        case EventEntry::TYPE_MOTION: {
......
            // Publish the motion event.
            status = connection->inputPublisher.publishMotionEvent(dispatchEntry->seq,
                    motionEntry->deviceId, motionEntry->source, motionEntry->displayId,
                    dispatchEntry->resolvedAction, motionEntry->actionButton,
                    dispatchEntry->resolvedFlags, motionEntry->edgeFlags,
                    motionEntry->metaState, motionEntry->buttonState,
                    xOffset, yOffset, motionEntry->xPrecision, motionEntry->yPrecision,
                    motionEntry->downTime, motionEntry->eventTime,
                    motionEntry->pointerCount, motionEntry->pointerProperties,
                    usingCoords);
            break;
        }
......

frameworks/native/libs/input/InputTransport.cpp
status_t InputPublisher::publishMotionEvent(
......

    InputMessage msg;
    msg.header.type = InputMessage::TYPE_MOTION;
...... // 通过socket发送
    return mChannel->sendMessage(&msg);
}

4. 数据接收

数据发送后,又被谁接收到了呢?之后流程又如何呢?

input数据主要有两种,一个应用,一个MonitoringChannel,

这里仅简单的列举下,详细的请看看参考文档

4.1 App 接收

对于应用的接收,需要看input channel是咋建立的,

然后看看findTouchedWindowTargetsLocked(),咋找到目录窗口,该函数很复杂,

但有个比较重要的是查询 mWindowHandles, 该变量在setInputWindows()设置,

WindowManagerService.java
mInputMonitor.updateInputWindowsLw(false /*force*/); in addWindow()                    -+
mInputMonitor.updateInputWindowsLw(true /*force*/); in postWindowRemoveCleanupLocked()  +
mInputMonitor.updateInputWindowsLw(true /*force*/); in relayoutWindow()                 +
mInputMonitor.updateInputWindowsLw(true /*force*/); in relayoutWindow()                 +--> updateInputWindowsLw() -->
mInputMonitor.updateInputWindowsLw(true /*force*/); in removeWindowToken()              +
mInputMonitor.updateInputWindowsLw(true /*force*/); in startPositioningLocked()         +
mInputMonitor.updateInputWindowsLw(true /*force*/); in startPositioningLocked()         +
mInputMonitor.updateInputWindowsLw(true /*force*/); in finishPositioning()             -+

InputMonitor.java updateInputWindowsLw()
    +--> mUpdateInputForAllWindowsConsumer.updateInputWindows(inDrag)
        +--> updateInputWindows() 
            +--> InputManagerService.java setInputWindows() 
                +--> nativeSetInputWindows()
                    +--> im->setInputWindows (NativeInputManager::setInputWindows()) 
                        +--> mInputManager->getDispatcher()->setInputWindows() 
                             +-->
void InputDispatcher::setInputWindows(const Vector<sp<InputWindowHandle> >& inputWindowHandles) {
......
        mWindowHandles = inputWindowHandles;

应用添加窗口设置mWindowHandles如上。在addWindow() relayoutWindow()...过程中都可能设置该变量

frameworks/base/core/java/android/view/ViewRootImpl.java
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
......          // addToDisplay() 将调用WMS mService.addWindow()
                res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                        getHostVisibility(), mDisplay.getDisplayId(), mWinFrame,
                        mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                        mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel);
...... //
                mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
                        Looper.myLooper());
            }

对应用来说在setView() 时会调用mWindowSession.addToDisplay(),很后调用addWindow(), 然后win.openInputChannel(outInputChannel)等建立channel操作

addToDisplay() 之会,会将mInputChannel looper,通过 WindowInputEventReceiver绑在一起,

这样当有数据到来时在looper里面处理。

WindowInputEventReceiver()流程如下

WindowInputEventReceiver() --> InputEventReceiver.java InputEventReceiver() --> nativeInit() -->
frameworks/base/core/jni/android_view_InputEventReceiver.cpp
static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,
        jobject inputChannelObj, jobject messageQueueObj) {
......
    sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,
            receiverWeak, inputChannel, messageQueue);
    status_t status = receiver->initialize();
......
}
initialize() (NativeInputEventReceiver::initialize())--> setFdEvents(ALOOPER_EVENT_INPUT) -->
// 注意事件类型为 ALOOPER_EVENT_INPUT
void NativeInputEventReceiver::setFdEvents(int events) {
    if (mFdEvents != events) {
        mFdEvents = events;
        int fd = mInputConsumer.getChannel()->getFd();
        if (events) {
            mMessageQueue->getLooper()->addFd(fd, 0, events, this, NULL);

system/core/libutils/Looper.cpp
int Looper::addFd(int fd, int ident, int events, const sp<LooperCallback>& callback, void* data) {
......// 将input channel的fd加入到epoll监控中
            int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem);

** 当socket接收到数据时,通过handle来处理

android_view_InputEventReceiver.cpp
int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) {
......// ALOOPER_EVENT_INPUT 事件
    if (events & ALOOPER_EVENT_INPUT) {
        JNIEnv* env = AndroidRuntime::getJNIEnv();
        status_t status = consumeEvents(env, false /*consumeBatches*/, -1, NULL);
        mMessageQueue->raiseAndClearException(env, "handleReceiveCallback");
        return status == OK || status == NO_MEMORY ? 1 : 0;
    }
......
}
handleEvent() --> consumeEvents() -->
status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
        bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) {
......
        status_t status = mInputConsumer.consume(&mInputEventFactory, //取数据
......
            if (inputEventObj) {
...
                env->CallVoidMethod(receiverObj.get(),
                        gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj, //调用 dispatchInputEvent()
......
}
-->
frameworks/base/core/java/android/view/InputEventReceiver.java dispatchInputEvent() --> onInputEvent() --> 
final class WindowInputEventReceiver extends InputEventReceiver {
......
    @Override
    public void onInputEvent(InputEvent event, int displayId) {
        enqueueInputEvent(event, this, 0, true);
    }

void enqueueInputEvent(InputEvent event,
        InputEventReceiver receiver, int flags, boolean processImmediately) {
......
    if (processImmediately) {
        doProcessInputEvents(); // -->  deliverInputEvent(q);
    } else {
        scheduleProcessInputEvents();
    }
}
ViewRootImpl.java
private void deliverInputEvent(QueuedInputEvent q) {
......
    InputStage stage;
    if (q.shouldSendToSynthesizer()) {
        stage = mSyntheticInputStage;
    } else {
        // mFirstPostImeInputStage = earlyPostImeStage; new EarlyPostImeInputStage(nativePostImeStage); 注意参数为nativePostImeStage,在 apply(q, onProcess(q)) 返回forward时会用到
        stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;
    }
......
    if (stage != null) {
        handleWindowFocusChanged();
        stage.deliver(q);
......
}
stage.deliver --> apply(q, onProcess(q)) --> EarlyPostImeInputStage onProcess() --> processPointerEvent() --> (EarlyPostImeInputStage-->NativePostImeInputStage-->ViewPostImeInputStage-->SyntheticInputStage;)
ViewPostImeInputStage mView.dispatchPointerEvent(event)
View.java dispatchPointerEvent() +-->View.java dispatchTouchEvent() --> li.mOnTouchListener.onTouch(this, event) onTouchEvent(event)
                                 +
                                 +--> ViewGroup.java dispatchTouchEvent()

4.2 接收

在分发input数据时,会把 mMonitoringChannels 加入到目标中,然后通过socket也发给该目标,

InputDispatcher::dispatchMotionLocked() --> addMonitoringTargetsLocked() --> for mMonitoringChannels

在WMS时构造,会通过monitorInput()创建,

之后别的服务可通过WMS registerPointerEventListener() unregisterPointerEventListener() 以listener方式获取数据

frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
private WindowManagerService(......) {
......
    if(mInputManager != null) {
        final InputChannel inputChannel = mInputManager.monitorInput(TAG_WM);
        mPointerEventDispatcher = inputChannel != null
                ? new PointerEventDispatcher(inputChannel) : null;

@Override
public void registerPointerEventListener(PointerEventListener listener) {
    mPointerEventDispatcher.registerInputEventListener(listener);
}

@Override
public void unregisterPointerEventListener(PointerEventListener listener) {
    mPointerEventDispatcher.unregisterInputEventListener(listener);
}

monitorInput()流程如下:

InputManagerService.java monitorInput()
    +--> nativeRegisterInputChannel(......, true);
        +--> NativeInputManager::registerInputChannel() 
            +--> mInputManager->getDispatcher()->registerInputChannel() -->
status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel,
        const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {
......
        if (monitor) {
            mMonitoringChannels.push(inputChannel);
        }
......
}

个人笔记

数据的转存

从slot --> RawPointerData --> cookAndDispatch() cookPointerData()进一步处理将值给

mCurrentCookedState.cookedPointerData,主要为

cookedPointerData.pointerCoords cookedPointerData.pointerProperties

dispatchMotion()时参数传入cookedPointerData,进一步将数据封装为

NotifyMotionArgs

dispatchMotion(when, policyFlags, mSource,

......

mCurrentCookedStat.cookedPointerData.pointerProperties,
   mCurrentCookedStat.cookedPointerData.pointerCoords,
   mCurrentCookedStat.cookedPointerData.idToIndex,

......

TouchInputMapper::sync() +-> syncTouch(when, next); --> 数据从slot到outState->rawPointerData.pointers[outCount];

+-> processRawTouches() --> cookAndDispatch() --> dispatchTouches() --> dispatchMotion()

数据处理完后将 NotifyMotionArgs 压入mArgsQueue

TouchInputMapper::dispatchMotion() --> getListener()->notifyMotion(&args) -->

frameworks/native/services/inputflinger/InputListener.cpp

void NotifyMotionArgs::notify(const sp<InputListenerInterface>& listener) const {

listener->notifyMotion(this);

}

void QueuedInputListener::notifyMotion(const NotifyMotionArgs* args) {

mArgsQueue.push(new NotifyMotionArgs(*args));

}

void MultiTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) {

size_t inCount = mMultiTouchMotionAccumulator.getSlotCount();

......

for (size_t inIndex = 0; inIndex < inCount; inIndex++) {
    const MultiTouchMotionAccumulator::Slot* inSlot =
            mMultiTouchMotionAccumulator.getSlot(inIndex);

......

RawPointerData::Pointer& outPointer = outState->rawPointerData.pointers[outCount];
    outPointer.x = inSlot->getX();
    outPointer.y = inSlot->getY();

......

}

数据从MultiTouchMotionAccumulator::Slot 转到 RawPointerData::Pointer

void TouchInputMapper::processRawTouches(bool timeout) {
....//在处理mRawStatesPending数据时,一个一个取出给mCurrentRawState,然后 cookAndDispatch进一步处理
    for(count = 0; count < N; count++) {
        const RawState& next = mRawStatesPending[count];
......//给mCurrentRawState
        mCurrentRawState.copyFrom(next);
......//cookAndDispatch加工并分发
        cookAndDispatch(mCurrentRawState.when);

void TouchInputMapper::cookPointerData() {
    uint32_t currentPointerCount = mCurrentRawState.rawPointerData.pointerCount;

    mCurrentCookedState.cookedPointerData.clear();
......//将数据进一步的处理,例如,计算旋转后的值
    // Walk through the the active pointers and map device coordinates onto
    // surface coordinates and adjust for display orientation.
    for (uint32_t i = 0; i < currentPointerCount; i++) {
        const RawPointerData::Pointer& in = mCurrentRawState.rawPointerData.pointers[i];
......
        case DISPLAY_ORIENTATION_90:
            x = float(yTransformed - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
            y = float(mRawPointerAxes.x.maxValue - xTransformed) * mXScale + mXTranslate;
......//将值给 cookedPointerData.pointerCoords
        // Write output coords.
        PointerCoords& out = mCurrentCookedState.cookedPointerData.pointerCoords[i];
        out.clear();
        out.setAxisValue(AMOTION_EVENT_AXIS_X, x);
        out.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
        out.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pressure);
......//将值给 cookedPointerData.pointerProperties
        // Write output properties.
        PointerProperties& properties =
                mCurrentCookedState.cookedPointerData.pointerProperties[i];
        uint32_t id = in.id;
        properties.clear();
        properties.id = id;
        properties.toolType = in.toolType;

        // Write id index.
        mCurrentCookedState.cookedPointerData.idToIndex[id] = i;
......

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

The Smashing Book

The Smashing Book

Jacob Gube、Dmitry Fadeev、Chris Spooner、Darius A Monsef IV、Alessandro Cattaneo、Steven Snell、David Leggett、Andrew Maier、Kayla Knight、Yves Peters、René Schmidt、Smashing Magazine editorial team、Vitaly Friedman、Sven Lennartz / 2009 / $ 29.90 / € 23.90

The Smashing Book is a printed book about best practices in modern Web design. The book shares technical tips and best practices on coding, usability and optimization and explores how to create succes......一起来看看 《The Smashing Book》 这本书的介绍吧!

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

html转js在线工具
html转js在线工具

html转js在线工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换