Android 输入系统(三)InputReader

在之前,整理了输入系统服务InputManagerService(服务的生成、启动),EventHub(读取原始输入事件、设备增删)。接着来了解一下InputReader,InputReader主要工作是从EventHub读取事件、进行加工、将加工好的事件发送到InputDispatcher。

                                                                                                                                                                                         

1 InputReader初始化

由InputManagerService这篇博客中,可知InputManagerService初始化的时候,一层层调用,最终在InputManager中初始化InputReader和InputReaderThread。

先看下InputReader的构造函数

InputReader::InputReader(const sp<EventHubInterface>& eventHub,
        const sp<InputReaderPolicyInterface>& policy,
        const sp<InputListenerInterface>& listener) :
        mContext(this), mEventHub(eventHub), mPolicy(policy),
        mGlobalMetaState(0), mGeneration(1),
        mDisableVirtualKeysTimeout(LLONG_MIN), mNextTimeout(LLONG_MAX),
        mConfigurationChangesToRefresh(0) {
    mQueuedListener = new QueuedInputListener(listener);

    { // acquire lock
        AutoMutex _l(mLock);

        refreshConfigurationLocked(0);
        updateGlobalMetaStateLocked();
    } // release lock
}
初始化必要的变量,mEventHub对应EventHub对象,mPolicy对应NativeInputManager对象,mGlobalMetaState表示控制键的状态,mGeneration表示输入设备的状态,mQueuedListener用来将加工后的按键事件传到InputDispatcher。

                                                                                                                                                                                         

InputReaderThread循环体

InputManagerService的start() 层层调用 最后调用到 InputManager的start()中。

result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
InputReaderThread开始运行,以下是InputReaderThread的线程循环,返回true则循环不断的调用threadLoop()方法,返回false则退出循环。
bool InputReaderThread::threadLoop() {
    mReader->loopOnce();
    return true;
}

该线程中会循环不断的执行mReader->loopOnce();看一下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;
	//查看InputReader配置是否修改,如界面大小、方向、键盘布局重新加载、指针速度改变等
        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
    //获取输入事件、设备增删事件,count为事件数量。
    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) {
                mNextTimeout = LLONG_MAX;
                timeoutExpiredLocked(now);
            }
        }

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

    // 发送一个消息,该消息描述已更改的输入设备。
    if (inputDevicesChanged) {
        mPolicy->notifyInputDevicesChanged(inputDevices);
    }
    mQueuedListener->flush();  //将事件传到InputDispatcher.
}

loopOnce()函数先复制mConfigurationChangesToRefresh的值,看mConfigurationChangesToRefresh是否有更改,如果mConfigurationChangesToRefresh有修改则刷新Configuration(refreshConfigurationLocked()),timeoutMillis设置为0.
然后通过EventHub->getEvents(),获取输入事件和设备增删事件,count为读取的事件数量,mEventBuffer存储着事件。由上一篇文章可知getEvents()是阻塞的,只有当有事件或者被wake才会被唤醒向下执行。

                                                                                                                                                                                         

processEventsLocked 处理事件

接着判断count的值,如果有读到事件(count大于0),则调用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;
            }
            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;  //count减少已处理事件个数,表示剩余事件个数
        rawEvent += batchSize;  //rawEvent指针向后移动batchSize个RawEvent对象,也就是指到该处理的事件上。
    }
}

processEventsLocked()函数中会遍历所有的事件,分别进行处理。其处理的事件类型分为四种:原始输入事件、设备加载事件、设备卸载事件及FINISHED_DEVICE_SCAN事件

                                                                                                                                                                                         

1 设备加载事件

void InputReader::addDeviceLocked(nsecs_t when, int32_t deviceId) {
    // 通过设备id(deviceId)获取mDevices中该设备下标,从而判断是否包含该设备。
    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
    //查看mDevices是否包含该设备,-2表示没有包含, 大于等于0表示包含。
    if (deviceIndex >= 0) {
        ALOGW("Ignoring spurious device added event for deviceId %d.", deviceId);
        return;
    }
    //获取设备厂商信息
    InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(deviceId);
    //获取设备类型
    uint32_t classes = mEventHub->getDeviceClasses(deviceId);
    //创建设备inputDevice。
    InputDevice* device = createDeviceLocked(deviceId, identifier, classes);
    device->configure(when, &mConfig, 0);
    device->reset(when);

    if (device->isIgnored()) {
        ALOGI("Device added: id=%d, name='%s' (ignored non-input device)", deviceId,
                identifier.name.string());
    } else {
        ALOGI("Device added: id=%d, name='%s', sources=0x%08x", deviceId,
                identifier.name.string(), device->getSources());
    }
    //将设备添加到mDevices字典中。
    mDevices.add(deviceId, device);
    bumpGenerationLocked();
}

