当前位置:   article > 正文

Android按键亮屏之旅_android关机键触发到屏幕亮起的流程

android关机键触发到屏幕亮起的流程

生活中有许多我们习以为常的事情,然而其中的细节往往为我们所忽略。今天,我们将深入探讨 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中。

  1. static int mtk_pmic_keys_probe(struct platform_device *pdev)
  2. {
  3. ...
  4. keys = devm_kzalloc(&pdev->dev, sizeof(*keys), GFP_KERNEL);
  5. if (!keys)
  6. return -ENOMEM;
  7. keys->input_dev = input_dev = devm_input_allocate_device(keys->dev);
  8. if (!input_dev) {
  9. dev_err(keys->dev, "input allocate device fail.\n");
  10. return -ENOMEM;
  11. }
  12. ...
  13. error = input_register_device(input_dev);
  14. if (error) {
  15. dev_err(&pdev->dev,
  16. "register input device failed (%d)\n", error);
  17. return error;
  18. }
  19. mtk_pmic_keys_lp_reset_setup(keys, mtk_pmic_regs);
  20. platform_set_drvdata(pdev, keys);
  21. return 0;
  22. }

内核加载时会安装power key的驱动,并注册生成sys文件/dev/input/eventX(X为数字),用于输入消息的读取写入(input框架可以参考网上文章)。

当有按键按下的时候会触pmic的中断处理mtk_pmic_keys_release_irq_handler_thread:

  1. // 这里当电源按键按下时,会触发中断,进入该中断线程进行处理
  2. static irqreturn_t mtk_pmic_keys_release_irq_handler_thread(
  3. int irq, void *data)
  4. {
  5. struct mtk_pmic_keys_info *info = data;
  6. //上报116(实际上就是POWER)到/dev/input/event
  7. input_report_key(info->keys->input_dev, info->keycode, 0);
  8. input_sync(info->keys->input_dev);
  9. if (info->suspend_lock)
  10. __pm_relax(info->suspend_lock);
  11. dev_info(info->keys->dev, "release key =%d using PMIC\n",
  12. info->keycode);
  13. return IRQ_HANDLED;
  14. }

下面按键消息将会通过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核心功能实现

  1. status_t InputReader::start() {
  2. if (mThread) {
  3. return ALREADY_EXISTS;
  4. }
  5.     // 处理的主逻辑
  6. mThread = std::make_unique<InputThread>(
  7. "InputReader", [this]() { loopOnce(); }, [this]() { mEventHub->wake(); });
  8. return OK;
  9. }
  1. void InputReader::loopOnce() {
  2. int32_t oldGeneration;
  3. int32_t timeoutMillis;
  4. bool inputDevicesChanged = false;
  5. std::vector<InputDeviceInfo> inputDevices;
  6. ...
  7. // 这里利用EventHub去获取/dev下输入设备的所有输入上报消息
  8. size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
  9. { // acquire lock
  10. std::scoped_lock _l(mLock);
  11. mReaderIsAliveCondition.notify_all();
  12. // 处理从linux input子系统读取到的输入上报rawEvent转换为NotifyKeyArgs消息
  13. if (count) {
  14. processEventsLocked(mEventBuffer, count);
  15. }
  16.         ...
  17. } // release lock
  18. // 如果有inpiut设备的变化则会通知到java层InputManagerService回调notifyInputDevicesChanged
  19. // Send out a message that the describes the changed input devices.
  20. if (inputDevicesChanged) {
  21. mPolicy->notifyInputDevicesChanged(inputDevices);
  22. }
  23. // 把NotifyKeyArgs消息转换为EventEntry消息,推送给InputDispatcher处理
  24. // Flush queued events out to the listener.
  25. // This must happen outside of the lock because the listener could potentially call
  26. // back into the InputReader's methods, such as getScanCodeState, or become blocked
  27. // on another thread similarly waiting to acquire the InputReader lock thereby
  28. // resulting in a deadlock. This situation is actually quite plausible because the
  29. // listener is actually the input dispatcher, which calls into the window manager,
  30. // which occasionally calls into the input reader.
  31. mQueuedListener.flush();
  32. }

