赞
踩
生活中有许多我们习以为常的事情,然而其中的细节往往为我们所忽略。今天,我们将深入探讨 Android 设备从按键到屏幕点亮的一系列过程,以期更好地理解它们的实现方式。(注:本文所参考的源代码基于 Android-13.0.0_r3 和 MTK 平台。)
首先,我们画一个大致的数据流图,对整个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是随着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);
- }
我们看一下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
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。