Android输入系统(二)IMS的启动过程和输入事件的处理

Android输入系统(二)IMS的启动过程和输入事件的处理,第1张

概述本文首发于微信公众号「刘望舒」关联系列解析WMS系列深入理解JNI系列输入系统系列基于Android8.1前言在上一篇文章中,我们学习了IMS的诞生(创建),IMS创建后还会进行启动,这篇文章我们来学习IMS的启动过程和输入事件的处理。1.IMS的启动过程IMS的创建在SystemServer的startOtherS

本文首发于微信公众号「刘望舒」

关联系列
解析WMS系列
深入理解JNI系列
输入系统系列

基于AndroID 8.1

前言

在上一篇文章中,我们学习了ims的诞生(创建),ims创建后还会进行启动,这篇文章我们来学习ims的启动过程和输入事件的处理。

1.ims的启动过程

ims的创建在SystemServer的startOtherServices方法中,不了解请查看Android输入系统(一)输入事件传递流程和InputManagerService的诞生这篇文章。
frameworks/base/services/java/com/androID/server/SystemServer.java

 private voID () { ...         traceBeginAndSlog("StartinputManagerService");         inputManager = new inputManagerService(context);         traceEnd(); ...         traceBeginAndSlog("StartinputManager");         inputManager.setwindowManagerCallbacks(wm.getinputMonitor());         inputManager.start();         traceEnd();}

创建ims后就会紧接着执行ims的启动。ims的start方法如下所示。
frameworks/base/services/core/java/com/androID/server/input/inputManagerService.java

public voID start() {       Slog.i(TAG, "Starting input manager");       nativeStart(mPtr);              Watchdog.getInstance().addMonitor(this);     ...   }

ims的start方法中,会将自身添加到Watchdog中进行监控,用于定时检测系统关键服务(AMS和WMS等)是否可能发生死锁。
nativeStart方法对应的JNI层的函数是什么呢?查看com_androID_server_input_inputManagerService的ginputManagerMethods数组,不理解JNI的可以查看深入理解JNI系列文章。
frameworks/base/services/core/jni/com_androID_server_input_inputManagerService.cpp

static const JNINativeMethod ginputManagerMethods[] = {...   { "nativeStart", "(J)V",            (voID*) nativeStart },...}

nativeStart方法对应的JNI函数为nativeStart:
frameworks/base/services/core/jni/com_androID_server_input_inputManagerService.cpp

static voID nativeStart(jnienv* env, jclass /* clazz */, jlong ptr) {    NativeinputManager* im = reinterpret_cast<NativeinputManager*>(ptr);    status_t result = im->getinputManager()->start();//1    if (result) {        jniThrowRuntimeException(env, "input manager Could not be started.");    }}

用reinterpret_cast *** 作符将jlong类型的ptr强制转换为原类型(NativeinputManager指针类型)。注释1处会调用inputManager的start函数。
frameworks/native/services/inputflinger/inputManager.cpp

status_t inputManager::start() {    status_t result = mdispatcherThread->run("inputdispatcher", PRIORITY_URGENT_disPLAY);    if (result) {        ALOGE("Could not start inputdispatcher thread due to error %d.", result);        return result;    }    result = mReaderThread->run("inputReader", PRIORITY_URGENT_disPLAY);    if (result) {        ALOGE("Could not start inputReader thread due to error %d.", result);        mdispatcherThread->requestExit();        return result;    }    return OK;}

可以看到inputManager的start函数运行了inputReaderThread和inputdispatcherThread,这两个线程在Android输入系统(一)输入事件传递流程和InputManagerService的诞生提到过,它们在inputManager的构造函数中被创建,其中inputReaderThread中运行了inputReader, inputdispatcherThread中运行了inputdispatcher。

2.inputdispatcher的启动过程

先来回顾下inputdispatcher和inputReader是在哪创建的,inputManager的构造函数如下所示。
frameworks/native/services/inputflinger/inputManager.cpp

inputManager::inputManager(        const sp<EventHubInterface>& eventHub,        const sp<inputReaderPolicyInterface>& readerPolicy,        const sp<inputdispatcherPolicyInterface>& dispatcherPolicy) {    mdispatcher = new inputdispatcher(dispatcherPolicy);    mReader = new inputReader(eventHub, readerPolicy, mdispatcher);    initialize();}

可以看到inputdispatcher和inputReader是有关联的,inputdispatcher会作为一个参数传入到inputReader中。
inputdispatcher是在inputReader之前创建的,这个顺序不能改变,因为要确保inputReader将加工后的输入事件交给inputdispatcher时,inputdispatcher已经被创建。
inputdispatcher的定义如下所示。
frameworks/native/services/inputflinger/inputdispatcher.h

class inputdispatcherThread : public Thread {public:    explicit inputdispatcherThread(const sp<inputdispatcherInterface>& dispatcher);    ~inputdispatcherThread();private:    virtual bool threadLoop();    sp<inputdispatcherInterface> mdispatcher;};}

