赞
踩
一、整体框架介绍
电源键亮灭屏流程从框架上分硬件层,驱动层,Java框架层和Natvie服务层;
整个流程分为两部分,一部分是电源按键事件传递流程,一部分是亮灭屏处理流程;
中间通过一个策略类来衔接,决定按键动作是做亮屏还是灭屏动作。
二、电源键传递流程
1. 内核空间电源键传递流程
各层的介绍如下:
设备驱动层:将底层的硬件输入转化为统一事件形式,向核心层传递;
核心层:为驱动层提供输入设备注册与操作接口,通知事件处理层对事件进行处理;
事件处理层:主要是和用户空间交互,提供设备节点来给用户空间访问;
用户空间: 从事件处理层获得事件,进行进一步的事件分发和业务处理。
电源键的处理流程是:
1)按键驱动初始化的时候首先通过request_irq进行中断的注册;
2)当电源键按下和抬起时候,发生中断,中断处理函数被执行,调用input接口input_report_key和input_sync来进行事件的上报;
3)事件通过核心层和事件层后,到达用户空间,这样用户空间的程序就可以通过/dev/input节点和getEvents获得按键事件。
2. 用户空间电源键传递流程
InputReader线程的loop里面,通过EventHub的getEvents函数获得按键事件,然后传递给processEventsLocked来处理。
processEventsLocked依次调用processEventsForDeviceLocked()->InputDevice::process()->KeyboardInputMapper::process()->processKey()函数。
processKey会调用listener的notifyKey,这个linstener是谁呢?
跟踪赋值流程可以找到,getListener获得的是在InputManager开始初始化时候生成的InputDispatch对象,所以按键传递给了InputDispatch的notifyKey。
InputDispatch::notifyKey对事件的处理,首先调用interceptKeyBeforeQueueing,让策略类在入队前能处理该事件。
interceptKeyBeforeQueueing经过InputManager和WindowManager的InputManagerCallback调用,最终执行到PhoneWindowManager的interceptKeyBeforeQueueing方法,具体做了如下动作:
1)将result去掉了ACTION_PASS_TO_USER标志位;
2)调用interceptPowerKeyDown和interceptPowerKeyUp分别处理电源键的按下和抬起,这两个处理函数我们在下一章的亮灭屏流程中详细介绍。
调用完interceptKeyBeforeQueueing之后,接着调enqueueInboundEventLocked将该事件入队。
InputReader线程将事件入队后,InputDispatch线程在looper处理里面,会将事件取出,然后调用dispatchKeyLocked进行事件的派发处理。
如果判断是KEYCODE_POWER,isValidGlobalKey会返回true, 接着PhoneWindowManager的interceptKeyBeforeDispatching则返回-1。
interceptKeyBeforeDispatching返回的-1赋值给InputDispatcher里面delay,判断小于0,则会将interceptKeyResult标志设置为INTERCEPT_KEY_RESULT_SKIP。
在dispatchKeyLocked函数里面会判断上面的interceptKeyResult标志,如果是INTERCEPT_KEY_RESULT_SKIP,则Power键会drop掉,不会传递给应用。
电源键传递流程总结一下,在入队前通过传递给策略类的interceptKeyBeforeQueueing来处理具体业务,而在派发流程里面则会忽略掉。
三、亮灭屏处理流程
1. 亮屏处理流程
亮屏的触发是电源键按下时候触发的,我们来看下interceptPowerKeyDown,判断interactive为灭屏状态,则调用wakeUpFromPowerKey唤醒系统。
wakeUpFromPowerKey调用到PowerManagerService的wakeUp函数。
PowerManagerService wakeUp进行权限检查后,调用到wakeUpInternal。
可以看到主要做了两件事,调用wakeUpNoUpdateLocked,如果返回true,则调用updatePowerStateLocked。
我们先来看下wakeUpNoUpdateLocked里面做了哪些动作,首先做了一些状态判断和变量赋值,然后执行:
1)setWakefulnessLocked,设置wakefulness为WAKEFULNESS_AWAKE唤醒状态,并发送亮屏广播;
2)mNotifier.onWakeUp,通知BatteryStatsService、AppService系统服务唤醒状态变化;
3)userActivityNoUpdateLocked更新用户活动时间,重置超时灭屏时间。
接下来我们看updatePowerStateLocked,这个里面主要是在updateDisplayPowerStateLocked里面处理亮屏的关键动作:
1)updateDisplayPowerStateLocked调用requestPowerState设置mPendingRequestLocked变量;
2)调用sendUpdatePowerStateLocked发送MSG_UPDATE_POWER_STATE消息,MSG_UPDATE_POWER_STATE的消息处理里面调用updatePowerState;
3)updatePowerState调用animateScreenStateChange;
4)根据状态判断后调用setScreenState(Display.STATE_ON)->
DisplayPowerController::setScreenState()->DisplayPowerState::setScreenState(),通过scheduleScreenUpdate启动一个mScreenUpdateRunnable;
5)mScreenUpdateRunnable里面调用mPhotonicModulator.setState(mScreenState, brightnessState),
赋值mPendingBacklight变量后通知PhotonicModulator线程去处理,调用到mBlanker.requestDisplayState;
6)DisplayManagerService在initPowerManagement初始化的时候会注册requestDisplayState的处理,具体处理步骤是:requestDisplayState->requestGlobalDisplayStateInternal->applyGlobalDisplayStateLocked->updateDisplayStateLocked;
7)LocalDisplayAdapter::requestDisplayStateLocked调用背光的设置函数setDisplayBrightness。
至此SurfaceControl再通过native方法通过binder通信通知surfaceflinger设置背光。
SurfaceFlinger通过调用Hal层的实现,操作内核背光节点,从而完成背光的设置。
2. 灭屏处理流程
灭屏是按下电源键后抬起的时候触发,流程这块我们来看interceptPowerKeyUp,做了一些长按和多次点击判断后,正常短按点击会跑到powerPress函数。
powerPress进一步调用goToSleepFromPowerButton函数。
进而调用到PowerManagerService的goToSleep,进行权限检查后调用goToSleepInternal。
这里可以看到调用了goToSleepNoUpdateLocked和updatePowerStateLocked,也就是在goToSleepNoUpdateLocked里面设置灭屏状态。
1)在goToSleepNoUpdateLocked()中完成发送了将要休眠的通知;
2) 修改了Wakefulness,将其置成WAKEFULNESS_DOZING,将mDirty置位DIRTY_WAKEFULNESS。
更多的实际动作在updatePowerStateLocked()中完成,背光变化流程和3.1章节介绍的亮屏流程是一样的。
亮灭屏流程总结一下,亮屏流程通过wakeUpNoUpdateLocked设置唤醒状态,灭屏流程通过goToSleepNoUpdateLocked设置灭屏状态,最终都是调用updatePowerStateLocked来完成最终背光变化动作。
参考链接:
[1] https://blog.csdn.net/qq_35865125/article/details/80637809
[2] https://www.jianshu.com/p/961207f442f9
长按关注
内核工匠微信
Linux 内核黑科技 | 技术文章 | 精选教程