Android系统--输入系统(十一)Reader线程_简单处理

摘要:
Reader线程只负责输入事件的简单处理。Dispatch线程负责最复杂的处理。Dispatch线程将在以下博客文章中具体分析=deviceId){break;}batchSize+=1;}#如果DEBUG_RAW_ EVENTSALOGD;#endifprocessEventsForDeviceLocked;}否则{switch{case EventHubInterface::DEVICE_ADDED://处理设备插入addDeviceLocked;break;case EventHubInterface::DEVICI_REMOVED://处理设备拉取removeDeviceLocked;break;caseEventHubInterface=:FINISHED_DEVICE_SCAN:handeConfigurationChangedLocked;brok;默认值:ALOG_ASSERT;//can‘that appenbreak;}}count-=批次大小;rawEvent+=batchSize;}}3.插入输入设备以处理3.1创建InputDevice对象InputReader。cppvoidInputReader::addDeviceLocked{…InputDevice*device=createDeviceLocked;//首先创建InputDevice对象device-˃configure;device-˃reset;如果{ALOGI;}否则{ALOGI;}m设备。添加;//将InputDevice添加到mDevice,mDevice记录管理输入设备bumpGenerationLocked();}扩展:我们知道在输入系统中,我们使用EventHub类来管理多个输入设备。为什么在InputReader中构建mDevices来记录和管理多个输入设备?也就是说,Android输入系统采用了分层功能。

Android系统--输入系统(十一)Reader线程_简单处理

1. 引入

Reader线程主要负责三件事情
  • 获得输入事件
  • 简单处理
  • 上传给Dispatch线程
InputReader.cpp
void InputReader::loopOnce() {

    ......

    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE); //获得事件

    { // acquire lock
        AutoMutex _l(mLock);
        mReaderIsAliveCondition.broadcast();

    if (count) {
        processEventsLocked(mEventBuffer, count); //进行简单处理
    }

    ......

    mQueuedListener->flush();
}
前面我们已经分析其如何获取输入事件以及涉及中重要的数据结构,本次博文主要阐述其如何处理输入事件。Reader线程只是对输入事件进行简单的处理,大多数复杂处理由Dispatch线程负责处理,在后面的博文会具体分析Dispatch线程。

2. Reader线程的简单处理概述

根据所获得的RawEvent中的type参数进行处理
  • ADD Device
  • Remove Device
  • 真正的输入事件
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分支对真正的事件进行处理
    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) {
        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;
    }
}

3. 插入输入设备处理

3.1 创建InpuDevice对象
InputReader.cpp
void InputReader::addDeviceLocked(nsecs_t when, int32_t deviceId) {
    ......

    InputDevice* device = createDeviceLocked(deviceId, controllerNumber, identifier, classes); //首先创建InputDevice对象
     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.add(deviceId, device); //将InputDevice添加到mDevice中,mDevice记录管理输入设备
    bumpGenerationLocked();
}
扩展:我们知道在输入系统中我们使用EeventHub类管理多个输入设备(之前博文已经具体分析),为什么在InputReader中又要构建一个mDevices来记录管理多个输入设备呢? 即Android输入系统当中采用分层作用。
输入系统中的分层作用:
(1)EventHub中有mDvices类记录管理一个输入设备,主要作用是读取事件
mDevices类
- fd : int //设备节点所打开的文件句柄
- identifier : const InputDeviceIdentifier //记录厂商信息,存储了设备的供应商、型号等信息
- keyBitmask[] : uint8_t
- configurationFile : String8 //IDC文件名
- configuration : PropertyMap* //IDC属性:(内嵌OR外接)设备
- keyMap : KeyMap //保存配置文件(kl,kcm)
(2)InputReader中有mDvices链表中使用InputDevice对象记录管理一个输入设备,主要用来处理事件
InputDevice类
- InputReaderContext* mContext;
- int32_t mId; //通过mId从EventHub中找到对应的输入设备
- Vector<InputMapper*> mMappers; //处理上报的事件
总结:InputDevice主要负责处理事件,并不需要关心设备的具体信息,其中具体信息由EventHub中mDevice类记录,当需要设备信息时,可以通过mId找到对应设备,提取信息
3.2 根据输入设备类别,添加Mapper,主要负责之后的按键映射。
InputReader.cpp

