Android系统--输入系统(十一)Reader线程_简单处理
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线程
在输入子系统中,Java程序调用C程序时,会构造一个NativeInputManager对象
NativeInputManager对象包括:
- mReader (实现具体功能)
- mReaderThread(实现简单循环)
- mDispatcher
- mDispatcherThread
mReader具体功能
3.1 mReader使用EventHub管理多个输入设备,多个输入设备存储在mDevices容器中
mDevices中有一个Device对象,表示具体的输入设备
mDevices对象中有keyMap对象,用于进行内核扫描码转化为Android具体按键
3.2 在Reader线程中,使用Epoll机制来检测设备节点的添加和删除,以及该设备节点是否有数据产生,如果有数据,就会读到数据,将读到的数据会构建为一个RawEvent类中,并进行处理,处理的过程便是根据RawEvent中的类型进行本篇博文所述的处理,就不在赘述。
6. Reader线程简单处理各类调用关系图
Android系统--输入系统(十一)Reader线程_简单处理的更多相关文章
- Android系统--输入系统(八)Reader线程_使用EventHub读取事件
Android系统--输入系统(八)Reader线程_使用EventHub读取事件 1. Reader线程工作流程 获得事件 size_t count = mEventHub->getEvent ...
- Android系统--输入系统(九)Reader线程_核心类及配置文件
Android系统--输入系统(九)Reader线程_核心类及配置文件 1. Reader线程核心类--EventHub 1.1 Reader线程核心结构体 实例化对象:mEventHub--表示多个 ...
- Android系统--输入系统(十)Reader线程_核心类及配置文件深入分析
Android系统--输入系统(十)Reader线程_核心类及配置文件深入分析 0. 前言 个人认为该知识点阅读Android源代码会不仅容易走进死胡同,并且效果并不好,前脚看完后脚忘记,故进行总结, ...
- Android系统--输入系统(十二)Dispatch线程_总体框架
Android系统--输入系统(十二)Dispatch线程_总体框架 1. Dispatch线程框架 我们知道Dispatch线程是分发之意,那么便可以引入两个问题:1. 发什么;2. 发给谁.这两个 ...
- Android系统--输入系统(十七)Dispatcher线程_分发dispatch
Android系统--输入系统(十七)Dispatcher线程_分发dispatch 1. 回顾 InputRead线程从输入设备当中得到输入事件 对于读到输入事件稍作处理,比如紧急事件,来电时候按下 ...
- Android系统--输入系统(七)Reader_Dispatcher线程启动分析
Android系统--输入系统(七)Reader_Dispatcher线程启动分析 1. Reader/Dispatcher的引入 对于输入系统来说,将会创建两个线程: Reader线程(读取事件) ...
- Android系统--输入系统(十三)Dispatcher线程情景分析_Reader线程传递事件
Android系统--输入系统(十三)Dispatcher线程情景分析_Reader线程传递事件 1. 输入按键 我们知道Android系统的按键分为三类:(1)Global Key;(2)Syste ...
- Android系统--输入系统(十四)Dispatcher线程情景分析_dispatch前处理
Android系统--输入系统(十四)Dispatcher线程情景分析_dispatch前处理 1. 回顾 我们知道Android输入系统是Reader线程通过驱动程序得到上报的输入事件,还要经过处理 ...
- Android系统--输入系统(三)必备Linux知识_双向通信(scoketpair)
Android系统--输入系统(三)必备Linux知识_双向通信(scoketpair) 引入 1. 进程和APP通信 创建进程 读取.分发 - 进程发送输入事件给APP 进程读取APP回应的事件 输 ...
随机推荐
- SignalR指定用户推送消息
一.首先,在MVC项目中安装SingalR包(SingalR2.0需要.net4.5以上,VS2010可以安装1.1.3版本,本例为VS2010+SignalR1.1.3). 打开工具-NuGet程序 ...
- python中的I/O
先看比较简单的,标准输入输出流 输出到表屏幕: >>>str = "hello, python" >>>print "content: ...
- iOS回顾笔记(08) -- 自定义Cell的类型和创建步骤总结
iOS回顾笔记(08) -- 自定义Cell的类型和创建步骤总结 项目中我们常见的自定义cell主要分为两种 等高cell:如应用列表.功能列表 非等高cell:如微博列表.QQ聊天页面 下面对这 ...
- java学习笔记 --- 面向对象2
一.匿名对象 (1)没有名字的对象 (2)应用场景 A:调用方法,仅仅只调用一次的时候. class Car { //描述属性.颜色,轮胎数. String color; int number; ...
- 1、初识Activity
Activity是Android的基本组成部分,是人机交互程序入口:一个Android项目由多个Activity组成,所有的显示组件必须放在Activity上才能进行显示. (1)Android项目工 ...
- 如何将多条update语句合并为一条
需求: 如何将多条update语句合并为一条update语句:如,update table1 set col='2012' where id='2014001' update table1 ...
- iptables初探
一,前言 本来想起个名字叫做"小白都是怎么学习iptables的?"或者"你为什么还不了解iptables?"等等,就像简书上的头条文章,虽然被说成" ...
- Android Weekly Notes Issue #251
Android Weekly Issue #251 April 2nd, 2017 Android Weekly Issue #251. 本期内容: Android O新增的API: View的too ...
- 浅析Java中synchronized与static synchronized
synchronized关键字 synchronized是进行同步处理而保证线程安全.在一个方法中,如果是方法内的私有变量,那个这个变量是线程安全的,但是类中的实例变量是可能会出现线程安全问题的,当多 ...
- Windows入门基础:2.vs2013中Icon显示
第一:系统小图标的显示 wndclass.hIcon = LoadIcon(NULL,IDI_WARNING); //LoadIcon函数的第一的参数要为0,第二个参数是系统自定义的ID号: IDI_ ...