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中。

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
    }
}

对于po

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值