Android 输入系统之InputReader篇

上篇文章我们大概讲解了一下EventHub的工作原理,有提到EventHub将原始input数据粗加工成RawEvent给InputReader。

这篇文章会讲解InputReader如何来处理这些RawEvent。。。。。

为了使文章保持连贯,先大概解释一下InputReader是如何被创建和工作起来的。。。。

Java层管理输入系统的是InputManagerService,它属于核心的系统service,当然会在SystemServer中创建。

ServerThread.java---->initAndLoop()        顺便提一下,我使用的是MTK8127 4.4的代码做分析,mtk做了一些定制可能和Google原生代码有区别,但是本质是一样的。

InputManagerService inputManager = null;

……………………

//注意构造InputManagerService的参数wmHandler,为了保证输入事件相应的实时性,InputManagerService会有很多任务在wmHandler中处理

  inputManager = new InputManagerService(context, wmHandler);

            Slog.i(TAG, "Window Manager");
            wm = WindowManagerService.main(context, power, display, inputManager,
                    wmHandler, factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL,
                    !firstBoot, onlyCore);
            ServiceManager.addService(Context.WINDOW_SERVICE, wm);
            ServiceManager.addService(Context.INPUT_SERVICE, inputManager);

            ActivityManagerService.self().setWindowManager(wm);

            inputManager.setWindowManagerCallbacks(wm.getInputMonitor());

//启动InputManagerService

            inputManager.start();

            display.setWindowManager(wm);
            display.setInputManager(inputManager);


我们看看InputManagerService的构造函数:

public InputManagerService(Context context, Handler handler) {
        this.mContext = context;
        this.mHandler = new InputManagerHandler(handler.getLooper());

        mUseDevInputEventForAudioJack =
                context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack);
        Slog.i(TAG, "Initializing input manager, mUseDevInputEventForAudioJack="
                + mUseDevInputEventForAudioJack);
        mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());
    }

初始化工作都在nativeInit,看看:

com_android_server_input_InputManagerService.cpp---->nativeInit()

static jint nativeInit(JNIEnv* env, jclass clazz,
        jobject serviceObj, jobject contextObj, jobject messageQueueObj) {
    sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
    if (messageQueue == NULL) {
        jniThrowRuntimeException(env, "MessageQueue is not initialized.");
        return 0;
    }
//创建NativeInputManager,它将用来和Java层的InputManagerService通信
    NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,
            messageQueue->getLooper());
    im->incStrong(0);
    return reinterpret_cast<jint>(im);
}

再进一步走入NativeInputManager构造函数:

NativeInputManager::NativeInputManager(jobject contextObj,
        jobject serviceObj, const sp<Looper>& looper) :
        mLooper(looper) {
    JNIEnv* env = jniEnv();

    mContextObj = env->NewGlobalRef(contextObj);
    mServiceObj = env->NewGlobalRef(serviceObj);

    {
        AutoMutex _l(mLock);
        mLocked.systemUiVisibility = ASYSTEM_UI_VISIBILITY_STATUS_BAR_VISIBLE;
        mLocked.pointerSpeed = 0;
        mLocked.pointerGesturesEnabled = true;
        mLocked.showTouches = false;
    }
//大名鼎鼎的EventHub被创建出来了,但愿大家没有忘记上篇文章讲过,EventHub的构造函数通过INotify+Epoll使"/dev/input"目录被监听
    sp<EventHub> eventHub = new EventHub();

//构造native层的InputManager

    mInputManager = new InputManager(eventHub, this, this);
}

继续看InputManager构造函数:

