生活中有许多我们习以为常的事情,然而其中的细节往往为我们所忽略。今天,我们将深入探讨 Android 设备从按键到屏幕点亮的一系列过程,以期更好地理解它们的实现方式。(注:本文所参考的源代码基于 Android-13.0.0_r3 和 MTK 平台。)
一、overview
首先,我们画一个大致的数据流图,对整个android输入到输出的反馈有一个大体的了解。

如上图,power按键(短按)触发按下后,硬件会上报中断通知inux input子系统把按键上报给framework层的输入管理子系统(IMS),IMS通过PWM(电话窗口管理)的策略确定按键消息是丢弃还是转发给对应的处理组件,这里他识别到时电源短按键,会识别为唤醒亮屏流程,进而委托PMS(电源管理服务)进行唤醒流程处理。PMS会通过DMS(显示管理服务)亮屏显示,最终从framework层调用到底层驱动实现硬件上的亮屏反馈动作。
二、按键输入上报
power按键上报是和具体的硬件相关的,所以开始前我们需要对相关的硬件有一些认识。

这里简单画了个框图,物理的按键会通过io口连接到pmic(电源管理集成电路),然后通过pmic连接到soc内部。pmic有些是独立封装的一颗ic,有些是集成在soc内部,总之就是物理的按键按下后会触发cpu上的一个中断。
我们看下mtk power按键的软件的实现。
首先看dts配置:

power key的keycode为116,按键长按是2s以上,相关驱动实现可以通过compatible字段去找。其主要在drivers/input/keyboard/mtk-pmic-keys.c中。
static int mtk_pmic_keys_probe(struct platform_device *pdev)
{
...
keys = devm_kzalloc(&pdev->dev, sizeof(*keys), GFP_KERNEL);
if (!keys)
return -ENOMEM;
keys->input_dev = input_dev = devm_input_allocate_device(keys->dev);
if (!input_dev) {
dev_err(keys->dev, "input allocate device fail.\n");
return -ENOMEM;
}
...
error = input_register_device(input_dev);
if (error) {
dev_err(&pdev->dev,
"register input device failed (%d)\n", error);
return error;
}
mtk_pmic_keys_lp_reset_setup(keys, mtk_pmic_regs);
platform_set_drvdata(pdev, keys);
return 0;
}
内核加载时会安装power key的驱动,并注册生成sys文件/dev/input/eventX(X为数字),用于输入消息的读取写入(input框架可以参考网上文章)。
当有按键按下的时候会触pmic的中断处理mtk_pmic_keys_release_irq_handler_thread:
// 这里当电源按键按下时,会触发中断,进入该中断线程进行处理
static irqreturn_t mtk_pmic_keys_release_irq_handler_thread(
int irq, void *data)
{
struct mtk_pmic_keys_info *info = data;
//上报116(实际上就是POWER)到/dev/input/event
input_report_key(info->keys->input_dev, info->keycode, 0);
input_sync(info->keys->input_dev);
if (info->suspend_lock)
__pm_relax(info->suspend_lock);
dev_info(info->keys->dev, "release key =%d using PMIC\n",
info->keycode);
return IRQ_HANDLED;
}
下面按键消息将会通过IMS(InputManagerService)注入到我们的framework:

三、输入消息传递
从驱动层的消息上报到linux系统的input子系统后,就轮到Android的IMS(Input Manager Service)子系统处理了。
IMS启动及主体功能
这里我们先看一下IMS子系统的启动流程。