addDeviceLocked()函数中主要通过设备厂商信息、类型来创建InputDevice,配置InputDevice,并将device添加到mDevices中。其中createDeviceLocked()函数用来创建InputDevice对象,并根据设备类别设置不同的mapper,如(INPUT_DEVICE_CLASS_SWITCH对应SwitchInputMapper)。Mapper用来处理输入事件(这个稍后再说)。

                                                                                                                                                                                         

2 设备卸载事件

void InputReader::removeDeviceLocked(nsecs_t when, int32_t deviceId) {
    InputDevice* device = NULL;
    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
    if (deviceIndex < 0) {
        ALOGW("Ignoring spurious device removed event for deviceId %d.", deviceId);
        return;
    }

    device = mDevices.valueAt(deviceIndex);
    mDevices.removeItemsAt(deviceIndex, 1);//从mDevices移除对应的设备
    bumpGenerationLocked();

    if (device->isIgnored()) {
        ALOGI("Device removed: id=%d, name='%s' (ignored non-input device)",
                device->getId(), device->getName().string());
    } else {
        ALOGI("Device removed: id=%d, name='%s', sources=0x%08x",
                device->getId(), device->getName().string(), device->getSources());
    }

    device->reset(when);//设备重置
    delete device; //释放。
}

设备卸载事件很简单,将其从mDevices中移除该设备。

                                                                                                                                                                                         

3 FINISHED_DEVICE_SCAN事件

void InputReader::handleConfigurationChangedLocked(nsecs_t when) {
    // Reset global meta state because it depends on the list of all configured devices.
    updateGlobalMetaStateLocked();

    // 将事件封装成NotifyConfigurationChangedArgs对象
    NotifyConfigurationChangedArgs args(when);
    //将该对象放到mQueuedListener队列中。mQueuedListener->flush()时传到InputDispstcher线程
    mQueuedListener->notifyConfigurationChanged(&args);
}

在设备加载事件或设备卸载事件后会跟着FINISHED_DEVICE_SCAN,用来表示设备加载事件或设备卸载事件已完毕。

                                                                                                                                                                                         

4 原始输入事件

type < EventHubInterface::FIRST_SYNTHETIC_EVENT,当满足该条件时表示原始输入事件,对同一设备的原始输入事件调用processEventsForDeviceLocked()函数进行处理。

void InputReader::processEventsForDeviceLocked(int32_t deviceId,
        const RawEvent* rawEvents, size_t count) {
    //通过设备id获取mDevices中存储的下标
    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
    if (deviceIndex < 0) {  //没有对应设备
        ALOGW("Discarding event for unknown deviceId %d.", deviceId);
        return;
    }
    //通过下标从mDevices取出对应的设备。
    InputDevice* device = mDevices.valueAt(deviceIndex);
    if (device->isIgnored()) {
        //ALOGD("Discarding event for ignored deviceId %d.", deviceId);
        return;
    }
    //设备处理事件
    device->process(rawEvents, count);
}

通过设备id从字典中获取InputDevice,调用InputDevice->process()函数进行处理,接着看InputDevice的process()函数。

void InputDevice::process(const RawEvent* rawEvents, size_t count) {
    size_t numMappers = mMappers.size();
    for (const RawEvent* rawEvent = rawEvents; count--; rawEvent++) {

        if (mDropUntilNextSync) {
            ......
        } else {
            for (size_t i = 0; i < numMappers; i++) {
                InputMapper* mapper = mMappers[i];
                mapper->process(rawEvent);
            }
        }
    }
}

该函数中主要遍历所有的事件,将各事件交给InputMapper处理。

之前createDeviceLocked()函数创建InputDevice的时候,会对InputDevice对象调用addMapper,添加到字典mMappers(一个InputDevice可能有一个或多个mapper,主要取决与设备类型)。InputDevice不知道、也不管哪个InputMapper可以处理这些事件,只需要让所有的InputMapper都去加工这些事件,InputMapper中会对事件进行判断是否是自己该处理的事件,如果不是则什么都不做,如果是则进行相应处理。


InputMapper有多个子类:SwitchInputMapper、VibratorInputMapper、KeyboardInputMapper、CursorInputMapper、MultiTouchInputMapper、SingleTouchInputMapper、JoystickInputMapper等。

这里只说一下KeyboardInputMapper。

                                                                                                                                                                                         

KeyboardInputMapper

void KeyboardInputMapper::process(const RawEvent* rawEvent) {
    switch (rawEvent->type) {
    case EV_KEY: { //事件类型:按键类型
        int32_t scanCode = rawEvent->code;
        int32_t usageCode = mCurrentHidUsage;
        mCurrentHidUsage = 0;
	//排除鼠标按键,鼠标按键由CursorInputMapper处理。
        if (isKeyboardOrGamepadKey(scanCode)) {
            int32_t keyCode;
            uint32_t flags;
	    //通过EventHub调用mapKey()函数,将扫描码映射出键值。scanCode为扫描码,keyCode用来存键值。
            if (getEventHub()->mapKey(getDeviceId(), scanCode, usageCode, &keyCode, &flags)) {		//映射失败
                keyCode = AKEYCODE_UNKNOWN;
                flags = 0;
            }
            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;
        }
    }
    }
}