InputManager::InputManager(
        const sp<EventHubInterface>& eventHub,
        const sp<InputReaderPolicyInterface>& readerPolicy,
        const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {

// InputDispatcher被创建

    mDispatcher = new InputDispatcher(dispatcherPolicy);

// InputReader被创建

    mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
    initialize();
}

到这里后,InputDispatcher和InputReader都被创建出来了。

继续看initialize();

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

很简单,创建了两个thread InputReaderThread&InputDispatcherThread。

OK,通过一步一步构造,Java和native层都已经万事俱备,只欠东风了。。。。

东风在哪里?

东风已经在上面出现了,,,inputManager.start();

这个过程和前面类似,不一步步分析了,它最终会调用InputManager.cpp的start()函数。。。。。。。写到这里才发现,这里涉及到的文件比较多,分布也比较散,我比较懒,不想一个个列出来,大家把整个framework加到source insight就非常方便了,跟踪代码神器!

我们看看InputManager.cpp的start()函数:

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

这里mDispatcherThread&mReaderThread两个线程都开始工作了,关于这两个线程工作原理我简单讲一下,启动线程时会不断的调用线程的threadLoop()函数,直到其返回false则停止。实际上,Android系统mDispatcherThread&mReaderThread的threadLoop()函数都是反馈true,所以这两个线程一旦开机便不会停止。

前面已经过了,开机线程实际上就是不断调用threadLoop()函数,这章我们讲的是InputReader,所以我们来看看他的threadLoop():

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

void InputReader::loopOnce() {
    int32_t oldGeneration;
    int32_t timeoutMillis;
    bool inputDevicesChanged = false;
    Vector<InputDeviceInfo> inputDevices;
    { // acquire lock
        AutoMutex _l(mLock);

        oldGeneration = mGeneration;
        timeoutMillis = -1;

        uint32_t changes = mConfigurationChangesToRefresh;
        if (changes) {
            mConfigurationChangesToRefresh = 0;
            timeoutMillis = 0;
            refreshConfigurationLocked(changes);
        } else if (mNextTimeout != LLONG_MAX) {
            nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
            timeoutMillis = toMillisecondTimeoutDelay(now, mNextTimeout);
        }
    } // release lock
//上篇文章讲过,getEvents会将输入事件转化为RawEvent带给InputReader
    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
    { // acquire lock
        AutoMutex _l(mLock);
        mReaderIsAliveCondition.broadcast();

        if (count) {

//读取到了输入事件,进行处理

            processEventsLocked(mEventBuffer, count);
        }

        if (mNextTimeout != LLONG_MAX) {
            nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
            if (now >= mNextTimeout) {
#if DEBUG_RAW_EVENTS
                ALOGD("Timeout expired, latency=%0.3fms", (now - mNextTimeout) * 0.000001f);
#endif
                mNextTimeout = LLONG_MAX;
                timeoutExpiredLocked(now);
            }
        }

        if (oldGeneration != mGeneration) {
            inputDevicesChanged = true;
            getInputDevicesLocked(inputDevices);
        }
    } // release lock

    // Send out a message that the describes the changed input devices.
    if (inputDevicesChanged) {
        mPolicy->notifyInputDevicesChanged(inputDevices);
    }

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

我们只关注processEventsLocked(mEventBuffer, count);

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;
            }
#if DEBUG_RAW_EVENTS
            ALOGD("BatchSize: %d Count: %d", batchSize, count);
#endif

//处理一般的输入事件

            processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
        } else {
            switch (rawEvent->type) {

//处理增加input device add事件

            case EventHubInterface::DEVICE_ADDED:
                addDeviceLocked(rawEvent->when, rawEvent->deviceId);
                break;
//处理增加input device remove事件

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;
    }
}
我们只关注processEventsForDeviceLocked(deviceId, rawEvent, batchSize);

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

这里出现了一个mDevices变量,存储的是InputDevice类型,其实大家可以认为和前面EventHub中的Device是差不多的东西,都是代表一个输入设备。

