本文基于Android S源码进行梳理。
学习目标
结合AOSP源码了解按下Power键后亮屏的系统流程。
系列回顾
Power键亮屏简介
简要概括一下Power键亮屏的流程,当用户按下power键后,InputReader从EventHub中读取到事件,交由InputDispatcher进行分发,通知PhoneWindowManager对此事件进行处理,PhoneWindowManager处理后,通知PowerManagerService进行wakeUp处理,随后PowerManagerService在内部更新wakefulness并发出亮屏广播之后,再通知DisplayPowerController进行亮屏状态更新,在此期间DisplayPowerController会根据Window绘制状态进行阻塞亮屏,待绘制完毕后,进行亮屏动画和设置屏幕亮度的操作。
下面是整个Power键亮屏的时序图:
TODO:这里可能还缺一张类图,以后补上^_^。
到这里,如果只是对Power键亮屏流程了解一下的同学,可以不用往下看了,下面就是具体业务中重要环节的详细介绍。
业务梳理
相关类
- frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
- frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
- frameworks/base/services/core/java/com/android/server/power/Notifier.java
- frameworks/base/services/core/java/com/android/server/display/DisplayPowerController.java
- frameworks/base/services/core/java/com/android/server/display/RampAnimator.java
- frameworks/base/services/core/java/com/android/server/display/ColorFade.java
- frameworks/base/services/core/java/com/android/server/display/DisplayPowerState.java
- frameworks/base/services/core/java/com/android/server/display/DisplayManagerService.java
- frameworks/base/services/core/java/com/android/server/display/LocalDisplayAdapter.java
- frameworks/base/services/core/java/com/android/server/lights/LightsService.java
部分类名解释与缩写
类名 | 作用 | 缩写 |
---|---|---|
PowerManagerService | Power状态管理 | PMS |
DisplayPowerController | 管理设备Display状态,主要处理近距sensor,亮灭屏 | DPC |
Notifier | Power侧用于通知其他系统模块 | |
RampAnimator | Display侧亮度动画 | |
ColorFade | 高端机器用于亮灭屏的蒙层 |
1. Power键事件处理
本章所讲的就是Power键按下亮屏的流程,对系统有一定了解的同学肯定知晓,按下power键后,kernel会将此事件进行中断,InputReader从EventHub中读取到事件后,InputDispatcher会callback给PhoneWindowManager,callback方法是interceptKeyBeforeQueueing:
frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
// TODO(b/117479243): handle it in InputPolicy
/** { @inheritDoc } */
@Override
public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
...
// 处理长按、连击事件, 灭屏流程
if ((event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
handleKeyGesture(event, interactiveAndOn);
}
...
// Handle special keys.
switch (keyCode) {
...
// 处理Power键 down和up事件
case KeyEvent.KEYCODE_POWER: {
EventLogTags.writeInterceptPower(
KeyEvent.actionToString(event.getAction()),
mPowerKeyHandled ? 1 : 0,
mSingleKeyGestureDetector.getKeyPressCounter(KeyEvent.KEYCODE_POWER));
// Any activity on the power button stops the accessibility shortcut
result &= ~ACTION_PASS_TO_USER;
isWakeKey = false; // wake-up will be handled separately
if (down) {
interceptPowerKeyDown(event, interactiveAndOn);
} else {
interceptPowerKeyUp(event, canceled);
}
break;
}
...
}
按下power键,其实是分为down和up事件,而亮屏流程触发再其down事件处理方法中:
frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
private void interceptPowerKeyDown(KeyEvent event, boolean interactive) {
// 获取一个wakelock,防止cpu在此流程期间休眠
// Hold a wake lock until the power key is released.
if (!mPowerKeyWakeLock.isHeld()) {
mPowerKeyWakeLock.acquire();
}
mWindowManagerFuncs.onPowerKeyDown(interactive);
// 对于来电场景下,按下power键的处理
// Stop ringing or end call if configured to do so when power is pressed.
TelecomManager telecomManager = getTelecommService();
boolean hungUp = false;
if (telecomManager != null) {
if (telecomManager.isRinging()) {
// Pressing Power while there's a ringing incoming
// call should silence the ringer.
telecomManager.silenceRinger();
} else if ((mIncallPowerBehavior
& Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) != 0
&& telecomManager.isInCall() && interactive) {
// Otherwise, if "Power button ends call" is enabled,
// the Power button will hang up any current active call.
hungUp = telecomManager.endCall();
}
}
...
// If the power key has still not yet been handled, then detect short
// press, long press, or multi press and decide what to do.
mPowerKeyHandled = mPowerKeyHandled || hungUp
|| handledByPowerManager || mKeyCombinationManager.isPowerKeyIntercepted();
if (!mPowerKeyHandled) {
if (!interactive) {
// wake up start
wakeUpFromPowerKey(event.getDownTime());
}
} else {
// handled by another power key policy.
if (!mSingleKeyGestureDetector.isKeyIntercepted(KEYCODE_POWER)) {
mSingleKeyGestureDetector.reset();
}
}
}
private void wakeUpFromPowerKey(long eventTime) {
...
if (wakeUp(eventTime, mAllowTheaterModeWakeFromPowerKey,
PowerManager.WAKE_REASON_POWER_BUTTON, "android.policy:POWER")) {
// Start HOME with "reason" extra if sleeping for more than mWakeUpToLastStateTimeout
if (shouldWakeUpWithHomeIntent()) {
startDockOrHome(DEFAULT_DISPLAY, /*fromHomeKey*/ false, /*wakenFromDreams*/ true,
PowerManager.wakeReasonToString(PowerManager.WAKE_REASON_POWER_BUTTON));
}
}
}
private boolean wakeUp(long wakeTime, boolean wakeInTheaterMode, @WakeReason int reason,
String details) {
...
// call powermanagerservice wakeup
mPowerManager.wakeUp(wakeTime, reason, details);
return true;
}
总结一下down事件的业务流程,当收到KEYCODE_POWER之后,在其down事件中,首先判断是否持有mPowerKeyWakeLock,如果没有的话就申请一个wakelock,这样可以防止在此业务期间,CPU进入休眠,通过一系列的条件判断,调用PowerManager的wakeUp接口。
而up事件就相对简单,主要是收尾工作和释放wakelock:
private void interceptPowerKeyUp(KeyEvent event, boolean canceled) {
final boolean handled = canceled || mPowerKeyHandled;
if (!handled) {
if ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) == 0) {
// Abort possibly stuck animations only when power key up without long press case.
mHandler.post(mWindowManagerFuncs::triggerAnimationFailsafe);
}
} else {
// handled by single key or another power key policy.
if (!mSingleKeyGestureDetector.isKeyIntercepted(KEYCODE_POWER)) {
mSingleKeyGestureDetector.reset();
}
}
finishPowerKeyPress();
}
private void finishPowerKeyPress() {
mPowerKeyHandled = false;
if (mPowerKeyWakeLock.isHeld()) {
mPowerKeyWakeLock.release();
}
}
2. PMS侧处理
本节中主要介绍PMS侧处理,梳理在PMS中如何处理亮屏请求,如何通知DPC处理亮屏请求。
2.1 wakeUp
上一节中,最后PhoneWindowManager通过PowerManager接口wakeUp方法调用,通知PowerManager模块执行亮屏动作,这里最终响应是在PowerManagerService中的wakeUp方法:
frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
@Override // Binder call
public void wakeUp(long eventTime, @WakeReason int reason, String details,
String opPackageName) {
if (eventTime > mClock.uptimeMillis()) {
throw new IllegalArgumentException("event time must not be in the future");
}
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.DEVICE_POWER, null);
final int uid = Binder.getCallingUid();
final long ident = Binder.clearCallingIdentity();
try {
wakeDisplayGroup(Display.DEFAULT_DISPLAY_GROUP, eventTime, reason, details, uid,
opPackageName, uid);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
这里重点说下以下两点:
1. reason参数
可以看到此函数中有一个reason参数,PMS定义了若干种reason:
WAKE_REASON_UNKNOWN // 未知原因
WAKE_REASON_POWER_BUTTON // 按power键唤醒
WAKE_REASON_APPLICATION // 应用唤醒
WAKE_REASON_PLUGGED_IN // 插入唤醒
WAKE_REASON_GESTURE // 手势唤醒
WAKE_REASON_CAMERA_LAUNCH // 相机启动
WAKE_REASON_WAKE_KEY // 唤醒键
WAKE_REASON_WAKE_MOTION // 唤醒操作触发
WAKE_REASON_HDMI // HDMI唤醒
WAKE_REASON_DISPLAY_GROUP_ADDED // 其他Display区域增加
WAKE_REASON_DISPLAY_GROUP_TURNED_ON // 其他Display区域点亮
2. R和S变化
在R上,PMS的wakeUp中是wakeUpInternal,而在S上变更成wakeDisplayGroup,具体的变化是增加了参数DisplayGroupId,这个是为了增加对多屏设备亮屏的处理。
2.2 wakeDisplayGroup
在wakeUp中,PMS调用了内部方法wakeDisplayGroup:
frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
private void wakeDisplayGroup(int groupId, long eventTime, @WakeReason int reason,
String details, int uid, String opPackageName, int opUid) {
synchronized (mLock) {
// 判断是否可以进行亮屏,设置Wakefulness以及相关参数,发送亮屏广播
if (wakeDisplayGroupNoUpdateLocked(groupId, eventTime, reason, details, uid,
opPackageName, opUid)) {
// 更新PMS中状态参数,通知DPC处理亮屏操作
updatePowerStateLocked();
}
}
}
这里面有两个非常重要的方法:wakeDisplayGroupNoUpdateLocked和updatePowerStateLocked。
2.3 亮屏广播
"android.intent.action.SCREEN_ON"这个很多开发者肯定不会陌生,这个是在屏幕即将点亮的时候发出的广播,整个亮屏广播的发送时序图如下:
2.3.1 wakeDisplayGroupNoUpdateLocked
此函数主要作用就是判断当前是否具备亮屏的条件,设置Wakefulness以及相关参数:
frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
private boolean wakeDisplayGroupNoUpdateLocked(int groupId, long eventTime,
@WakeReason int reason, String details, int uid, String opPackageName, int opUid) {
...
// 判断触发时间是否正确,系统是否挂起或者是否还在开机状态
if (eventTime < mLastSleepTime || mForceSuspendActive || !mSystemReady) {
return false;
}
...
// 根据Wakefulness,判断是否已亮屏
final int currentState = mDisplayGroupPowerStateMapper.getWakefulnessLocked(groupId);
if (currentState == WAKEFULNESS_AWAKE) {
// 对非系统启动完毕场景进行特殊处理
if (!mBootCompleted && sQuiescent) {
mDirty |= DIRTY_QUIESCENT;
return true;
}
return false;
}
...
try {
Slog.i(TAG, "Powering on display group from"
+ PowerManagerInternal.wakefulnessToString(currentState)
+ " (groupId=" + groupId
+ ", uid=" + uid
+ ", reason=" + PowerManager.wakeReasonToString(reason)
+ ", details=" + details
+ ")...");
Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, TRACE_SCREEN_ON, groupId);
// 设置Wakefulness
setWakefulnessLocked(groupId, WAKEFULNESS_AWAKE, eventTime, uid, reason, opUid,
opPackageName, details);
// 设置亮屏时间和状态到mDisplayGroupPowerStateMapper,DisplayGroupPowerStateMapper是S新增解决多屏Power管理的方案
mDisplayGroupPowerStateMapper.setLastPowerOnTimeLocked(groupId, eventTime);
mDisplayGroupPowerStateMapper.setPoweringOnLocked(groupId, true);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_POWER);
}
return true;
}
此函数中有一个很重要的变量Wakefulness,PMS定义了四种状态:
WAKEFULNESS_ASLEEP //灭屏
WAKEFULNESS_AWAKE //亮屏
WAKEFULNESS_DREAMING //屏保
WAKEFULNESS_DOZING //doze模式
2.3.2 setWakefulnessLocked
frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
@VisibleForTesting
void setWakefulnessLocked(int groupId, int wakefulness, long eventTime, int uid, int reason,
int opUid, String opPackageName, String details) {
if (mDisplayGroupPowerStateMapper.setWakefulnessLocked(groupId, wakefulness)) {
// 引入mDirty参数,用于判断当前Power State是否发生变化
mDirty |= DIRTY_DISPLAY_GROUP_WAKEFULNESS;
// 设置全局参数状态,并对亮屏工作进行准备
setGlobalWakefulnessLocked(mDisplayGroupPowerStateMapper.getGlobalWakefulnessLocked(),
eventTime, reason, uid, opUid, opPackageName, details);
if (wakefulness == WAKEFULNESS_AWAKE) {
// Kick user activity to prevent newly awake group from timing out instantly.
userActivityNoUpdateLocked(
groupId, eventTime, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, uid);
}
}
}
此函数中最重要的就是参数mDirty,这个是PMS中一个非常重要的标志位,通过对它的位运算,可以知晓当前power状态是否发生变化,根据变化的位置可以进行对应的业务操作。
PMS中定义了以下DirtyState:
// Dirty bit: mWakeLocks changed
private static final int DIRTY_WAKE_LOCKS = 1 << 0;
// Dirty bit: mWakefulness changed
private static final int DIRTY_WAKEFULNESS = 1 << 1;
// Dirty bit: user activity was poked or may have timed out
private static final int DIRTY_USER_ACTIVITY = 1 << 2;
// Dirty bit: actual display power state was updated asynchronously
private static final int DIRTY_ACTUAL_DISPLAY_POWER_STATE_UPDATED = 1 << 3;
// Dirty bit: mBootCompleted changed
private static final int DIRTY_BOOT_COMPLETED = 1 << 4;
// Dirty bit: settings changed
private static final int DIRTY_SETTINGS = 1 << 5;
// Dirty bit: mIsPowered changed
private static final int DIRTY_IS_POWERED = 1 << 6;
// Dirty bit: mStayOn changed
private static final int DIRTY_STAY_ON = 1 << 7;
// Dirty bit: battery state changed
private static final int DIRTY_BATTERY_STATE = 1 << 8;
// Dirty bit: proximity state changed
private static final int DIRTY_PROXIMITY_POSITIVE = 1 << 9;
// Dirty bit: dock state changed
private static final int DIRTY_DOCK_STATE = 1 << 10;
// Dirty bit: brightness boost changed
private static final int DIRTY_SCREEN_BRIGHTNESS_BOOST = 1 << 11;
// Dirty bit: sQuiescent changed
private static final int DIRTY_QUIESCENT = 1 << 12;
// Dirty bit: VR Mode enabled changed
private static final int DIRTY_VR_MODE_CHANGED = 1 << 13;
// Dirty bit: attentive timer may have timed out
private static final int DIRTY_ATTENTIVE = 1 << 14;
// Dirty bit: display group wakefulness has changed
private static final int DIRTY_DISPLAY_GROUP_WAKEFULNESS = 1 << 16;
2.3.3 setGlobalWakefulnessLocked
frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
private void setGlobalWakefulnessLocked(int wakefulness, long eventTime, int reason, int uid,
int opUid, String opPackageName, String details) {
...
// 通知系统中其他模块Wakefulness开始变化,发送亮屏广播
if (mNotifier != null) {
mNotifier.onWakefulnessChangeStarted(wakefulness, reason, eventTime);
}
...
}
在这个方法中,用到了PMS中另外一个重要的成员变量mNotifier,其主要功能就是告知系统其他核心服务或者模块,当前Power State。
2.3.4 Notifier.onWakefulnessChangeStarted
frameworks/base/services/core/java/com/android/server/power/Notifier.java
/**
* Notifies that the device is changing wakefulness.
* This function may be called even if the previous change hasn't finished in
* which case it will assume that the state did not fully converge before the
* next transition began and will recover accordingly.
*/
public void onWakefulnessChangeStarted(final int wakefulness, int reason, long eventTime) {
final boolean interactive = PowerManagerInternal.isInteractive(wakefulness);
if (DEBUG) {
Slog.d(TAG, "onWakefulnessChangeStarted: wakefulness=" + wakefulness
+ ", reason=" + reason + ", interactive=" + interactive);
}
// Tell the activity manager about changes in wakefulness, not just interactivity.
// It needs more granularity than other components.
// 通知AMS Wakefulness 变化
mHandler.post(new Runnable() {
@Override
public void run() {
mActivityManagerInternal.onWakefulnessChanged(wakefulness);
}
});
...
// 触发广播消息发送
handleEarlyInteractiveChange();
}
}
/**
* Handle early interactive state changes such as getting applications or the lock
* screen running and ready for the user to see (such as when turning on the screen).
*/
private void handleEarlyInteractiveChange() {
synchronized (mLock) {
if (mInteractive) {
// Waking up...
// 通知WMS正在亮屏
mHandler.post(() -> mPolicy.startedWakingUp(mInteractiveChangeReason));
// Send interactive broadcast.
mPendingInteractiveState = INTERACTIVE_STATE_AWAKE;
mPendingWakeUpBroadcast = true;
// 发送广播
updatePendingBroadcastLocked();
} else {
// Going to sleep...
// Tell the policy that we started going to sleep.
mHandler.post(() -> mPolicy.startedGoingToSleep(mInteractiveChangeReason));
}
}
}
private void updatePendingBroadcastLocked() {
if (!mBroadcastInProgress
&& mPendingInteractiveState != INTERACTIVE_STATE_UNKNOWN
&& (mPendingWakeUpBroadcast || mPendingGoToSleepBroadcast
|| mPendingInteractiveState != mBroadcastedInteractiveState)) {
mBroadcastInProgress = true;
// 为了避免发送广播的时候,为了避免CPU还在休眠,申请SuspendBlocker
mSuspendBlocker.acquire();
Message msg = mHandler.obtainMessage(MSG_BROADCAST);
msg.setAsynchronous(true);
mHandler.sendMessage(msg);
}
}
这里最后通过消息机制触发了亮屏广播的发送,由于业务单一,这里就不贴代码了。
3. DPC侧处理
3.1 PMS.updatePowerStateLocked
上一节中,在PMS. wakeDisplayGroup执行完wakeDisplayGroupNoUpdateLocked之后,在updatePowerStateLocked中进行DPC侧业务处理的发起:
frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
/**
* Updates the global power state based on dirty bits recorded in mDirty.
*
* This is the main function that performs power state transitions.
* We centralize them here so that we can recompute the power state completely
* each time something important changes, and ensure that we do it the same
* way each time. The point is to gather all of the transition logic here.
*/
private void updatePowerStateLocked() {
...
// Phase 3: Update display power state.
final boolean displayBecameReady = updateDisplayPowerStateLocked(dirtyPhase2);
...
}
/**
* Updates the display power state asynchronously.
* When the update is finished, the ready state of the displays will be updated. The display
* controllers post a message to tell us when the actual display power state
* has been updated so we come back here to double-check and finish up.
*
* This function recalculates the display power state each time.
*
* @return { @code true} if all displays became ready; { @code false} otherwise
*/
private boolean updateDisplayPowerStateLocked(int dirty) {
...
final boolean ready = mDisplayManagerInternal.requestPowerState(groupId,
displayPowerRequest, mRequestWaitForNegativeProximity);
...
}
这里最后调用了mDisplayManagerInternal的requestPowerState方法,DisplayManagerInternal是一个抽象类,其具体实现的类是DisplayManagerService,在其重写的方法中发起了对DPC的调用。感兴趣的可以去看下源码,这里就不贴了。
3.2 阻塞亮屏
在DPC中,最主要的一个业务就是进行亮屏前的最后准备,为什么说是亮屏前呢?因为里面有一个非常重要的环节就是阻塞亮屏,试想如果直接点亮屏幕,可能会出现什么情况,首先可能出现窗口还没有绘制,用户会观看到整个窗口绘制的过程,或者锁屏没有绘制,这样用户体验很差,所以在点亮屏幕前,我们需要进行一个阻塞亮屏的流程。
frameworks/base/services/core/java/com/android/server/display/DisplayPowerController.java
/**
* Requests a new power state.
* The controller makes a copy of the provided object and then
* begins adjusting the power state to match what was requested.
*
* @param request The requested power state.
* @param waitForNegativeProximity If true, issues a request to wait for
* negative proximity before turning the screen back on, assuming the screen
* was turned off by the proximity sensor.
* @return True if display is ready, false if there are important changes that must
* be made asynchronously (such as turning the screen on), in which case the caller
* should grab a wake lock, watch for { @link DisplayPowerCallbacks#onStateChanged()}
* then try the request again later until the state converges.
*/
public boolean requestPowerState(DisplayPowerRequest request,
boolean waitForNegativeProximity) {
...
if (changed) {
mDisplayReadyLocked = false;
if (!mPendingRequestChangedLocked) {
mPendingRequestChangedLocked = true;
sendUpdatePowerStateLocked();
}
}
return mDisplayReadyLocked;
}
}
private void sendUpdatePowerStateLocked() {
if (!mStopped && !mPendingUpdatePowerStateLocked) {
mPendingUpdatePowerStateLocked = true;
// 发送MSG_UPDATE_POWER_STATE
Message msg = mHandler.obtainMessage(MSG_UPDATE_POWER_STATE);
mHandler.sendMessage(msg);
}
}
private void updatePowerState() {
...
// Animate the screen state change unless already animating.
// The transition may be deferred, so after this point we will use the
// actual state instead of the desired one.
final int oldState = mPowerState.getScreenState();
// 准备执行屏幕变化动画,这里也可以认为是发起亮屏动画
animateScreenStateChange(state, performScreenOffTransition);
state = mPowerState.getScreenState();
}
private void animateScreenStateChange(int target, boolean performScreenOffTransition) {
...
if (target == Display.STATE_ON) {
// Want screen on. The contents of the screen may not yet
// be visible if the color fade has not been dismissed because
// its last frame of animation is solid black.
// 阻塞亮屏,如果当前不满足亮屏,那么直接return
if (!setScreenState(Display.STATE_ON)) {
return; // screen on blocked
}
if (USE_COLOR_FADE_ON_ANIMATION && mColorFadeEnabled && mPowerRequest.isBrightOrDim()) {
// Perform screen on animation.
if (mPowerState.getColorFadeLevel() == 1.0f) {
mPowerState.dismissColorFade();
} else if (mPowerState.prepareColorFade(mContext,
mColorFadeFadesConfig ?
ColorFade.MODE_FADE :
ColorFade.MODE_WARM_UP)) {
mColorFadeOnAnimator.start();
} else {
mColorFadeOnAnimator.end();
}
} else {
// Skip screen on animation.
mPowerState.setColorFadeLevel(1.0f);
mPowerState.dismissColorFade();
}
}
...
}
从最后animateScreenStateChange可以看到,如果setScreenState不满足要求的话,就会直接return,这并不是表示流程结束了,而是阻塞亮屏的开始:
frameworks/base/services/core/java/com/android/server/display/DisplayPowerController.java
private boolean setScreenState(int state, boolean reportOnly) {
...
if (!isOff
&& (mReportedScreenStateToPolicy == REPORTED_TO_POLICY_SCREEN_OFF
|| mReportedScreenStateToPolicy == REPORTED_TO_POLICY_UNREPORTED)) {
setReportedScreenState(REPORTED_TO_POLICY_SCREEN_TURNING_ON);
if (mPowerState.getColorFadeLevel() == 0.0f) {
// 阻塞亮屏
blockScreenOn();
} else {
unblockScreenOn();
}
// 向PhoneWindowManager传入mPendingScreenOnUnblocker
mWindowManagerPolicy.screenTurningOn(mDisplayId, mPendingScreenOnUnblocker);
}
...
}
private void blockScreenOn() {
if (mPendingScreenOnUnblocker == null) {
Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, SCREEN_ON_BLOCKED_TRACE_NAME, 0);
mPendingScreenOnUnblocker = new ScreenOnUnblocker();
mScreenOnBlockStartRealTime = SystemClock.elapsedRealtime();
Slog.i(TAG, "Blocking screen on until initial contents have been drawn.");
}
}
private final class ScreenOnUnblocker implements WindowManagerPolicy.ScreenOnListener {
@Override
public void onScreenOn() {
Message msg = mHandler.obtainMessage(MSG_SCREEN_ON_UNBLOCKED, this);
mHandler.sendMessage(msg);
}
}
可以看到,在blockScreenOn中,创建了一个ScreenUnblocker的实例mPendingScreenOnUnblocker,并通过WindowManagerPolicy传给了PhoneWindowMangaer:
frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
// Called on the DisplayManager's DisplayPowerController thread.
@Override
public void screenTurningOn(int displayId, final ScreenOnListener screenOnListener) {
if (DEBUG_WAKEUP) Slog.i(TAG, "Display " + displayId + " turning on...");
if (displayId == DEFAULT_DISPLAY) {
Trace.asyncTraceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "screenTurningOn",
0 /* cookie */);
updateScreenOffSleepToken(false);
mDefaultDisplayPolicy.screenTurnedOn(screenOnListener);
mBootAnimationDismissable = false;
synchronized (mLock) {
if (mKeyguardDelegate != null && mKeyguardDelegate.hasKeyguard()) {
mHandler.removeMessages(MSG_KEYGUARD_DRAWN_TIMEOUT);
// 发送消息,通知锁屏绘制完毕
mHandler.sendEmptyMessageDelayed(MSG_KEYGUARD_DRAWN_TIMEOUT,
getKeyguardDrawnTimeout());
mKeyguardDelegate.onScreenTurningOn(mKeyguardDrawnCallback);
} else {
if (DEBUG_WAKEUP) Slog.d(TAG,
"null mKeyguardDelegate: setting mKeyguardDrawComplete.");
mHandler.sendEmptyMessage(MSG_KEYGUARD_DRAWN_COMPLETE);
}
}
} else {
mScreenOnListeners.put(displayId, screenOnListener);
mWindowManagerInternal.waitForAllWindowsDrawn(() -> {
if (DEBUG_WAKEUP) Slog.i(TAG, "All windows ready for display: " + displayId);
mHandler.sendMessage(mHandler.obtainMessage(MSG_WINDOW_MANAGER_DRAWN_COMPLETE,
displayId, 0));
}, WAITING_FOR_DRAWN_TIMEOUT, displayId);
}
}
正常在Keyguard绘制完毕后,PhoneWindowManager会调用onScreenOn callback通知到DPC。当DPC中收到onScreenOn,会发送消息进行unblock,消息中就执行了两个函数unblockScreenOn和updatePowerState:
frameworks/base/services/core/java/com/android/server/display/DisplayPowerController.java
private void unblockScreenOn() {
if (mPendingScreenOnUnblocker != null) {
// 这里将mPendingScreenOnUnblocker置成了null
mPendingScreenOnUnblocker = null;
long delay = SystemClock.elapsedRealtime() - mScreenOnBlockStartRealTime;
Slog.i(TAG, "Unblocked screen on after " + delay + " ms");
Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, SCREEN_ON_BLOCKED_TRACE_NAME, 0);
}
}
然后又再次执行了updatePowerState-animateScreenStateChange-setScreenState,在setScreenState中最后因为mPendingScreenOnUnblocker = null,所以返回了true,这样animateScreenStateChange就可以继续执行下去,阻塞亮屏的流程也就全部执行完毕了。
3.3 亮屏动画
在上一节最后提到,当阻塞亮屏流程结束,那么原本DPC中animateScreenStateChange就可以继续执行下去了:
frameworks/base/services/core/java/com/android/server/display/DisplayPowerController.java
private void animateScreenStateChange(int target, boolean performScreenOffTransition) {
...
if (target == Display.STATE_ON) {
// Want screen on. The contents of the screen may not yet
// be visible if the color fade has not been dismissed because
// its last frame of animation is solid black.
// 现在已经不阻塞亮屏了
if (!setScreenState(Display.STATE_ON)) {
return; // screen on blocked
}
if (USE_COLOR_FADE_ON_ANIMATION && mColorFadeEnabled && mPowerRequest.isBrightOrDim()) {
// Perform screen on animation.
if (mPowerState.getColorFadeLevel() == 1.0f) {
mPowerState.dismissColorFade();
} else if (mPowerState.prepareColorFade(mContext,
mColorFadeFadesConfig ?
ColorFade.MODE_FADE :
ColorFade.MODE_WARM_UP)) {
mColorFadeOnAnimator.start();
} else {
mColorFadeOnAnimator.end();
}
} else {
// Skip screen on animation.
mPowerState.setColorFadeLevel(1.0f);
mPowerState.dismissColorFade();
}
}
...
}
这里有个对象ColorFade,这是什么呢?在高端的机器上,为了让用户亮灭屏体验更加,有一个蒙层会在亮灭屏的时候,盖在最上面,通过设置其alpha值,可以实现亮灭屏的时候的渐变动效。
在DPC.updatePowerState中完成阻塞亮屏,并执行ColorFade的渐变动效后,就到了亮屏的最后一个环节,设置屏幕的亮度,在updatePowerState中最后调用了animateScreenBrightness:
frameworks/base/services/core/java/com/android/server/display/DisplayPowerController.java
private void animateScreenBrightness(float target, float sdrTarget, float rate) {
if (DEBUG) {
Slog.d(TAG, "Animating brightness: target=" + target + ", sdrTarget=" + sdrTarget
+ ", rate=" + rate);
}
if (mScreenBrightnessRampAnimator.animateTo(target, sdrTarget, rate)) {
Trace.traceCounter(Trace.TRACE_TAG_POWER, "TargetScreenBrightness", (int) target);
// TODO(b/153319140) remove when we can get this from the above trace invocation
SystemProperties.set("debug.tracing.screen_brightness", String.valueOf(target));
noteScreenBrightness(target);
}
}
通过mScreenBrightnessRampAnimator进行一个亮度动画,mScreenBrightnessRampAnimator是由DualRampAnimator实例化的,在DPC初始化的时候创建:
frameworks/base/services/core/java/com/android/server/display/DisplayPowerController.java
private void initialize(int displayState) {
...
mScreenBrightnessRampAnimator = new DualRampAnimator<>(mPowerState,
DisplayPowerState.SCREEN_BRIGHTNESS_FLOAT,
DisplayPowerState.SCREEN_SDR_BRIGHTNESS_FLOAT);
mScreenBrightnessRampAnimator.setListener(mRampAnimatorListener);
...
}
这里有两个很重要的参数:DisplayPowerState.SCREEN_BRIGHTNESS_FLOAT, DisplayPowerState.SCREEN_SDR_BRIGHTNESS_FLOAT。这是两个参数中,会通过其setValue的方法设置亮度。这里就从mScreenBrightnessRampAnimator的animateTo为入口来进一步分析:
frameworks/base/services/core/java/com/android/server/display/RampAnimator.java
/**
* Starts animating towards the specified values.
*
* If this is the first time the property is being set or if the rate is 0,
* the value jumps directly to the target.
*
* @param firstTarget The first target value.
* @param secondTarget The second target value.
* @param rate The convergence rate in units per second, or 0 to set the value immediately.
* @return True if either target differs from the previous target.
*/
public boolean animateTo(float firstTarget, float secondTarget, float rate) {
final boolean firstRetval = mFirst.animateTo(firstTarget, rate);
final boolean secondRetval = mSecond.animateTo(secondTarget, rate);
return firstRetval && secondRetval;
}
/**
* Starts animating towards the specified value.
*
* If this is the first time the property is being set or if the rate is 0,
* the value jumps directly to the target.
*
* @param target The target value.
* @param rate The convergence rate in units per second, or 0 to set the value immediately.
* @return True if the target differs from the previous target.
*/
public boolean animateTo(float target, float rate) {
// Immediately jump to the target the first time.
if (mFirstTime || rate <= 0) {
if (mFirstTime || target != mCurrentValue) {
mFirstTime = false;
mRate = 0;
mTargetValue = target;
mCurrentValue = target;
mProperty.setValue(mObject, target);
if (mAnimating) {
mAnimating = false;
cancelAnimationCallback();
}
if (mListener != null) {
mListener.onAnimationEnd();
}
return true;
}
return false;
}
// Adjust the rate based on the closest target.
// If a faster rate is specified, then use the new rate so that we converge
// more rapidly based on the new request.
// If a slower rate is specified, then use the new rate only if the current
// value is somewhere in between the new and the old target meaning that
// we will be ramping in a different direction to get there.
// Otherwise, continue at the previous rate.
if (!mAnimating
|| rate > mRate
|| (target <= mCurrentValue && mCurrentValue <= mTargetValue)
|| (mTargetValue <= mCurrentValue && mCurrentValue <= target)) {
mRate = rate;
}
final boolean changed = (mTargetValue != target);
mTargetValue = target;
// Start animating.
if (!mAnimating && target != mCurrentValue) {
mAnimating = true;
mAnimatedValue = mCurrentValue;
mLastFrameTimeNanos = System.nanoTime();
postAnimationCallback();
}
return changed;
}
这里的mFirst和mSecond就是前面提到的DisplayPowerState.SCREEN_BRIGHTNESS_FLOAT, DisplayPowerState.SCREEN_SDR_BRIGHTNESS_FLOAT。根据代码中注释来看,最终调用了postAnimationCallback:
frameworks/base/services/core/java/com/android/server/display/RampAnimator.java
private void postAnimationCallback() {
mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, mAnimationCallback, null);
}
private final Runnable mAnimationCallback = new Runnable() {
@Override // Choreographer callback
public void run() {
final long frameTimeNanos = mChoreographer.getFrameTimeNanos();
final float timeDelta = (frameTimeNanos - mLastFrameTimeNanos)
* 0.000000001f;
mLastFrameTimeNanos = frameTimeNanos;
// Advance the animated value towards the target at the specified rate
// and clamp to the target. This gives us the new current value but
// we keep the animated value around to allow for fractional increments
// towards the target.
final float scale = ValueAnimator.getDurationScale();
if (scale == 0) {
// Animation off.
mAnimatedValue = mTargetValue;
} else {
final float amount = timeDelta * mRate / scale;
if (mTargetValue > mCurrentValue) {
mAnimatedValue = Math.min(mAnimatedValue + amount, mTargetValue);
} else {
mAnimatedValue = Math.max(mAnimatedValue - amount, mTargetValue);
}
}
final float oldCurrentValue = mCurrentValue;
mCurrentValue = mAnimatedValue;
// 判断是否满足亮度,如果不满足再次设置亮度,直到满足位置
if (oldCurrentValue != mCurrentValue) {
mProperty.setValue(mObject, mCurrentValue);
}
if (mTargetValue != mCurrentValue) {
postAnimationCallback();
} else {
mAnimating = false;
if (mListener != null) {
mListener.onAnimationEnd();
}
}
}
};
这一段,最终在mScreenUpdateRunnable中结束,这里引入了mPhotonicModulator,这个是在DisplayPowerState的一个内部类,其继承于Thread,那么不用深思,具体实现肯定在其的run方法中:
frameworks/base/services/core/java/com/android/server/display/DisplayPowerState.java
/**
* Updates the state of the screen and backlight asynchronously on a separate thread.
*/
private final class PhotonicModulator extends Thread {
...
public boolean setState(int state, float brightnessState, float sdrBrightnessState) {
synchronized (mLock) {
...
boolean changeInProgress = mStateChangeInProgress || mBacklightChangeInProgress;
mStateChangeInProgress = stateChanged || mStateChangeInProgress;
mBacklightChangeInProgress = backlightChanged || mBacklightChangeInProgress;
// 唤醒wait
if (!changeInProgress) {
mLock.notifyAll();
}
}
return !mStateChangeInProgress;
}
}
...
@Override
public void run() {
for (;;) {
// Get pending change.
final int state;
final boolean stateChanged;
final float brightnessState;
final float sdrBrightnessState;
final boolean backlightChanged;
synchronized (mLock) {
...
// 死循环等待变化
if (!stateChanged && !backlightChanged) {
try {
mLock.wait();
} catch (InterruptedException ex) {
if (mStopped) {
return;
}
}
continue;
}
...
}
...
mBlanker.requestDisplayState(mDisplayId, state, brightnessState,
sdrBrightnessState);
}
}
}
可以看到最终在线程的run方法中调用了mBlanker.requestDisplayState,这个mBlanker其实是接口DisplayBlanker的实例,其最终callback到了DisplayManagerService中:
frameworks/base/services/core/java/com/android/server/display/DisplayManagerService.java
/** { @link DisplayBlanker} used by all { @link DisplayPowerController}s. */
private final DisplayBlanker mDisplayBlanker = new DisplayBlanker() {
// Synchronized to avoid race conditions when updating multiple display states.
@Override
public synchronized void requestDisplayState(int displayId, int state, float brightness,
float sdrBrightness) {
...
if (state != Display.STATE_OFF) {
requestDisplayStateInternal(displayId, state, brightness, sdrBrightness);
}
}
};
private void requestDisplayStateInternal(int displayId, int state, float brightnessState,
float sdrBrightnessState) {
...
// Update the display state within the lock.
// Note that we do not need to schedule traversals here although it
// may happen as a side-effect of displays changing state.
final Runnable runnable;
final String traceMessage;
synchronized (mSyncRoot) {
...
runnable = updateDisplayStateLocked(mLogicalDisplayMapper.getDisplayLocked(displayId)
.getPrimaryDisplayDeviceLocked());
}
// Setting the display power state can take hundreds of milliseconds
// to complete so we defer the most expensive part of the work until
// after we have exited the critical section to avoid blocking other
// threads for a long time.
if (runnable != null) {
runnable.run();
}
...
}
private Runnable updateDisplayStateLocked(DisplayDevice device) {
...
// Only send a request for display state if display state has already been initialized.
if (state != Display.STATE_UNKNOWN) {
final BrightnessPair brightnessPair = mDisplayBrightnesses.get(displayId);
return device.requestDisplayStateLocked(state, brightnessPair.brightness,
brightnessPair.sdrBrightness);
}
}
...
}
在最后面,其实是通过updateDisplayStateLocked创建了一个runnable,那么设置屏幕亮度的业务也将会在此中进行,通过DisplayDevice的requestDisplayStateLocked进行创建runnable,但是DisplayDevice是一个抽象类,真正的实现是在LocalDisplayAdapter中:
frameworks/base/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@Override
public Runnable requestDisplayStateLocked(final int state, final float brightnessState,
final float sdrBrightnessState) {
...
// Defer actually setting the display state until after we have exited
// the critical section since it can take hundreds of milliseconds
// to complete.
return new Runnable() {
@Override
public void run() {
...
// 设置屏幕亮度
setDisplayBrightness(brightnessState, sdrBrightnessState);
...
}
private void setDisplayBrightness(float brightnessState,
float sdrBrightnessState) {
...
mBacklightAdapter.setBacklight(sdrBacklight, sdrNits, backlight, nits);
...
}
// Set backlight within min and max backlight values
void setBacklight(float sdrBacklight, float sdrNits, float backlight, float nits) {
if (mUseSurfaceControlBrightness || mForceSurfaceControl) {
if (BrightnessSynchronizer.floatEquals(
sdrBacklight, PowerManager.BRIGHTNESS_INVALID_FLOAT)) {
mSurfaceControlProxy.setDisplayBrightness(mDisplayToken, backlight);
} else {
mSurfaceControlProxy.setDisplayBrightness(mDisplayToken, sdrBacklight, sdrNits,
backlight, nits);
}
} else if (mBacklight != null) {
mBacklight.setBrightness(backlight);
}
}
前面提到在DisplayManagerService中最终执行了一个runnable,根据代码的走读,在runnable中最终执行到了LocalDisplayAdapter中mBacklight.setBrightness,这个最终响应是在LightsService中:
frameworks/base/services/core/java/com/android/server/lights/LightsService.java
@Override
public void setBrightness(float brightness) {
setBrightness(brightness, BRIGHTNESS_MODE_USER);
}
@Override
public void setBrightness(float brightness, int brightnessMode) {
...
setLightLocked(color, LIGHT_FLASH_NONE, 0, 0, brightnessMode);
...
}
private void setLightLocked(int color, int mode, int onMS, int offMS, int brightnessMode) {
...
setLightUnchecked(color, mode, onMS, offMS, brightnessMode);
...
}
private void setLightUnchecked(int color, int mode, int onMS, int offMS,
int brightnessMode) {
Trace.traceBegin(Trace.TRACE_TAG_POWER, "setLightState(" + mHwLight.id + ", 0x"
+ Integer.toHexString(color) + ")");
try {
if (mVintfLights != null) {
HwLightState lightState = new HwLightState();
lightState.color = color;
lightState.flashMode = (byte) mode;
lightState.flashOnMs = onMS;
lightState.flashOffMs = offMS;
lightState.brightnessMode = (byte) brightnessMode;
mVintfLights.get().setLightState(mHwLight.id, lightState);
} else {
setLight_native(mHwLight.id, color, mode, onMS, offMS, brightnessMode);
}
} catch (RemoteException | UnsupportedOperationException ex) {
Slog.e(TAG, "Failed issuing setLightState", ex);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_POWER);
}
}
可以看到,在LightsService中,最终完成了对底层亮屏的JNI调用。到这里上层的Power键亮屏流程全部梳理完毕。