getEventHub()->mapKey()用来将扫描码映射成虚拟键值。

如何映射呢?
EventHub创建设备、加载设备时调用openDeviceLocked函数,其中如何设备类型为INPUT_DEVICE_CLASS_KEYBOARD或INPUT_DEVICE_CLASS_JOYSTICK时,会调用EventHub::loadKeyMapLocked函数为此设备加载配置文件,键盘配置文件路径/system/usr/input/或/system/usr/keylayout,不同设备可能会稍有差别。该目录下的文件后缀都是.kl,其中一个处理实体按键的文件内容如下:
key 116   POWER      WAKE
key 102   HOME       WAKE
key 158   BACK       WAKE
key 212   CAMERA     WAKE
key 114   VOLUME_DOWN
key 30    A
。。。。

第一列关键字key,第二列表示扫描码,第三列表示虚拟键值的称,第四列表示此按键的功能。
frameworks/base/include/androidfw/KeycodeLabels.h中数组存储着个虚拟键名和键值。
loadKeyMapLocked函数将虚拟键值的名转换为键值存在结构体中,以扫描码为键。通过mapKey()函数可以获取到扫描码对应的键值和flag。
电源键扫描码为116,键值为26,flags为1。


接着看processKey()函数对事件进行加工

void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t keyCode,
        int32_t scanCode, uint32_t policyFlags) {
    //按键 按下	
    if (down) {
        // 根据屏幕方向,转换键值
        if (mParameters.orientationAware && mParameters.hasAssociatedDisplay) {
            keyCode = rotateKeyCode(keyCode, mOrientation);
        }
	
        //KeyboardInputMapper中的mKeyDown结集合用来存储KeyDown结构体,记录按下的键。
        ssize_t keyDownIndex = findKeyDown(scanCode);
        if (keyDownIndex >= 0) { //表示该扫描码之前被按下,也就是现在是重复按下。
	    //确保多次按下键值一致。
            keyCode = mKeyDowns.itemAt(keyDownIndex).keyCode;
        } else {
            // POLICY_FLAG_VIRTUAL 不做处理
            if ((policyFlags & POLICY_FLAG_VIRTUAL)
                    && mContext->shouldDropVirtualKey(when,
                            getDevice(), keyCode, scanCode)) {
                return;
            }
	    //通过扫描码和键值生成keyDown对象,添加到mKeyDowns集合中。
            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) {
            // mKeyDowns移除抬起的键
            keyCode = mKeyDowns.itemAt(keyDownIndex).keyCode;
            mKeyDowns.removeAt(size_t(keyDownIndex));
        } else {
            //如果按键没有被按下就只被抬起,则事件忽略不处理。
            return;
        }
    }

    bool metaStateChanged = false;
    //控制键的状态、控制键包括alt、shift、ctrl、meta、NumLock、ScrollLock、CapsLock等。
    int32_t oldMetaState = mMetaState;
    //检查是否是控制键,并返回控制键状态。
    int32_t newMetaState = updateMetaState(keyCode, down, oldMetaState);
    if (oldMetaState != newMetaState) {//与之前状态对比,判断控制键状态是否更改
        mMetaState = newMetaState;
        metaStateChanged = true;
        updateLedState(false);
    }

    nsecs_t downTime = mDownTime;

    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();
    }
    //按键信息封装到NotifyKeyArgs对象中。
    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);
}

processKey()函数主要管理mKeyDowns,将按下的按钮保存在mKeyDowns中,抬起的从中移除。

。还有控制键状态的更新。最后将按键事件封装在NotifyKeyArgs对象中。之后就是向InputDispatcher中发送。

getListener() -> InputListenerInterface* InputReader::ContextImpl::getListener() 该函数返回的对象是InputReader构造函数中的mQueuedListener,调用其notifyKey()函数并不会马上发送到InputDispatcher中,而是在InputReader::loopOnce()中调用mQueuedListener->flush(),这才会将这次所有已加工的事件发送到InputDispatcher中。


除了NotifyKeyArgs按键类型事件,还有其他mapper加工封装的事件,其对应的提交方法如下:
NotifyKeyArgs封装按键类型事件      notifyKey
NotifySwitchArgs封装开关类型事件   notifySwitch
NotifyMotionArgs封装手势类型事件   notifyMotion

InputMapper有多个子类,用来处理不同设备,具体分析过程类似。


你可能感兴趣的:(android,加工,InputReader,输入系统)