最终处理还是调用对应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.
    size_t numMappers = mMappers.size();
    for (const RawEvent* rawEvent = rawEvents; count--; rawEvent++) {
#if DEBUG_RAW_EVENTS
        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

        if (mDropUntilNextSync) {
            if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
                mDropUntilNextSync = false;
#if DEBUG_RAW_EVENTS
                ALOGD("Recovered from input event buffer overrun.");
#endif
            } else {
#if DEBUG_RAW_EVENTS
                ALOGD("Dropped input event while waiting for next input sync.");
#endif
            }
        } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) {
            ALOGI("Detected input event buffer overrun for device %s.", getName().string());
            mDropUntilNextSync = true;
            reset(rawEvent->when);
        } else {
            for (size_t i = 0; i < numMappers; i++) {
                InputMapper* mapper = mMappers[i];
                mapper->process(rawEvent);
            }
        }
    }
}

又出现了个InputMapper,并且调用了其process()处理,那么InputMapper又是个啥?哪来的?

要回答这个问题,我们需要退回去,前面的processEventsLocked()函数,我们只分析processEventsForDeviceLocked(),大家可以自己去看看

case EventHubInterface::DEVICE_ADDED:

EventHubInterface::DEVICE_REMOVED:

情况下的处理流程就明白了,当有设备添加时,会向mDevices中添加对应的InputDevice,创建InputDevice的过程就包含了添加InputMapper过程,限于篇幅,这些就不细说了。

InputMapper有很多子类,我们只讨论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)) {
            int32_t keyCode;
            uint32_t flags;

        //这里会通过kl文件对键值进行映射以及添加flag,这样做可以大大降低代码的耦合度,不同的硬件平台,key不同的硬件实现方式,对于上层都不需要做任何调整,只需要修改kl文件即可,关于kl文件中各个字段意思,大家可以自行百度
            if (getEventHub()->mapKey(getDeviceId(), scanCode, usageCode, &keyCode, &flags)) {
                keyCode = AKEYCODE_UNKNOWN;
                flags = 0;
            }

      //处理key
            processKey(rawEvent->when, rawEvent->value != 0, keyCode, scanCode, flags);
        }
        break;
    }
    case EV_MSC: {
        if (rawEvent->code == MSC_SCAN) {
            mCurrentHidUsage = rawEvent->value;
        }
        break;
    }
    case EV_SYN: {
        if (rawEvent->code == SYN_REPORT) {
            mCurrentHidUsage = 0;
        }
    }
    }
}

对于上面出现的EV_MSC,EV_SYN等等是什么东东,我从网上copy了一份表格:

EV_SYN用于标识独立的事件,这些独立的事件时在时间或者空间上是可以分离的,比如在多点触摸中
EV_KEY用于标识按键,按钮或者类似按键的设备状态的变化
EV_REL用于描述 对于轴线相对变化量,如鼠标向左移动5个单位
EV_ABS用于描述 对于轴线的绝对变化量, 比如在触摸屏上的触摸点的坐标
EV_SW标识二进制的开关状态
EV_LED表示设备上的LED是开or关
EV_SND用于标识发送声音到设备
EV_REP表示自动重复的设备
V_FF用于标识发送强制要回馈的命令到设备
EV_PWR对于Power键的一个特殊状态或者切换输入
EV_FF_STATUS用于收到需要强制回馈的设备状态
EV_MSC如果不是这些已存在的状态,那么就用这个标识
多点触屏   大多是EV_ABS, EV_KEY, EV_SYN,有的还设置了EV_MSC
键盘        EV_KEY, EV_SW
鼠标       EV_REL, EV_KEY, EV_ABS
说白了就是用来区分不同类型设备的。。。。

我们接下来看看processKey(rawEvent->when, rawEvent->value != 0, keyCode, scanCode, flags);