InputDevice* InputReader::createDeviceLocked(int32_t deviceId, int32_t controllerNumber,
        const InputDeviceIdentifier& identifier, uint32_t classes) {
    InputDevice* device = new InputDevice(&mContext, deviceId, bumpGenerationLocked(),
            controllerNumber, identifier, classes);

    // External devices.
    if (classes & INPUT_DEVICE_CLASS_EXTERNAL) {
        device->setExternal(true);
    }

    // Switch-like devices.
    if (classes & INPUT_DEVICE_CLASS_SWITCH) {
        device->addMapper(new SwitchInputMapper(device));
    }

    // Vibrator-like devices.
    if (classes & INPUT_DEVICE_CLASS_VIBRATOR) {
        device->addMapper(new VibratorInputMapper(device));
    }

    // Keyboard-like devices.
    uint32_t keyboardSource = 0;
    int32_t keyboardType = AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC;
    if (classes & INPUT_DEVICE_CLASS_KEYBOARD) {
        keyboardSource |= AINPUT_SOURCE_KEYBOARD;
    }
    if (classes & INPUT_DEVICE_CLASS_ALPHAKEY) {
        keyboardType = AINPUT_KEYBOARD_TYPE_ALPHABETIC;
    }
    if (classes & INPUT_DEVICE_CLASS_DPAD) {
        keyboardSource |= AINPUT_SOURCE_DPAD;
    }
    if (classes & INPUT_DEVICE_CLASS_GAMEPAD) {
        keyboardSource |= AINPUT_SOURCE_GAMEPAD;
    }

    if (keyboardSource != 0) {
        device->addMapper(new KeyboardInputMapper(device, keyboardSource, keyboardType));
    }

    // Cursor-like devices.
    if (classes & INPUT_DEVICE_CLASS_CURSOR) {
        device->addMapper(new CursorInputMapper(device));
    }

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

    // Joystick-like devices.
    if (classes & INPUT_DEVICE_CLASS_JOYSTICK) {
        device->addMapper(new JoystickInputMapper(device));
    }

    return device;
}
总的来说,添加新设备的过程就是这样子,拿键盘输入来说,首先创建一个InputDevice类记录,并且它添加KeyboardInputMapper对象,使用该对象进行处理,移除过程与添加过程类似就不具体分析了。

4. 真正的输入设备上报事件处理

(1)设备产生的输入事件,主要是通过调用processEventsForDeviceLocked函数进行处理
InputReader.cpp
void InputReader::processEventsForDeviceLocked(int32_t deviceId,
const RawEvent* rawEvents, size_t count) {
    ......

    device->process(rawEvents, count); //直接调用process进行处理
}
(2)process函数中将InputDvices对象中的mMappers一个个取出来,调用其process函数。
InputReader.cpp
void InputDevice::process(const RawEvent* rawEvents, size_t count) {
    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 {

        //将InputDvices对象中的mMappers一个个取出来,调用其process函数
            for (size_t i = 0; i < numMappers; i++) {
              InputMapper* mapper = mMappers[i]; //
              mapper->process(rawEvent);
           }
        }
    }
}
(3)这里我们用键盘的process函数进行分析
- 进行内核扫描码转化Android系统所需要的按键码,转化成功则处理该按键
InputReader.cpp
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;
        /*
        *调用EventHub中的mapKey函数进行转化
        *传入参数
        *scanCode:驱动程序上报的扫描码;keyCode:转化之后的Android使用的按键值
        */
     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;
        }
    }
}
}
- 扫描码转化过程(具体在上次博文已经论述):
EventHub.cpp
status_t EventHub::mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode,
int32_t* outKeycode, uint32_t* outFlags) const {
AutoMutex _l(mLock);
    Device* device = getDeviceLocked(deviceId);

    if (device) {
        // Check the key character map first.
        //首先使用KCM文件进行映射,映射过程在上次博文已经详细分析
        sp<KeyCharacterMap> kcm = device->getKeyCharacterMap();
    if (kcm != NULL) {
        if (!kcm->mapKey(scanCode, usageCode, outKeycode)) {
            *outFlags = 0;
            return NO_ERROR;
        }
}

    // Check the key layout next.
    //其次使用keylayout文件进行映射
    if (device->keyMap.haveKeyLayout()) {
        if (!device->keyMap.keyLayoutMap->mapKey(
        scanCode, usageCode, outKeycode, outFlags)) {
            return NO_ERROR;
        }
    }
}

    *outKeycode = 0;
    *outFlags = 0;
    return NAME_NOT_FOUND;
- 在Reader线程中,只是将内核上报的扫描码,转化为Android系统所使用的按键码,然后重新构造一个args,将其发给下一级的Dispatch线程进行处理。
InputReader.cpp
void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t keyCode,
int32_t scanCode, uint32_t policyFlags) {

    ......

    //根据扫描码scanCode、按键码keyCode、newMetaState、downTime按下的时间进行处理     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); //通知Listener处理,Dispatch线程会监听该事件,并处理,下次博文会具体分析
}