InputDispatcher核心功能实现

  1. status_t InputDispatcher::start() {
  2. if (mThread) {
  3. return ALREADY_EXISTS;
  4. }
  5. mThread = std::make_unique<InputThread>(
  6. "InputDispatcher", [this]() { dispatchOnce(); }, [this]() { mLooper->wake(); });
  7. return OK;
  8. }
  1. void InputDispatcher::dispatchOnce() {
  2. nsecs_t nextWakeupTime = LONG_LONG_MAX;
  3. { // acquire lock
  4. std::scoped_lock _l(mLock);
  5. mDispatcherIsAlive.notify_all();
  6. // Run a dispatch loop if there are no pending commands.
  7. // The dispatch loop might enqueue commands to run afterwards.
  8. if (!haveCommandsLocked()) {
  9. dispatchOnceInnerLocked(&nextWakeupTime);
  10. }
  11. // Run all pending commands if there are any.
  12. // If any commands were run then force the next poll to wake up immediately.
  13. if (runCommandsLockedInterruptable()) {
  14. nextWakeupTime = LONG_LONG_MIN;
  15. }
  16.         // ANR的处理
  17. // If we are still waiting for ack on some events,
  18. // we might have to wake up earlier to check if an app is anr'ing.
  19. const nsecs_t nextAnrCheck = processAnrsLocked();
  20. nextWakeupTime = std::min(nextWakeupTime, nextAnrCheck);
  21. // We are about to enter an infinitely long sleep, because we have no commands or
  22. // pending or queued events
  23. if (nextWakeupTime == LONG_LONG_MAX) {
  24. mDispatcherEnteredIdle.notify_all();
  25. }
  26. } // release lock
  27. // Wait for callback or timeout or wake. (make sure we round up, not down)
  28. nsecs_t currentTime = now();
  29. int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);
  30. mLooper->pollOnce(timeoutMillis);
  31. }
  • 电源按键在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处理。

  1. void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
  2. ...
  3. // 读取mInboundQueue中的消息进行处理
  4. if (!mPendingEvent) {
  5. if (mInboundQueue.empty()) {
  6.     ...
  7. } else {
  8. // Inbound queue has at least one entry.
  9. mPendingEvent = mInboundQueue.front();
  10. mInboundQueue.pop_front();
  11. traceInboundQueueLengthLocked();
  12. }
  13. // Poke user activity for this event.
  14. if (mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER) {
  15. pokeUserActivityLocked(*mPendingEvent);
  16. }
  17. }
  18. // 进行消息类型进行实际的分发处理
  19. // All events are eventually dequeued and processed this way, even if we intend to drop them.
  20. ALOG_ASSERT(mPendingEvent != nullptr);
  21. bool done = false;
  22. DropReason dropReason = DropReason::NOT_DROPPED;
  23. if (!(mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER)) {
  24. dropReason = DropReason::POLICY;
  25. } else if (!mDispatchEnabled) {
  26. dropReason = DropReason::DISABLED;
  27. }
  28. switch (mPendingEvent->type) {
  29.         ...
  30. case EventEntry::Type::KEY: {
  31. std::shared_ptr<KeyEntry> keyEntry = std::static_pointer_cast<KeyEntry>(mPendingEvent);
  32. if (isAppSwitchDue) {
  33. if (isAppSwitchKeyEvent(*keyEntry)) {
  34. resetPendingAppSwitchLocked(true);
  35. isAppSwitchDue = false;
  36. } else if (dropReason == DropReason::NOT_DROPPED) {
  37. dropReason = DropReason::APP_SWITCH;
  38. }
  39. }
  40. if (dropReason == DropReason::NOT_DROPPED && isStaleEvent(currentTime, *keyEntry)) {
  41. dropReason = DropReason::STALE;
  42. }
  43. if (dropReason == DropReason::NOT_DROPPED && mNextUnblockedEvent) {
  44. dropReason = DropReason::BLOCKED;
  45. }
  46. done = dispatchKeyLocked(currentTime, keyEntry, &dropReason, nextWakeupTime);
  47. break;
  48. }
  49.         ...
  50. }
  51. if (done) {
  52. if (dropReason != DropReason::NOT_DROPPED) {
  53. dropInboundEventLocked(*mPendingEvent, dropReason);
  54. }
  55. mLastDropReason = dropReason;
  56. releasePendingEventLocked();
  57. *nextWakeupTime = LONG_LONG_MIN; // force next poll to wake up immediately
  58. }
  59. }

对于power

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/木道寻08/article/detail/888985
推荐阅读
相关标签
  

闽ICP备14008679号