IMS启动时序
IMS是随着SystemServer启动而启动的,主要InputReader和InputDispatcher两个主要的线程,前者读取/dev/input目录下eventx (x为数字)linux input子系统上报的输入事件,后者对InputReader进行分发处理。EventHub主要监听/dev/下与输入相关的设备变化通知InputReader进行读取。
InputReader核心功能实现
status_t InputReader::start() {
if (mThread) {
return ALREADY_EXISTS;
}
// 处理的主逻辑
mThread = std::make_unique<InputThread>(
"InputReader", [this]() { loopOnce(); }, [this]() { mEventHub->wake(); });
return OK;
}
void InputReader::loopOnce() {
int32_t oldGeneration;
int32_t timeoutMillis;
bool inputDevicesChanged = false;
std::vector<InputDeviceInfo> inputDevices;
...
// 这里利用EventHub去获取/dev下输入设备的所有输入上报消息
size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
{ // acquire lock
std::scoped_lock _l(mLock);
mReaderIsAliveCondition.notify_all();
// 处理从linux input子系统读取到的输入上报rawEvent转换为NotifyKeyArgs消息
if (count) {
processEventsLocked(mEventBuffer, count);
}
...
} // release lock
// 如果有inpiut设备的变化则会通知到java层InputManagerService回调notifyInputDevicesChanged
// Send out a message that the describes the changed input devices.
if (inputDevicesChanged) {
mPolicy->notifyInputDevicesChanged(inputDevices);
}
// 把NotifyKeyArgs消息转换为EventEntry消息,推送给InputDispatcher处理
// Flush queued events out to the listener.
// This must happen outside of the lock because the listener could potentially call
// back into the InputReader's methods, such as getScanCodeState, or become blocked
// on another thread similarly waiting to acquire the InputReader lock thereby
// resulting in a deadlock. This situation is actually quite plausible because the
// listener is actually the input dispatcher, which calls into the window manager,
// which occasionally calls into the input reader.
mQueuedListener.flush();
}
InputDispatcher核心功能实现
status_t InputDispatcher::start() {
if (mThread) {
return ALREADY_EXISTS;
}
mThread = std::make_unique<InputThread>(
"InputDispatcher", [this]() { dispatchOnce(); }, [this]() { mLooper->wake(); });
return OK;
}
void InputDispatcher::dispatchOnce() {
nsecs_t nextWakeupTime = LONG_LONG_MAX;
{ // acquire lock
std::scoped_lock _l(mLock);
mDispatcherIsAlive.notify_all();
// Run a dispatch loop if there are no pending commands.
// The dispatch loop might enqueue commands to run afterwards.
if (!haveCommandsLocked()) {
dispatchOnceInnerLocked(&nextWakeupTime);
}
// Run all pending commands if there are any.
// If any commands were run then force the next poll to wake up immediately.
if (runCommandsLockedInterruptable()) {
nextWakeupTime = LONG_LONG_MIN;
}
// ANR的处理
// If we are still waiting for ack on some events,
// we might have to wake up earlier to check if an app is anr'ing.
const nsecs_t nextAnrCheck = processAnrsLocked();
nextWakeupTime = std::min(nextWakeupTime, nextAnrCheck);
// We are about to enter an infinitely long sleep, because we have no commands or
// pending or queued events
if (nextWakeupTime == LONG_LONG_MAX) {
mDispatcherEnteredIdle.notify_all();
}
} // release lock
// Wait for callback or timeout or wake. (make sure we round up, not down)
nsecs_t currentTime = now();
int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);
mLooper->pollOnce(timeoutMillis);
}
电源按键在IMS层的传递
我们看一下systrace中电源按键的处理过程

首先电源按键进入InputReader.loopOnce的读取,并做了简单的消息过滤和转换:

调用流程:processEventsLocked-->InputReader::processEventsForDeviceLocked-->InputDevice::process-->mapper.process-->KeyboardInputMapper::process-->KeyboardInputMapper::processKey-->getListener().notifyKey-->QueuedInputListener::notifyKey
processEventsLocked把事件先交由了对应的InputDevice,然后找对处理该事件类型的InputMapper 进行处理。以及一些基本的入队前的过滤(NativeInputManager.interceptKeyBeforeQueueing),然后InputMapper将事件等信息构造了NotifyArgs,然后加入到了mArgsQueue中。
mQueuedListener.flush()-->QueuedInputListener::flush-->NotifyKeyArgs::notify-->InputClassifier::notifyKey-->InputDispatcher::notifyKey-->InputDispatcher::enqueueInboundEventLocked
mQueuedListener.flush把mArgsQueue中的NotifyArgs消息依次读出,传递到InputDispatcher,封装成EventEntry并加入mInboundQueue队列尾部并wakeup分发线程。
至此,按键消息读取过程完毕。接下来,进入分发线程InputDispatcher::dispatchOnce处理。
void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
...
// 读取mInboundQueue中的消息进行处理
if (!mPendingEvent) {
if (mInboundQueue.empty()) {
...
} else {
// Inbound queue has at least one entry.
mPendingEvent = mInboundQueue.front();
mInboundQueue.pop_front();
traceInboundQueueLengthLocked();
}
// Poke user activity for this event.
if (mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER) {
pokeUserActivityLocked(*mPendingEvent);
}
}
// 进行消息类型进行实际的分发处理
// All events are eventually dequeued and processed this way, even if we intend to drop them.
ALOG_ASSERT(mPendingEvent != nullptr);
bool done = false;
DropReason dropReason = DropReason::NOT_DROPPED;
if (!(mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER)) {
dropReason = DropReason::POLICY;
} else if (!mDispatchEnabled) {
dropReason = DropReason::DISABLED;
}
switch (mPendingEvent->type) {
...
case EventEntry::Type::KEY: {
std::shared_ptr<KeyEntry> keyEntry = std::static_pointer_cast<KeyEntry>(mPendingEvent);
if (isAppSwitchDue) {
if (isAppSwitchKeyEvent(*keyEntry)) {
resetPendingAppSwitchLocked(true);
isAppSwitchDue = false;
} else if (dropReason == DropReason::NOT_DROPPED) {
dropReason = DropReason::APP_SWITCH;
}
}
if (dropReason == DropReason::NOT_DROPPED && isStaleEvent(currentTime, *keyEntry)) {
dropReason = DropReason::STALE;
}
if (dropReason == DropReason::NOT_DROPPED && mNextUnblockedEvent) {
dropReason = DropReason::BLOCKED;
}
done = dispatchKeyLocked(currentTime, keyEntry, &dropReason, nextWakeupTime);
break;
}
...
}
if (done) {
if (dropReason != DropReason::NOT_DROPPED) {
dropInboundEventLocked(*mPendingEvent, dropReason);
}
mLastDropReason = dropReason;
releasePendingEventLocked();
*nextWakeupTime = LONG_LONG_MIN; // force next poll to wake up immediately
}
}
对于power