void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t keyCode,
        int32_t scanCode, uint32_t policyFlags) {
    nsecs_t now;

    if (down) {
        // Rotate key codes according to orientation if needed.
        if (mParameters.orientationAware && mParameters.hasAssociatedDisplay) {
            keyCode = rotateKeyCode(keyCode, mOrientation);
        }

        // Add key down.
        ssize_t keyDownIndex = findKeyDown(scanCode);
  
        if (keyDownIndex >= 0) {
            // key repeat, be sure to use same keycode as before in case of rotation
            keyCode = mKeyDowns.itemAt(keyDownIndex).keyCode;
        } else {
            // key down
           
            if ((policyFlags & POLICY_FLAG_VIRTUAL)
                    && mContext->shouldDropVirtualKey(when,
                            getDevice(), keyCode, scanCode)) {
                return;
            }

            mKeyDowns.push();
            KeyDown& keyDown = mKeyDowns.editTop();
            keyDown.keyCode = keyCode;
            keyDown.scanCode = scanCode;
        }

        mDownTime = when;
    } else {
        // Remove key down.
        ssize_t keyDownIndex = findKeyDown(scanCode);
        if (keyDownIndex >= 0) {
            // key up, be sure to use same keycode as before in case of rotation
            keyCode = mKeyDowns.itemAt(keyDownIndex).keyCode;
            mKeyDowns.removeAt(size_t(keyDownIndex));
        } else {
            // key was not actually down
            ALOGI("Dropping key up from device %s because the key was not down.  "
                    "keyCode=%d, scanCode=%d",
                    getDeviceName().string(), keyCode, scanCode);
            return;
        }
    }

    int32_t oldMetaState = mMetaState;
    int32_t newMetaState = updateMetaState(keyCode, down, oldMetaState);
    bool metaStateChanged = oldMetaState != newMetaState;
    if (metaStateChanged) {
        mMetaState = newMetaState;
        updateLedState(false);
    }

    nsecs_t downTime = mDownTime;

    // Key down on external an keyboard should wake the device.
    // We don't do this for internal keyboards to prevent them from waking up in your pocket.
    // For internal keyboards, the key layout file should specify the policy flags for
    // each wake key individually.
    // TODO: Use the input device configuration to control this behavior more finely.
    if (down && getDevice()->isExternal()
            && !(policyFlags & (POLICY_FLAG_WAKE | POLICY_FLAG_WAKE_DROPPED))) {
        policyFlags |= POLICY_FLAG_WAKE_DROPPED;
    }

    if (metaStateChanged) {
        getContext()->updateGlobalMetaState();
    }

    if (down && !isMetaKey(keyCode)) {
        getContext()->fadePointer();
    }
    now = systemTime(SYSTEM_TIME_MONOTONIC);
    ALOGD_READER("Latency info debug, processKey now(ms): %lld",now/1000000);

    NotifyKeyArgs args(when, getDeviceId(), mSource, policyFlags,
            down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
            AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, newMetaState, downTime);
    getListener()->notifyKey(&args);
}
这里最终将一个key event封装成一个NotifyKeyArgs调用getListener()->notifyKey(&args);处理了,getListener()得到的其实是mQueuedListener,我们看看mQueuedListener的notifyKey()

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

将NotifyKeyArgs压入到了队列中。然后就没有然后了。。。

其实还有有然后的,让我们回到loopOnce(),最后面有一句:mQueuedListener->flush();

很明显,是要队列中的数据送出去,送给谁呢?

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

注意到args->notify(mInnerListener);

这里的mInnerListener其实就是InputDispatcher,当然下面的内容是下面章节要分析的了。。。。

好了,InputReader的内容就讲到这了,InputReader的基本任务就是从EventHub中取得数据,然后细加工成NotifyArgs传给InputDispatcher,InputDispatcher进一步处理后发送给准备好接受数据的窗口。

顺带提一下,虽然InputReader看上去一直在threadLoop()中死循环,但是大家不要忘了,threadLoop()中会通过getEvent()从EventHub中取数据,而当没有数据时getEvent()会阻塞在epoll_wait中,所以,大部分时间,InputReader还是在睡大觉的。






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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值