从本文开始,分析下按键事件的处理和分发。
EventHub 获取按键事件
// EventHub.cpp
std::vector<RawEvent> EventHub::getEvents(int timeoutMillis) {
std::scoped_lock _l(mLock);
std::array<input_event, EVENT_BUFFER_SIZE> readBuffer;
std::vector<RawEvent> events;
bool awoken = false;
for (;;) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
// ...
// Grab the next input event.
bool deviceChanged = false;
// 2. 遍历处理所有输入事件
while (mPendingEventIndex < mPendingEventCount) {
const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];
// ...
// This must be an input event
if (eventItem.events & EPOLLIN) {
int32_t readSize =
read(device->fd, readBuffer.data(),
sizeof(decltype(readBuffer)::value_type) * readBuffer.size());
if (readSize == 0 || (readSize < 0 && errno == ENODEV)) {
// ...
} else if (readSize < 0) {
// ...
} else if ((readSize % sizeof(struct input_event)) != 0) {
// ...
} else {
const int32_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
const size_t count = size_t(readSize) / sizeof(struct input_event);
for (size_t i = 0; i < count; i++) {
struct input_event& iev = readBuffer[i];
// 2.1 把事件加入都 events 中
events.push_back({
.when = processEventTimestamp(iev),
.readTime = systemTime(SYSTEM_TIME_MONOTONIC),
.deviceId = deviceId,
.type = iev.type,
.code = iev.code,
.value = iev.value,
});
}
if (events.size() >= EVENT_BUFFER_SIZE) {
// The result buffer is full. Reset the pending event index
// so we will try to read the device again on the next iteration.
mPendingEventIndex -= 1;
break;
}
}
} else if (eventItem.events & EPOLLHUP) {
// ...
} else {
// ...
}
}
if (mPendingINotify && mPendingEventIndex >= mPendingEventCount) {
// ...
}
if (deviceChanged) {
continue;
}
// 3. events 现在有事件,跳出无限循环
if (!events.empty() || awoken) {
break;
}
mPendingEventIndex = 0;
mLock.unlock(); // release lock before poll
// 1. 监听到输入设备产生事件
// 事件保存到 mPendingEventItems
int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
mLock.lock(); // reacquire lock after poll
if (pollResult == 0) {
// ...
break;
}
if (pollResult < 0) {
// ...
} else {
// Some events occurred.
// 保存即将要处理的事件的数量
mPendingEventCount = size_t(pollResult);
}
}
// 4. 返回读到的事件
return events;
}
对于输入设备产生的事件,无论是 key event 还是 motion event,EventHub 统一使用结构体 RawEvent 来保存数据,如下
// EventHub.h
struct RawEvent {
nsecs_t when;
nsecs_t readTime;
int32_t deviceId;
int32_t type;
int32_t code;
int32_t value;
};
我们可以通过 adb shell getevent 命令,观察按下和抬起电源键时,驱动所产生的元数据,如下
/dev/input/event1: 0001 0074 00000001
/dev/input/event1: 0000 0000 00000000
/dev/input/event1: 0001 0074 00000000
/dev/input/event1: 0000 0000 00000000
每一行的最后三个数据,分别对一个 RawEvent 结构体中的 type, code, value。但是,这些数据并不直观,我们可以通过 adb shell getevent -l 观察,如下
/dev/input/event1: EV_KEY KEY_POWER DOWN
/dev/input/event1: EV_SYN SYN_REPORT 00000000
/dev/input/event1: EV_KEY KEY_POWER UP
/dev/input/event1: EV_SYN SYN_REPORT 00000000
第一行是按下电源键产生的事件数据,第三行是抬起电源键所产生的事件数据,而第二行和第四行是一个同步事件,它不代表电源键的任何事件,它只是告诉输入系统,需要同步之前的数据了。
InputReader 处理按键事件
// InputReader.cpp
void InputReader::loopOnce() {
int32_t oldGeneration;
int32_t timeoutMillis;
// Copy some state so that we can access it outside the lock later.
bool inputDevicesChanged = false;
std::vector<InputDeviceInfo> inputDevices;
// InputReader 处理事件时,会生成新的事件,保存到 notifyArgs
std::list<NotifyArgs> notifyArgs;
// ...
// 1. 读取事件
std::vector<RawEvent> events = mEventHub->getEvents(timeoutMillis);
{ // acquire lock
std::scoped_lock _l(mLock);
mReaderIsAliveCondition.notify_all();
// 2. 处理事件
if (!events.empty()) {
notifyArgs += processEventsLocked(events.data(), events.size());
}
// ...
} // release lock
// ...
// 3.按键事件加入到 mQueuedListener 队列
notifyAll(std::move(notifyArgs));
// 3.刷新 mQueuedListener 队列,把事件发送到下一环
mQueuedListener.flush();
}
//事件加入到 mQueuedListener 队列
void InputReader::notifyAll(std::list<NotifyArgs>&& argsList) {
for (const NotifyArgs& args : argsList) {
mQueuedListener.notify(args);
}
}
InputReader 从 EventHub 获得了按键事件后,会对它进行加工处理,然后把加工后的数据重新包装,最后分发给下一环。
处理按键事件
// InputReader.cpp
std::list<NotifyArgs> InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {
std::list<NotifyArgs> out;
for (const RawEvent* rawEvent = rawEvents; count;) {
int32_t type = rawEvent->type;
size_t batchSize = 1;
// 遍历所有的 RawEvent
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 (debugRawEvents()) {
ALOGD("BatchSize: %zu Count: %zu", batchSize, count);
}
// InputReader 对 raw event 进行批量的加工处理,并返回加工后的事件
out += processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
} else {
// ...
}
count -= batchSize;
rawEvent += batchSize;
}
// 返回加工后的事件
return out;
}
std::list<NotifyArgs> InputReader::processEventsForDeviceLocked(int32_t eventHubId,
const RawEvent* rawEvents,
size_t count) {
// 获取输入设备 InputDevice
auto deviceIt = mDevices.find(eventHubId);
if (deviceIt == mDevices.end()) {
ALOGW("Discarding event for unknown eventHubId %d.", eventHubId);
return {};
}
std::shared_ptr<InputDevice>& device = deviceIt->second;
// InputDevice 没有 InputMapper,就不能处理事件
if (device->isIgnored()) {
// ALOGD("Discarding event for ignored deviceId %d.", deviceId);
return {};
}
// InputDevice 批量处理事件
return device->process(rawEvents, count);
}
InputReader 批量地把按键事件,交给对应的输入设备 InputDevice 处理,如下
// InputDevice.cpp
std::list<NotifyArgs> 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.
std::list<NotifyArgs> out;
// 遍历所有事件,逐个处理
for (const RawEvent* rawEvent = rawEvents; count != 0; rawEvent++) {
// debug 版本,打印如下 raw events 数据
if (debugRawEvents()) {
const auto [type, code, value] =
InputEventLookup::getLinuxEvdevLabel(rawEvent->type, rawEvent->code,
rawEvent->value);
ALOGD("Input event: eventHubDevice=%d type=%s code=%s value=%s when=%" PRId64,
rawEvent->deviceId, type.c_str(), code.c_str(), value.c_str(), rawEvent->when);
}
if (mDropUntilNextSync) {
// ...
} else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) {
// ...
} else {
// 交给所有的 InputMapper 处理事件
for_each_mapper_in_subdevice(rawEvent->deviceId, [&](InputMapper& mapper) {
out += mapper.process(rawEvent);
});
}
--count;
}
return out;
}
虽然说 InputDevice 是批量处理按键事件,但是它是逐个地把按键事件,交给 InputDevice 的所有 InputMapper 处理。而 InputMapper 只会处理自己感兴趣的事件,例如, KeyboardInputMapper 只处理按键事件,而不会处理触摸事件。
对于按键事件,是由 KeyboardInputMapper 来处理的,如下
// KeyboardInputMapper.cpp
std::list<NotifyArgs> KeyboardInputMapper::process(const RawEvent* rawEvent) {
std::list<NotifyArgs> out;
// 处理 EV_MSC(type) + MSC_SCAN(code) 事件
mHidUsageAccumulator.process(*rawEvent);
switch (rawEvent->type) {
case EV_KEY: {
// RawEvent::code 也可以称之为 scan code
// 这个命令与硬件产生事件的原理有关
int32_t scanCode = rawEvent->code;
if (isSupportedScanCode(scanCode)) {
// 注意,RawEvent::value 不为 0,代表按键的按下事件
out += processKey(rawEvent->when, rawEvent->readTime, rawEvent->value != 0,
scanCode, mHidUsageAccumulator.consumeCurrentHidUsage());
}
break;
}
}
return out;
}
std::list<NotifyArgs> KeyboardInputMapper::processKey(nsecs_t when, nsecs_t readTime, bool down,
int32_t scanCode, int32_t usageCode) {
std::list<NotifyArgs> out;
int32_t keyCode;
int32_t keyMetaState;
uint32_t policyFlags;
// 1. 按键映射
// 把 scan code 转化为 key code,并获取标志位,标志位保存到 policyFlags
if (getDeviceContext().mapKey(scanCode, usageCode, mMetaState, &keyCode, &keyMetaState,
&policyFlags)) {
// ...
}
nsecs_t downTime = when;
// 从 mKeyDowns 中根据 scan code 获取 key down 数据的 index
std::optional<size_t> keyDownIndex = findKeyDownIndex(scanCode);
if (down) { // 处理 key down
// 根据 orientation 旋转 key code,一般的按键设备不支持
// Rotate key codes according to orientation if needed.
if (mParameters.orientationAware) {
keyCode = rotateKeyCode(keyCode, getOrientation());
}
if (keyDownIndex) {
// ...
} else {
// ...
// 使用 KeyDown 保存按键按下的数据,主要包括 scan code 和 key code
KeyDown keyDown;
keyDown.keyCode = keyCode;
keyDown.scanCode = scanCode;
keyDown.downTime = when;
// KeyDown 保存到集合 mKeyDowns
mKeyDowns.push_back(keyDown);
}
} else { // 处理 key up
// Remove key down.
if (keyDownIndex) {
// key up, be sure to use same keycode as before in case of rotation
keyCode = mKeyDowns[*keyDownIndex].keyCode;
downTime = mKeyDowns[*keyDownIndex].downTime;
// 按键抬起时,从 mKeyDowns 移除 key down 数据
mKeyDowns.erase(mKeyDowns.begin() + *keyDownIndex);
} else {
// ...
}
}
// ...
// 2. 返回加工后的按键事件数据
out.emplace_back(NotifyKeyArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
mSource, getDisplayId(), policyFlags,
down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState,
downTime));
return out;
}
对于手机/平板来说,处理按键事件( 系统称之为加工事件 ),主要是把 scan code 转化为 key code,然后使用结构体 NotifyKeyArgs 保存加工后的按键事件数据,并作为加工按键事件的结果返回。
为何要把 scan code 转化为 key code ? 不同的输入设备上,同一种功能的按键,例如,电源键,它们的 scan code 是不同的。Android 系统为了统一处理同一种功能的按键,需要把不同的 scan code 映射为统一的 key code,如下
// EventHub.cpp
status_t EventHub::mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t metaState,
int32_t* outKeycode, int32_t* outMetaState, uint32_t* outFlags) const {
std::scoped_lock _l(mLock);
// 获取输入设备
Device* device = getDeviceLocked(deviceId);
status_t status = NAME_NOT_FOUND;
if (device != nullptr) {
// 1. 首先由 key character map 映射
const std::shared_ptr<KeyCharacterMap> kcm = device->getKeyCharacterMap();
if (kcm) {
if (!kcm->mapKey(scanCode, usageCode, outKeycode)) {
// 注意,kcm 转换成功,是没有 policy flags
*outFlags = 0;
status = NO_ERROR;
}
}
// 2. 如果上一步没转换成功,接着由 key layout 映射
if (status != NO_ERROR && device->keyMap.haveKeyLayout()) {
// 注意,kl 转化,是有 policy flags
if (!device->keyMap.keyLayoutMap->mapKey(scanCode, usageCode, outKeycode, outFlags)) {
status = NO_ERROR;
}
}
if (status == NO_ERROR) {
if (kcm) { // kcm 再处理
// 上层可以通过 InputManager#remapModifierKey(fromKey, toKey)
// 把 kcm 映射的 key code, 重新映射为一个新的 key code
// 但是这是一个测试 API,通常用不到
*outKeycode = kcm->applyKeyRemapping(*outKeycode);
// Remap keys based on Key behavior defined in KCM file
// kcm 根据 meta 键(Ctrl、Alt、Shift) 重新转换 key code 和 meta state
std::tie(*outKeycode, *outMetaState) =
kcm->applyKeyBehavior(*outKeycode, metaState);
} else { // kl 再处理
*outMetaState = metaState;
}
}
}
if (status != NO_ERROR) {
// ...
}
return status;
}
有两个文件,可以用于按键映射
- key character map 文件,简称 kcm 文件。它主要是对键盘的按键进行映射,它会考虑 meta 键(shift、Ctrl、Alt,等)状态。例如,如果 shift 键按下,再按下 f 键,那么 kcm 把会 f 键的 scan code 转化为大写的 F 的 key code。
- key layout 文件,简称 kl 文件。与 kcm 一样,同样可以把 scan code 转化为 key code,但是它要简单的多,不会考虑 meta 键状态。因此 kl 文件,主要是手机/平板来使用。另外,它还可以附带一个标志位,Android 系统可以针对不同标志位,响应不同的行为。
根据前面文章的分析可知,kcm 和 kl 文件,是在扫描设备时加载的,可以通过 adb shell dump input 查看这两个文件路径,如下
9: pmic_pwrkey
Classes: KEYBOARD
Path: /dev/input/event1
KeyLayoutFile: /system/usr/keylayout/Generic.kl
KeyCharacterMapFile: /system/usr/keychars/Generic.kcm
在两个文件中同时搜索 POWER 关键字,在我的手机上,我只在 kl 文件中,查找到电源键的按键映射配置,如下
key 116 POWER
key 152 POWER
key 699 POWER
那么,到底哪个数据有效呢?可以通过 adb shell getevent 查看电源键上报的的 scan code
/dev/input/event1: 0001 0074 00000001
电源键的 scan code 为 0074,它是十六进制,转化为十进制就是 116,因此使用的就是 key 116 POWER 对按键进行映射的。
电源键的 scan code ,映射的值是一个字符串 POWER,然后在根据 frameworks/native/include/android/keycodes.h 转换为上层所使用的 int 值,如下
// frameworks/native/include/android/keycodes.h
enum {
/** Power key. */
AKEYCODE_POWER = 26,
}
上层的 KeyEvent.java 中定义的电源键值,恰好也是 26,如下
// KeyEvent.java
/** Key code constant: Power key. */
public static final int KEYCODE_POWER = 26;
关于 kcm、kl 文件,大家可以参考 Google 给的官方文档
- key layout files : source.android.google.cn/docs/core/i…
- key charater map files : source.android.google.cn/docs/core/i…
刷新 QueuedInputListener 队列
KeyboardInputMapper 处理完按键事件后,创建 NotifyKeyArgs 作为新的按键事件。之后,NotifyKeyArgs 保存到 InputReader::mQueuedListener 的队列中,最后刷新 mQueuedListener 的队列,如下
// InputListener.cpp
void QueuedInputListener::flush() {
for (const NotifyArgs& args : mArgsQueue) {
// 通知下一环处理
mInnerListener.notify(args);
}
mArgsQueue.clear();
}
void InputListenerInterface::notify(const NotifyArgs& generalArgs) {
Visitor v{
// ...
// 处理按键事件
[&](const NotifyKeyArgs& args) { notifyKey(args); },
// ...
};
std::visit(v, generalArgs);
}
根据第一篇文章的分析,InputReader 的下一环是 UnwantedInteractionBlocker,因此调用它的 notifyKey() 来处理按键事件
// UnwantedInteractionBlocker.cpp
void UnwantedInteractionBlocker::notifyKey(const NotifyKeyArgs& args) {
// 加入队列
mQueuedListener.notifyKey(args);
// 刷新队列
mQueuedListener.flush();
}
UnwantedInteractionBlocker 没有做任何处理,直接交给下一环 InputProcessor
void InputProcessor::notifyKey(const NotifyKeyArgs& args) {
// pass through
mQueuedListener.notifyKey(args);
mQueuedListener.flush();
}
InputProcessor 同样没做什么处理,直接交给下一环 InputDispatcher。 而 InputDispatcher 会分发事件给窗口,这个过程比较长,下一篇文章继续分析。