5. 总结Reader线程

  1. 在输入子系统中,Java程序调用C程序时,会构造一个NativeInputManager对象

  2. NativeInputManager对象包括:

    • mReader (实现具体功能)
    • mReaderThread(实现简单循环)
    • mDispatcher
    • mDispatcherThread
  3. mReader具体功能

    3.1 mReader使用EventHub管理多个输入设备,多个输入设备存储在mDevices容器中

    • mDevices中有一个Device对象,表示具体的输入设备

    • mDevices对象中有keyMap对象,用于进行内核扫描码转化为Android具体按键

    3.2 在Reader线程中,使用Epoll机制来检测设备节点的添加和删除,以及该设备节点是否有数据产生,如果有数据,就会读到数据,将读到的数据会构建为一个RawEvent类中,并进行处理,处理的过程便是根据RawEvent中的类型进行本篇博文所述的处理,就不在赘述。

6. Reader线程简单处理各类调用关系图

Android系统--输入系统(十一)Reader线程_简单处理第1张

免责声明:文章转载自《Android系统--输入系统(十一)Reader线程_简单处理》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇吴裕雄 python 机器学习——数据预处理字典学习模型利用shell脚本调用ansible自动化实现企业备份基本环境下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

input type="submit" 和"button"的区别及表单提交

在一个页面上画一个按钮,有四种办法: 1 <input type="button" /> 这就是一个按钮。如果你不写javascript 的话,按下去什么也不会发生。 2 <input type="submit" /> 这样的按钮用户点击之后会自动提交 form,除非你写了javascript 阻止它。 3 <butto...

Js中清空文件上传字段(input type=file )

表单中type=file字段的value属性无法由js来附值,一但选中某个文件后,如果用户不手动去清空那么这个值将保留,提交表单时对应文件也会被提交上去.当然在服务器上会再次验证,不过为了避免上传不必要的的文件,还是有必要提前在客户端验证失败后将文件上传字段清空.  具体做法是将file元素从DOM中移除,然后添加个"同名同姓"的上去,以下是基于JQuer...

Hadoop实战实例

      Hadoop实战实例        Hadoop 是Google MapReduce的一个Java实现。MapReduce是一种简化的分布式编程模式,让程序自动分布到一个由普通机器组成的超大集群上并发执行。就如同java程序员可以不考虑内存泄露一样, MapReduce的run-time系统会解决输入数据的分布细节,跨越机器集群的程序执行...

避免浏览器自动填充表单的解决方式(转载请注明出处)

  以前在做项目的时候遇到过这个问题,当时年少太轻狂,没有想过是为什么会发生这样的问题,只觉得作为一个用户,每次在登录网站的时候很有用,很便捷,甚至觉得这个自动填充功能,嗯, 真棒!但是,这次又遇到了这个问题,我不禁陷入了沉思。。。为什么会有自动填充呢?为什么会变成黄色框框呢?作为一个开发者,在我不需要它自动填充的时候,它真的,很碍事!   于是乎~各种谷...

Jquery面试题整合

一、Jquery测试题 下面哪种不是jquery的选择器?(单选) A、基本选择器 B、后代选择器 C、类选择器 D、进一步选择器 考点:jquery的选择器 (C) 当DOM加载完成后要执行的函数,下面哪个是正确的?(单选) jQuery(expression, [context]) B、jQuery(html,[ownerDocument]) C、jQ...

input框输入金额处理的解决办法

最近已经上线的项目突然出现了input输入金额删除时出现问题,网上各种搜索都没有搜到想要的,今天就以react框架为例子进行代码奉献,我会把需求及解决方法写出来,希望对朋友们有用。 如有更好地实现方式欢迎指点! 一、需求 1、只允许输入数字 2、只允许输入一个小数点,且小数点只能有一个,后面最多可输两位小数 3、金额转成大写 4、控制金额输入的位数 二、实...