inputdispatcher.h中定义了threadLoop纯虚函数,inputdispatcher继承了Thread。native的Thread内部有一个循环,当线程运行时,会调用threadLoop函数,如果它返回true并且没有调用requestExit函数,就会接着循环调用threadLoop函数。
查看inputdispatcherThread的threadLoop函数是如何实现的。
frameworks/native/services/inputflinger/inputdispatcher.cpp

bool inputdispatcherThread::threadLoop() {    mdispatcher->dispatchOnce();    return true;}

threadLoop函数中只调用了inputdispatcher的dispatchOnce函数:
frameworks/native/services/inputflinger/inputdispatcher.cpp

voID inputdispatcher::dispatchOnce() {    nsecs_t nextWakeupTime = LONG_LONG_MAX;    { // acquire lock        autoMutex _l(mlock);        mdispatcherIsAliveCondition.broadcast();        if (!haveCommandsLocked()) {//1            dispatchOnceInnerLocked(&nextWakeupTime);//2        }        if (runcommandsLockedInterruptible()) {            nextWakeupTime = LONG_LONG_MIN;        }    } // release lock    nsecs_t currentTime = Now();//3    int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);//4    mLooper->pollOnce(timeoutMillis);}

注释1处用于检查inputdispatcher的缓存队列中是否有等待处理 的命令,如果没有就会执行注释2处的dispatchOnceInnerLocked函数,用来将输入事件分发给合适的Window。注释3处获取当前的时间,结合注释4处,得出inputdispatcherThread需要睡眠的时间为timeoutMillis。最后调用Looper的pollOnce函数使inputdispatcherThread进入睡眠状态,并将它的最长的睡眠的时间设置为timeoutMillis。当有输入事件产生时,inputReader就会将睡眠状态的inputdispatcher
唤醒,inputdispatcher会重新开始分发输入事件。
那么inputReader是如何唤醒inputdispatcherThread的呢? 我们接着往下看。

3.inputReader处理事件过程

inputReader是在inputReaderThread中启动的,inputReaderThread和inputdispatcherThread的定义是类似的,也是继承了Thread并定义了threadLoop纯虚函数。如果处理的事件为键盘输入事件,则调用时序图如下所示。


inputReaderThread的threadLoop函数如下所示。
frameworks/native/services/inputflinger/inputReader.cpp

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

threadLoop函数中只调用了inputReader的loopOnce函数:
frameworks/native/services/inputflinger/inputReader.cpp

voID inputReader::loopOnce() {  ...    //通过EventHub的getEvents函数获取事件信息存在mEventBuffer中    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);//1    { // acquire lock        autoMutex _l(mlock);        mReaderIsAliveCondition.broadcast();        if (count) {            //如果有事件信息,调用processEventsLocked函数对事件进行加工处理            processEventsLocked(mEventBuffer, count);//2        }    ...}

注释1处调用EventHub的getEvents函数来获取设备节点的事件信息到mEventBuffer中,事件信息主要有两种,一种是设备节点的增删事件(设备事件),一种是原始输入事件。注释2处的processEventsLocked函数用于对mEventBuffer中的原始输入事件信息进行加工处理,加工后的输入事件会交由inputdispatcher来处理,processEventsLocked函数如下所示。
frameworks/native/services/inputflinger/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) {            int32_t deviceid = rawEvent->deviceid;            while (batchSize < count) {                if (rawEvent[batchSize].type >= EventHubInterface::FirsT_SYNTHETIC_EVENT                        || rawEvent[batchSize].deviceid != deviceid) {                    break;                }                batchSize += 1;            }            ALOGD("BatchSize: %d Count: %d", batchSize, count);#endif         //处理deviceid所对应的设备的原始输入事件            processEventsForDeviceLocked(deviceid, rawEvent, batchSize);//1        } 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;    }}

inputReader的processEventsLocked函数首先遍历了所有的事件,这些事件用RawEvent对象来表示,将原始
输入事件和设备事件分开处理,其中设备事件分为DEVICE_ADDED、DEVICE_REMOVED和FINISHED_DEVICE_SCAN,这些事件是在EventHub的getEvent函数中生成的。如果是DEVICE_ADDED事件(设备添加事件),inputReader会新建inputDevice对象,用来存储设备信息,并且会将inputDevice存储在
KeyedVector类型的容器mDevices中。
同一个设备的输入事件交给processEventsForDeviceLocked函数来处理。
frameworks/native/services/inputflinger/inputReader.cpp

voID inputReader::processEventsForDeviceLocked(int32_t deviceid,        const RawEvent* rawEvents, size_t count) {    ssize_t deviceIndex = mDevices.indexOfKey(deviceid);//1    if (deviceIndex < 0) {        ALOGW("discarding event for unkNown deviceid %d.", deviceid);        return;    }    inputDevice* device = mDevices.valueAt(deviceIndex);//2    if (device->isIgnored()) {        //ALOGD("discarding event for ignored deviceid %d.", deviceid);        return;    }    device->process(rawEvents, count);}

注释1处根据deviceid从mDevices中获取对应的deviceIndex,注释2处再根据这个deviceIndex从mDevices中获取对应的inputDevice。最后会调用inputDevice的process函数:
frameworks/native/services/inputflinger/inputReader.cpp

voID inputDevice::process(const RawEvent* rawEvents, size_t count) {*    size_t numMappers = mMappers.size();    //遍历处理该inputDevice所有的事件    for (const RawEvent* rawEvent = rawEvents; count--; rawEvent++) {        ALOGD("input event: device=%d type=0x%04x code=0x%04x value=0x%08x when=%lld",                rawEvent->deviceid, rawEvent->type, rawEvent->code, rawEvent->value,                rawEvent->when);#endif        //mDropUntilNextSync的值默认为false,如果设备的输入事件缓冲区溢出,这个值会置为true。        if (mDropUntilNextSync) {            ...        } else {            for (size_t i = 0; i < numMappers; i++) {//1                inputMapper* mapper = mMappers[i];                mapper->process(rawEvent);//2            }        }    }}

首先会遍历inputDevice中的所有的事件,真正加工原始输入事件的是inputMapper对象,由于原始输入事件的类型很多,因此在inputMapper有很多子类,用于加工不同的原始输入事件,比如KeyboardinputMapper用于处理键盘输入事件,touchinputMapper用于处理触摸输入事件。
注释1处遍历所有的inputMapper,在注释2处将原始输入事件交由这些inputMapper来处理,至于是哪个inputMapper来处理,inputReader并不关心。
这里就以处理键盘输入事件为例,KeyboardinputMapper的process函数如下所示。
frameworks/native/services/inputflinger/inputReader.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->value != 0, scanCode, usageCode);//2        }        break;    }   ...    }}

注释1处,如果事件的类型为按键类型的事件,就会调用注释2处的KeyboardinputMapper的processKey函数。
frameworks/native/services/inputflinger/inputReader.cpp

voID KeyboardinputMapper::processKey(nsecs_t when, bool down, int32_t scanCode,        int32_t usageCode) {   ...    NotifyKeyArgs args(when, getdeviceid(), mSource, policyFlags,            down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,            AKEY_EVENT_FLAG_FROM_SYstem, keyCode, scanCode, keyMetaState, downTime);    getListener()->notifyKey(&args);//1}

processKey函数会将加工后的键盘输入事件封装为NotifyKeyArgs,将NotifyKeyArgs通知给inputListenerInterface。
inputdispatcher继承了inputdispatcherInterface,而inputdispatcherInterface继承了inputListenerInterface,因此注释1处实际上是调用了inputdispatcher的notifyKey函数,将NotifyKeyArgs交给inputdispatcher处理。
frameworks/native/services/inputflinger/inputdispatcher.cpp

voID inputdispatcher::notifyKey(const NotifyKeyArgs* args) {  ...    bool neeDWake;    { // acquire lock        mlock.lock();        if (shouldSendKeyToinputFilterLocked(args)) {            mlock.unlock();            policyFlags |= POliCY_FLAG_FILTERED;            if (!mPolicy->filterinputEvent(&event, policyFlags)) {                return; // event was consumed by the filter            }            mlock.lock();        }        int32_t repeatCount = 0;        KeyEntry* newEntry = new KeyEntry(args->eventTime,                args->deviceid, args->source, policyFlags,                args->action, flags, keyCode, args->scanCode,                MetaState, repeatCount, args->downTime);//1        neeDWake = enqueueInboundEventLocked(newEntry);//2        mlock.unlock();    } // release lock    if (neeDWake) {        mLooper->wake();    }}

代码块中采用Mutex互斥锁的形式,在注释1处根据NotifyKeyArgs,重新封装一个KeyEntry对象,代表一次按键数据。注释2处根据KeyEntry,来判断是否需要将睡眠中的inputdispatcherThread唤醒,如果需要,就调用Looper的wake函数进行唤醒,inputdispatcherThread被唤醒后就会重新对输入事件的分发,具体的回头查看第2小节。

总结

本文涉及到了四个关键的类,分别是ims、EventHub、inputdispatcher和inputReader,它们做了如下的工作:

ims启动了inputdispatcherThread和inputReaderThread,分别用来运行inputdispatcher和inputReader。inputdispatcher先于inputReader被创建,inputdispatcher的dispatchOnceInnerLocked函数用来将事件分发给合适的Window。inputdispatcher没有输入事件处理时会进入睡眠状态,等待inputReader通知唤醒。inputReader通过EventHub的getEvents函数获取事件信息,如果是原始输入事件,就将这些原始输入事件交由不同的inputMapper来处理,最终交由inputdispatcher来进行分发。inputdispatcher的notifyKey函数中会根据按键数据来判断inputdispatcher是否要被唤醒,inputdispatcher被唤醒后,会重新调用dispatchOnceInnerLocked函数将输入事件分发给合适的Window。@H_409_404@

原文:大专栏  Android输入系统(二)IMS的启动过程和输入事件的处理


总结

以上是内存溢出为你收集整理的Android输入系统(二)IMS的启动过程和输入事件的处理全部内容,希望文章能够帮你解决Android输入系统(二)IMS的启动过程和输入事件的处理所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址:https://www.54852.com/web/1101020.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2022-05-28
下一篇2022-05-28

发表评论

登录后才能评论

评论列表(0条)

    保存