在前文
Linux/Android——Input系统之frameworks层InputManagerService (六)
这里介绍了android层input服务的启动,其中启动了一个读取来自底层event事件的线程.
而在
Linux/Android——input系统之 kernel层 与 frameworks层交互 (五)
有分析到是通过一个event%d的设备文件交互的,也就是说android层是通过读取event%d来获取event的,这个工作就是InputReader
撰写不易,转载需注明出处:
http://blog.csdn.net/jscese/article/details/42739197
getEvents:
这个是运行在inputread线程里面的,上篇有介绍到,字面意思就是获取事件的,实现在/frameworks/base/services/input/EventHub.cpp中,函数很长,列出几个重要的:
size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
struct input_event readBuffer[bufferSize];
RawEvent* event = buffer;
if (mNeedToScanDevices) { //这个在EventHub初始时为true
mNeedToScanDevices = false;
scanDevicesLocked(); // 扫描去打开设备,后面跟进去看
mNeedToSendFinishedDeviceScan = true;
while (mOpeningDevices != NULL) { //这个指针有指向,代表上面打开了某些input设备
Device* device = mOpeningDevices; // 初始化一个添加event,type 为DEVICE_ADDED
ALOGV("Reporting device opened: id=%d, name=%s\n",
device->id, device->path.string());
mOpeningDevices = device->next;
event->when = now;
event->deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
event->type = DEVICE_ADDED;
event += 1;
mNeedToSendFinishedDeviceScan = true;
if (--capacity == 0) {
break;
while (mPendingEventIndex < mPendingEventCount) { //这个是为处理多个input event 做的一个epoll_event 类型数组
const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];
Device* device = mDevices.valueAt(deviceIndex) ; // 上面会把打开的device 加入到 这个Vector
if (eventItem.events & EPOLLIN) {
int32_t readSize = read(device->fd, readBuffer, //这个之前有提到过。最终会调用到evdev中的read
sizeof(struct input_event) * capacity);
for (size_t i = 0; i < count; i++) {
const struct input_event& iev = readBuffer[i]; //这里把上面读到的input_event 转化过这里的RawEvent
event->deviceId = deviceId;
event->type = iev.type;
event->code = iev.code;
event->value = iev.value;
event += 1;
int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis); //用于等待监测是否有事件可读
// All done, return the number of events we read.
return event - buffer; //指针相减,这个数组的元素个数
scanDevicesLocked:
这个往下就是打开一个input 设备文件,配置并抽象为一个android层这边的input device:
void EventHub::scanDevicesLocked() {
status_t res = scanDirLocked(DEVICE_PATH);
if(res < 0) {
ALOGE("scan dir failed for %s\n", DEVICE_PATH); //这里的PATH为"/dev/input"
if (mDevices.indexOfKey(VIRTUAL_KEYBOARD_ID) < 0) {
createVirtualKeyboardLocked();
继续往下看会依次读取目录下的文件,并调用进openDeviceLocked打开,这个函数也比较长,关注几个地方先:
status_t EventHub::openDeviceLocked(const char *devicePath) {
char buffer[80];
ALOGV("Opening device: %s", devicePath);
int fd = open(devicePath, O_RDWR | O_CLOEXEC);
if(fd < 0) {
ALOGE("could not open %s, %s\n", devicePath, strerror(errno));
return -1;
... // 无非是根据打开的fd获取一些相关参数信息
// Allocate device. (The device object takes ownership of the fd at this point.)
int32_t deviceId = mNextDeviceId++;
Device* device = new Device(fd, deviceId, String8(devicePath), identifier); //这里根据前面获取的参数new成一个device,算是初步打开完成抽象成一个Device了
// Load the configuration file for the device.
loadConfigurationLocked(device); // 这个比较重要 ,加载这个device的配置信息,后面将会根据这个配置来定义规则
... //又是一系列的判断初始化,其中比较重要的就是 device->classes 这个变量,代表了input 设备类型,是键盘,鼠标,触摸屏...
// Register with epoll.
struct epoll_event eventItem; //注册epoll
memset(&eventItem, 0, sizeof(eventItem));
eventItem.events = EPOLLIN;
eventItem.data.u32 = deviceId;
if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, &eventItem)) {
ALOGE("Could not add device fd to epoll instance. errno=%d", errno);
delete device;
return -1;
addDeviceLocked(device); //添加到 KeyedVector中
这个里面有很多原始打印,想深入理解的可以放开调试看看,这里不多做介绍,可以看下 加载配置那个函数loadConfigurationLocked:
void EventHub::loadConfigurationLocked(Device* device) {
device->configurationFile = getInputDeviceConfigurationFilePathByDeviceIdentifier( //根据上面获取到的一些设备信息,进一步去找config文件
device->identifier, INPUT_DEVICE_CONFIGURATION_FILE_TYPE_CONFIGURATION);
if (device->configurationFile.isEmpty()) {
ALOGD("No input device configuration file found for device '%s'.",
device->identifier.name.string());
} else {
status_t status = PropertyMap::load(device->configurationFile, //找到之后 ,把这个device 的config file保存起来
&device->configuration);
if (status) {
ALOGE("Error loading input device configuration file for device '%s'. "
"Using default configuration.",
device->identifier.name.string());
接下来看怎么找对应config文件的,这里调用到了/frameworks/base/libs/androidfw/InputDevice.cpp中的:
String8 getInputDeviceConfigurationFilePathByDeviceIdentifier(
const InputDeviceIdentifier& deviceIdentifier,
InputDeviceConfigurationFileType type) {
if (deviceIdentifier.vendor !=0 && deviceIdentifier.product != 0) {
if (deviceIdentifier.version != 0) {
// Try vendor product version.
String8 versionPath(getInputDeviceConfigurationFilePathByName(
String8::format("Vendor_%04x_Product_%04x_Version_%04x", //用前面获取的input device 的相关VID PID 来找文件
deviceIdentifier.vendor, deviceIdentifier.product,
deviceIdentifier.version),
type));
if (!versionPath.isEmpty()) {
return versionPath;
// Try vendor product.
String8 productPath(getInputDeviceConfigurationFilePathByName(
String8::format("Vendor_%04x_Product_%04x",
deviceIdentifier.vendor, deviceIdentifier.product),
type));
if (!productPath.isEmpty()) {
return productPath;
// Try device name.
return getInputDeviceConfigurationFilePathByName(deviceIdentifier.name, type); //这个函数也在同文件中,就是到 path.setTo(getenv("ANDROID_ROOT")); path.append("/usr/"); 也就是文件系统中/system/usr目录下去找
所以我们对一些输出设备需要把它的配置文件编译进系统,而且命名一般都是
Vendor_%04x_Product_%04x.XXX 类型,这里就明白啦!
到这里getEvents应该差不多了,细节部分就需要另行细读代码了,当上面read到事件数组返回之后接下来就是初步处理了!
processEventsLocked:
上面getEvents返回了mEventBuffer之后,做初步的处理:
void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {
for (const RawEvent* rawEvent = rawEvents; count;) { 遍历获取到的event数组
int32_t type = rawEvent->type;
size_t batchSize = 1;
if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) { //如果是常规的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
ALOGW("BatchSize: %d Count: %d", batchSize, count);
//#endif
processEventsForDeviceLocked(deviceId, rawEvent, batchSize); //把这次获取到的event数组中属于同一批次的,进一步处理,判定条件就是:常规event以及是属于同一设备
} else {
switch (rawEvent->type) { //这里就是一些特殊的event类型了,上面有说到,打开设备的时候会有这个ADD事件
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; //如果再上面没有处理完event数组中的成员,那么依次继续
rawEvent += batchSize;
传进来的参数为 前面getEvents一定时间内获取到的event数组以及个数,原型定义在/frameworks/base/services/input/InputReader.h中:
// The event queue.
static const int EVENT_BUFFER_SIZE = 256;
RawEvent mEventBuffer[EVENT_BUFFER_SIZE];
* A raw event as retrieved from the EventHub.
struct RawEvent {
nsecs_t when;
int32_t deviceId;
int32_t type;
int32_t code;
int32_t value;
这里可以看到 processEventsLocked 只是对get到的event做一个初步的分发处理,先看添加的事件类型.
可以看到在InputReader里面又来了一次addDeviceLocked ,这个要更最上面getEvents中往下打开设备时addDeviceLocked 区分开来,不要给绕晕了哟~
void InputReader::addDeviceLocked(nsecs_t when, int32_t deviceId) {
ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
if (deviceIndex >= 0) {
ALOGW("Ignoring spurious device added event for deviceId %d.", deviceId);
return;
InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(deviceId); //这里取之前在EventHub中解析出来的设备相关参数
uint32_t classes = mEventHub->getDeviceClasses(deviceId); //这个上面有说到,在open设备时 初始化,代表类型
ALOGW("jscese display in addDeviceLocked classes == 0x%x \n",classes);
InputDevice* device = createDeviceLocked(deviceId, identifier, classes); //这里又创建一个 InputDervice,这个就不继续跟进了,会根据classes 选择对应的事件处理map与当前的设备绑定
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); //添加这个input device
bumpGenerationLocked();
看处理平常事件时接下来的处理:
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); //这里根据id 取出上面添加进去的inputdevice
if (device->isIgnored()) {
//ALOGD("Discarding event for ignored deviceId %d.", deviceId);
return;
device->process(rawEvents, count); //这里调用了一个process的函数
继续看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(); //这里有个map个数,这个也在上面提到过,在create时 会根据classes类型去匹配处理map,一般都是匹配一个
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); //这里就是调用处理map的process 函数啦
多次提到classes 设备类型,看下定义,在EventHub.h中:
/*//classes=0x80000004
* Input device classes.
enum {
/* The input device is a keyboard or has buttons. */
INPUT_DEVICE_CLASS_KEYBOARD = 0x00000001,
/* The input device is an alpha-numeric keyboard (not just a dial pad). */
INPUT_DEVICE_CLASS_ALPHAKEY = 0x00000002,
/* The input device is a touchscreen or a touchpad (either single-touch or multi-touch). */
INPUT_DEVICE_CLASS_TOUCH = 0x00000004,
/* The input device is a cursor device such as a trackball or mouse. */
INPUT_DEVICE_CLASS_CURSOR = 0x00000008,
/* The input device is a multi-touch touchscreen. */
INPUT_DEVICE_CLASS_TOUCH_MT = 0x00000010,
/* The input device is a directional pad (implies keyboard, has DPAD keys). */
INPUT_DEVICE_CLASS_DPAD = 0x00000020,
/* The input device is a gamepad (implies keyboard, has BUTTON keys). */
INPUT_DEVICE_CLASS_GAMEPAD = 0x00000040,
/* The input device has switches. */
INPUT_DEVICE_CLASS_SWITCH = 0x00000080,
/* The input device is a joystick (implies gamepad, has joystick absolute axes). */
INPUT_DEVICE_CLASS_JOYSTICK = 0x00000100,
/* The input device has a vibrator (supports FF_RUMBLE). */
INPUT_DEVICE_CLASS_VIBRATOR = 0x00000200,
/* The input device is virtual (not a real device, not part of UI configuration). */
INPUT_DEVICE_CLASS_VIRTUAL = 0x40000000,
/* The input device is external (not built-in). */
INPUT_DEVICE_CLASS_EXTERNAL = 0x80000000,
那么接下来需要处理的就是 定义的InputReader中的各个InputMap类了。
这里牵扯到了EventHub InputReader Device InputDevice InputMap
把关系理一下:
EventHub 中 会创建多个Device ,这个是根据/dev/input下面的设备文件 一一对应的,也就是kernel中注册的input设备.
InputReader 中会create多个 InputDevice ,这个是由Device中的参数为基础创建的,所以与EventHub中的Device 一一对应.
InputDevice 在创建的时候会根据 classes 来添加需要的InputMap,每一个不同的InputMap 在InputReader中定义,有很多种处理方式.
大体就是这么回事,这里关于InputReader部分就先分析到这里,再往下InputMap中的事件分发处理,后续分析~
在前文 Linux/Android——Input系统之frameworks层InputManagerService (六) 这里介绍了android层input服务的启动,其中启动了一个读取来自底层event事件的线程.而在Linux/Android——input系统之 kernel层 与 frameworks层交互 (五) 有分析到是通过一个event%d的设备文件交互的,也就是说android层是通过读取event%d来获取event的,这个工作就是InputReader
IMS:EventHub 设备添加和InputDevice转化
android11-release
IMS:InputReader线程获取输入事件 已经讲到过EventHub::getEvents读取事件/dev/input
EventHub 设备添加
IMS:键盘鼠标接入判断 已经讲到EventHub添加外界设备,以及classes和sources码相关转换标识
frameworks\native\services\inputflinger\reader\EventHub.cpp
framewor
文章目录1. 应用程序读取上报事件CPU占用率0%1.1 问题描述1.2 现象分析1.3上报流程分析2. 应用程序接收上报事件read()出错2.1 问题描述2.2 操作背景2.3 解放方法2.4 问题分析
1. 应用程序读取上报事件CPU占用率0%
1.1 问题描述
考虑到应用程序使用 while(1) 循环 read() 上报事件,类似于普通按键实验,cpu 占用率拉满
并且在正点原子关于 input 子系统实验讲解中,有较多朋友提到 cpu 占用率高
加载驱动程序,后台运行测试程序,top 查看应用
检查otg设备是否是鼠标或键盘 if(mIm == null) {
mIm = (InputManager) getSystemService(INPUT_SERVICE);
mIm.registerInputDeviceListener(this, null);
final int[] devices = InputDevice.getDe
接InputReader::loopOnce()的
if (count) {
processEventsLocked(mEventBuffer, count);
input事件获取到就该处理了。
一 type >= EventHubInterface::FIRST_SYNTHETIC_EVENT的情况
DEVICE_ADDED和DEVICE_REMOVED已经说了,还有FINISH
#!/bin/bash
#提示Please input your name并等待20秒,把输入内容存入变量name中read -t 20 -p "Please input your name: " name
echo "input name is $name"
#-s选项隐藏输入,用于隐藏项read -s -t 20 -p "Please enter ...
|-a| 后跟一个变量,该变量会被认为是个数组,然后给其赋值,默认是以空格为分割符|
|- d| 后面跟一个标志符,其实只有其后的第一个字符有用,作为结束的标志|
| - p| 后面跟提示信息,即在输入前打印提示信息|
| - e| 在输入的时候可以使用命令补全功能|
| - n| 后跟一个数字,定义输入文本的长度|
| - r| 屏蔽\,如果没有该选项,则\作为一个转义字符,有的话 \就是个正常的字符了|
| - s| 安静模式,在输入字符时不再屏幕上显示|
| - t| 后面跟秒数,定义输入字符的等待时
read命令用于从键盘或标准输入中读取文本。以交互的形式读取来自用户的输入。默认用”回车键”作为结束。
一般使用在自动化的sh脚本中。
#用不回显(non-echoed)方式读取密码
$ read -s var
#显示提示信息
$ read -p “Enter input:” var
#在特定的时间内读取输入(单位:秒)
$ read -t 2 var
#自定义结束符(即输入冒号(:)时结束)
例如以一次鼠标按下事件为例子来说明我们的input输入子系统的工作过程:
设备驱动层:当我们按下鼠标左键的时候就会触发中断(中断是早就注册好的),就会去执行中断所绑定的处理函数,在函数中就会去读取硬件寄存器来判断按下的是哪个按键和状态 ---->
将按键信息上报给input core层 ---> input core层处理好了之后就会上报给inp...