当 Power 键灭屏时,会在 PhoneWindowManager 中处理按键事件后,调用到 PMS 的 gotoSleep()
进行灭屏处理,下面直接看看 PhoneWindowManger 中对 Power 键灭屏的处理以及和 PMS 的交互。
//frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
@Override
public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
//...
final boolean interactive = (policyFlags & FLAG_INTERACTIVE) != 0;
final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
final boolean canceled = event.isCanceled();
final int keyCode = event.getKeyCode();
//...
// Handle special keys.
switch (keyCode) {
//...
case KeyEvent.KEYCODE_POWER: {
// Any activity on the power button stops the accessibility shortcut
cancelPendingAccessibilityShortcutAction();
result &= ~ACTION_PASS_TO_USER;
isWakeKey = false; // wake-up will be handled separately
if (down) {//处理按下事件
interceptPowerKeyDown(event, interactive);
} else {//处理抬起事件
interceptPowerKeyUp(event, interactive, canceled);
}
break;
}
//...
}
//...
return result;
}
//frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
private void interceptPowerKeyUp(KeyEvent event, boolean interactive, boolean canceled) {
final boolean handled = canceled || mPowerKeyHandled;
mScreenshotChordPowerKeyTriggered = false;
cancelPendingScreenshotChordAction();
cancelPendingPowerKeyAction();
if (!handled) {
//...
// No other actions. Handle it immediately.
powerPress(eventTime, interactive, mPowerKeyPressCounter);
}
// Done. Reset our state.
finishPowerKeyPress();
}
在处理 Power 键抬起事件时,开始了灭屏流程。
//frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
private void powerPress(long eventTime, boolean interactive, int count) {
if (mScreenOnEarly && !mScreenOnFully) {
Slog.i(TAG, "Suppressed redundant power key press while "
+ "already in the process of turning the screen on.");
return;
}
if (count == 2) {//处理同时按两个按键的情况
powerMultiPressAction(eventTime, interactive, mDoublePressOnPowerBehavior);
} else if (count == 3) {//处理同时按三个个按键的情况
powerMultiPressAction(eventTime, interactive, mTriplePressOnPowerBehavior);
} else if (interactive && !mBeganFromNonInteractive) {
switch (mShortPressOnPowerBehavior) {
case SHORT_PRESS_POWER_NOTHING:
break;
case SHORT_PRESS_POWER_GO_TO_SLEEP://灭屏
mPowerManager.goToSleep(eventTime,
PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, 0);
break;
case SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP://灭屏,直接跳过 Doze 状态
mPowerManager.goToSleep(eventTime,
PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON,
PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);
break;
case SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP_AND_GO_HOME://灭屏,回到 Home
mPowerManager.goToSleep(eventTime,
PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON,
PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);
launchHomeFromHotKey();
break;
//...
}
}
}
在这里调用了 PowerManager 的 goToSleep()
方法来灭屏。
//frameworks/base/core/java/android/os/PowerManager.java
public void goToSleep(long time, int reason, int flags) {
try {
mService.goToSleep(time, reason, flags);
} catch (RemoteException e) { }
}
可以看到,在 PowerManger 中开始向下调用到了 PoweManagerService 中的 goToSleep()
中。
//frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
@Override // Binder call
public void goToSleep(long eventTime, int reason, int flags) {
if (eventTime > SystemClock.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 {
//调用 gotToSleepInternal
goToSleepInternal(eventTime, reason, flags, uid);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
这个方法的参数和 PowerManager,PhoneWindowManage r中的同名方法对应,需要注意的是第二个参数和第三个参数; 第二个参数:表示灭屏原因,在 PowerManager 中定义了一些常量值来表示; 第三个参数:是一个标识,用来表示是否直接进入灭屏,一般的灭屏流程,都会先进入 Doze 状态,然后才会进入 Sleep 状态,如果将 flag 设置为 1,则将会直接进入 Sleep 状态,这部分会在下文中逐渐分析到。
在 goToSleep() 方法中,检查权限之后,开始调用了 goToSleepInternal() 方法,该方法如下:
//frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
private void goToSleepInternal(long eventTime, int reason, int flags, int uid) {
synchronized (mLock) {
//详见【1.5】
if (goToSleepNoUpdateLocked(eventTime, reason, flags, uid)) {
updatePowerStateLocked();//详见【1.6】
}
}
}
这个方法逻辑很简单,首先是调用了 goToSleepNoUpdateLocked()
方法,并根据该方法返回值来决定是否调用 updatePowerStateLocked()
方法。
一般来说,goToSleepNoUpdateLocked() 都会返回 true,现在看看该方法:
//frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
@SuppressWarnings("deprecation")
private boolean goToSleepNoUpdateLocked(long eventTime, int reason, int flags, int uid) {
if (eventTime < mLastWakeTime
|| mWakefulness == WAKEFULNESS_ASLEEP
|| mWakefulness == WAKEFULNESS_DOZING
|| !mBootCompleted || !mSystemReady) {
return false;
}
try {
switch (reason) {
case PowerManager.GO_TO_SLEEP_REASON_DEVICE_ADMIN:
Slog.i(TAG, "Going to sleep due to device administration policy "
+ "(uid " + uid +")...");
break;
case PowerManager.GO_TO_SLEEP_REASON_TIMEOUT:
Slog.i(TAG, "Going to sleep due to screen timeout (uid " + uid +")...");
break;
case PowerManager.GO_TO_SLEEP_REASON_LID_SWITCH:
Slog.i(TAG, "Going to sleep due to lid switch (uid " + uid +")...");
break;
case PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON:
Slog.i(TAG, "Going to sleep due to power button (uid " + uid +")...");
break;
case PowerManager.GO_TO_SLEEP_REASON_SLEEP_BUTTON:
Slog.i(TAG, "Going to sleep due to sleep button (uid " + uid +")...");
break;
case PowerManager.GO_TO_SLEEP_REASON_HDMI:
Slog.i(TAG, "Going to sleep due to HDMI standby (uid " + uid +")...");
break;
default:
Slog.i(TAG, "Going to sleep by application request (uid " + uid +")...");
reason = PowerManager.GO_TO_SLEEP_REASON_APPLICATION;
break;
}
//标记最后一次灭屏时间
mLastSleepTime = eventTime;
//用于判定是否进入屏保
mSandmanSummoned = true;
//设置 wakefulness 值为 WAKEFULNESS_DOZING,
//因此先进 Doze 状态,详见【1.5.1】
setWakefulnessLocked(WAKEFULNESS_DOZING, reason);
// Report the number of wake locks that will be cleared by going to sleep.
//灭屏时,将清楚以下三种使得屏幕保持亮屏的 wakelock 锁,numWakeLocksCleared 统计下个数
int numWakeLocksCleared = 0;
final int numWakeLocks = mWakeLocks.size();
for (int i = 0; i < numWakeLocks; i++) {
final WakeLock wakeLock = mWakeLocks.get(i);
switch (wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) {
case PowerManager.FULL_WAKE_LOCK:
case PowerManager.SCREEN_BRIGHT_WAKE_LOCK:
case PowerManager.SCREEN_DIM_WAKE_LOCK:
numWakeLocksCleared += 1;
break;
}
}
// Skip dozing if requested.
//如果带有 PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE 的 flag,
//则直接进入 Sleep 状态,不再进入 Doze 状态
if ((flags & PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE) != 0) {
//该方法才会真正地进入睡眠,详见【1.5.2】
reallyGoToSleepNoUpdateLocked(eventTime, uid);
}
} finally {
Trace.traceEnd(Trace.TRACE_TAG_POWER);
}
return true;
}
在这个方法中:
首先,是判断调用该方法的原因并打印 log,该 log 在日常分析问题时非常有用;
然后,通过 setWakefulnessLocked()
将当前 wakefulness 设置为 Doze 状态;
最后,通过 flag 判断,如果 flag 为 1,则调用 reallyGoToSleepNoUpdateLocked()
方法直接进入 Sleep 状态。
因此,系统其他模块在调用 PM.goToSleep() 灭屏时,在除指定 flag 为 PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE
的情况外,都会首先进入 Doze,再由 Doze 进入 Sleep。
setWakefulnessLocked()
方法用来设置 wakefulness 值,同时将会调用 Notifier中wakefulness 相关的逻辑,这部分在之前的流程分析中也分析过,这里再来看下:
//frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
private void setWakefulnessLocked(int wakefulness, int reason) {
if (mWakefulness != wakefulness) {
//设置 mWakefulness
mWakefulness = wakefulness;
mWakefulnessChanging = true;
mDirty |= DIRTY_WAKEFULNESS;
//调用 Notifier 中的方法,做 wakefulness 改变开始时的工作
mNotifier.onWakefulnessChangeStarted(wakefulness, reason);
}
}
我们跟着执行流程来进行分析,Notifier 是 PMS 模块中用于进行“通知”的一个组件类。比如:发送亮灭屏广播就是它来负责,具体详细的分析请【】查看。这里针对于灭屏场景,再来看下其中的逻辑:
//frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
public void onWakefulnessChangeStarted(final int wakefulness, int reason) {
//由于 wakefulness 为 Doze,故 interactive 为 false
final boolean interactive = PowerManagerInternal.isInteractive(wakefulness);
//...
// Handle any early interactive state changes.
// Finish pending incomplete ones from a previous cycle.
if (mInteractive != interactive) {
// Finish up late behaviors if needed.
if (mInteractiveChanging) {
handleLateInteractiveChange();
}
// Start input as soon as we start waking up or going to sleep.
mInputManagerInternal.setInteractive(interactive);
mInputMethodManagerInternal.setInteractive(interactive);
// Notify battery stats.
try {
mBatteryStats.noteInteractive(interactive);
} catch (RemoteException ex) { }
// Handle early behaviors.
mInteractive = interactive;
mInteractiveChangeReason = reason;
mInteractiveChanging = true;
handleEarlyInteractiveChange();
}
}
在这个方法中,首先根据 wakefulness 值判断了系统当前的交互状态,如果是处于 Awake 状态和 Dream 状态,则表示可交互;如果处于 Doze 和 Asleep 状态,则表示不可交互;由于在 setWakefulnessLocked() 中已经设置了 wakefulness 为 DOZE 状态,因此此时处于不可交互状态,接下来开始执行 handleEarlyInteractiveChange() 方法:
//frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
private void handleEarlyInteractiveChange() {
synchronized (mLock) {
if (mInteractive) {//此时为 false
// Waking up...
mHandler.post(new Runnable() {
@Override
public void run() {
// Note a SCREEN tron event is logged in PowerManagerService.
mPolicy.startedWakingUp();
}
});
// Send interactive broadcast.
mPendingInteractiveState = INTERACTIVE_STATE_AWAKE;
mPendingWakeUpBroadcast = true;
updatePendingBroadcastLocked();
} else {
// Going to sleep...
// Tell the policy that we started going to sleep.
final int why = translateOffReason(mInteractiveChangeReason);
mHandler.post(new Runnable() {
@Override
public void run() {//通过 PhoneWindowManager 设置锁屏
mPolicy.startedGoingToSleep(why);
}
});
}
}
}
在这个方法中,将调用 mPolicy.startedGoingToSleep(why) 进行锁屏流程。
回到 PMS 中,在处理完 setWakefulnessLocked()
方法后,由于没有 PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE
,所以不会立即执行 reallyGoToSleepNoUpdateLocked()
方法,此时 goToSleepNoUpdateLocked()
方法完毕并返回 true。
之后开始执行 updatePowerStateLocked()
方法了,这个方法对于熟悉 PMS 模块的人来说再熟悉不过了,它是整个 PMS 的核心,详细的分析请点击这里,在这里我们只看其灭屏时的一些处理。
//frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
private void updatePowerStateLocked() {
//...
try {
//...
// Phase 2: Update display power state.
//更新屏幕状态
boolean displayBecameReady = updateDisplayPowerStateLocked(dirtyPhase2);
// Phase 3: Update dream state (depends on display ready signal).
//更新屏保信息
updateDreamLocked(dirtyPhase2, displayBecameReady);
// Phase 4: Send notifications, if needed.
// 收尾
finishWakefulnessChangeIfNeededLocked();
// Phase 5: Update suspend blocker.
// Because we might release the last suspend blocker here, we need to make sure
// we finished everything else first!
//释放锁
updateSuspendBlockerLocked();
} finally {
Trace.traceEnd(Trace.TRACE_TAG_POWER);
}
}
updateDisplayPowerStateLocked()
将会向 DisplayPowerController 请求新的屏幕状态,完成屏幕的更新;- updateDreamLocked() 方法用来更新屏保信息,除此之外还有一个任务——调用 reallyGoToSleep() 方法进入休眠,即由 DOZE 状态进入 Sleep 状态。
finishWakefulnessChangeIfNeededLocked()
方法用来做最后的收尾工作,当然,在这里会调用到 Notifier 中进行收尾。updateSuspendBlockerLocked()
方法将用来更新 SuspendBlocker 锁,会根据当前的 WakeLock 类型以及屏幕状态来决定是否需要申请 SuspendBlocker 锁。
在 updateDreamLocked()
中更新屏保状态时,如果此时处于Doze状态且屏保没有进行,则将调用 reallyGoToSleepNoUpdateLocked()
方法,将 wakefulness 值设置为了 Sleep,部分代码如下:
//frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
private void handleSandman() { // runs on handler thread
//...
synchronized (mLock) {
//...
//决定是否继续 Dream
if (wakefulness == WAKEFULNESS_DREAMING) {
//...
} else if (wakefulness == WAKEFULNESS_DOZING) {
if (isDreaming) {
return; // continue dozing
}
//进入 asleep 状态
reallyGoToSleepNoUpdateLocked(SystemClock.uptimeMillis(), Process.SYSTEM_UID);
updatePowerStateLocked();
}
}
//...
}
//frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
// Done dozing, drop everything and go to sleep.
private boolean reallyGoToSleepNoUpdateLocked(long eventTime, int uid) {
if (eventTime < mLastWakeTime || mWakefulness == WAKEFULNESS_ASLEEP
|| !mBootCompleted || !mSystemReady) {
return false;
}
try {
//设置为 ASLEEP 状态
setWakefulnessLocked(WAKEFULNESS_ASLEEP, PowerManager.GO_TO_SLEEP_REASON_TIMEOUT);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_POWER);
}
return true;
}
到这里 Power 按键灭屏过程已经分析完了,我们来看下整体流程。
经过上面的分析,我们知道了 Power 键灭屏由 PhoneWindowManager 发起了 goToSleep
,现在来看看超时灭屏是如何实现的。
时灭屏主要有两个影响因素:休眠时间和用户活动。休眠时间在 Settings 中进行设置,用户活动是指当手机处于亮屏状态,都会调用 userActivityNoUpdateLocked()
方法去更新用户活动时间。接下来我们就从 userActivityNoUpdateLocked()
方法开始分析其超时灭屏的流程。
//frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
private boolean userActivityNoUpdateLocked(long eventTime, int event, int flags, int uid) {
if (eventTime < mLastSleepTime || eventTime < mLastWakeTime
|| !mBootCompleted || !mSystemReady) {
return false;
}
try {
if (eventTime > mLastInteractivePowerHintTime) {
powerHintInternal(PowerHint.INTERACTION, 0);
mLastInteractivePowerHintTime = eventTime;
}
mNotifier.onUserActivity(event, uid);
if (mUserInactiveOverrideFromWindowManager) {
mUserInactiveOverrideFromWindowManager = false;
mOverriddenTimeout = -1;
}
//如果 wakefulness 为 Asleep 或 Doze,不再计算超时时间,直接返回
if (mWakefulness == WAKEFULNESS_ASLEEP
|| mWakefulness == WAKEFULNESS_DOZING
|| (flags & PowerManager.USER_ACTIVITY_FLAG_INDIRECT) != 0) {
return false;
}
//如果带有该 flag,则会小亮一会儿再灭屏
if ((flags & PowerManager.USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS) != 0) {
if (eventTime > mLastUserActivityTimeNoChangeLights
&& eventTime > mLastUserActivityTime) {
mLastUserActivityTimeNoChangeLights = eventTime;
mDirty |= DIRTY_USER_ACTIVITY;
if (event == PowerManager.USER_ACTIVITY_EVENT_BUTTON) {
mDirty |= DIRTY_QUIESCENT;
}
return true;
}
} else {
if (eventTime > mLastUserActivityTime) {
//将当前时间赋值给 mLastUserActivityTime
mLastUserActivityTime = eventTime;
mDirty |= DIRTY_USER_ACTIVITY;
if (event == PowerManager.USER_ACTIVITY_EVENT_BUTTON) {
mDirty |= DIRTY_QUIESCENT;
}
return true;
}
}
} finally {
Trace.traceEnd(Trace.TRACE_TAG_POWER);
}
return false;
}
在这个方法中,如果传入的参数 flag 为 PowerManager.USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS
,则将事件时间赋值给 mLastUserActivityTimeNoChangeLights,否则将事件时间赋值给 mLastUserActivityTime。这个 flag 标志用于延长亮屏或 Dim 的时长一小会儿。
当这个方法执行之后,就得到了 mLastUserActivityTime 或者 mLastUserActivityTimeNoChangeLights 的值,然后经过一些调用后,又会进入 updatePowerStateLocked()
方法中。在这个方法中,与超时灭屏直接相关的就是 for 循环部分:
//frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
private void updatePowerStateLocked() {
//...
try {
//...
for (;;) {
int dirtyPhase1 = mDirty;
dirtyPhase2 |= dirtyPhase1;
mDirty = 0;
updateWakeLockSummaryLocked(dirtyPhase1);
updateUserActivitySummaryLocked(now, dirtyPhase1);//详见【2.2.1】
if (!updateWakefulnessLocked(dirtyPhase1)) {//详见【2.3】
break;
}
}
//...
} finally {
Trace.traceEnd(Trace.TRACE_TAG_POWER);
}
}
其中 updateWakeLockSummaryLocked()
用来统计WakeLock,这里就不分析该方法了,详细的分析请【】。
现在从 updateUserActivitySummaryLocked()
方法开始分析,该方法如下:
//frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
private void updateUserActivitySummaryLocked(long now, int dirty) {
// Update the status of the user activity timeout timer.
if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_USER_ACTIVITY
| DIRTY_WAKEFULNESS | DIRTY_SETTINGS)) != 0) {
mHandler.removeMessages(MSG_USER_ACTIVITY_TIMEOUT);
long nextTimeout = 0;
if (mWakefulness == WAKEFULNESS_AWAKE
|| mWakefulness == WAKEFULNESS_DREAMING
|| mWakefulness == WAKEFULNESS_DOZING) {
//获取睡眠时长,为 Settings.Secure.SLEEP_TIMEOUT 的值和最小休眠时间的最大值,
//Settings.Secure.SLEEP_TIMEOUT 一般为 -1,表示禁用,因此该值默认为 -1
final int sleepTimeout = getSleepTimeoutLocked();
//获取休眠时长,在 Settings 中设置的值
final int screenOffTimeout = getScreenOffTimeoutLocked(sleepTimeout);
//获取 Dim 时长,由休眠时长剩 Dim 百分比得到
final int screenDimDuration = getScreenDimDurationLocked(screenOffTimeout);
//用户活动是否由 Window 覆盖
final boolean userInactiveOverride = mUserInactiveOverrideFromWindowManager;
//该值用来统计用户活动状态,每次进入该方法,置为 0
mUserActivitySummary = 0;
//上次用户活动时间 >= 上次唤醒时间
if (mLastUserActivityTime >= mLastWakeTime) {
//下次超时时间为上次用户活动时间 + 休眠时间 - Dim 时间
//到达这个时间后,将进入 Dim 状态
nextTimeout = mLastUserActivityTime
+ screenOffTimeout - screenDimDuration;
//如果当前时间 < nextTimeout,则此时处于亮屏状态,
//标记 mUserActivitySummary 为 USER_ACTIVITY_SCREEN_BRIGHT
if (now < nextTimeout) {
mUserActivitySummary = USER_ACTIVITY_SCREEN_BRIGHT;
} else {
//如果当前时间 > nextTimeout,此时有两种情况,要么进入 Dim 要么进入 Sleep
//将上次用户活动时间 + 灭屏时间赋值给 nextTimeout,
//如果该值大于当前时间,则说明此时应该处于 Dim 状态
//因此将标记 mUserActivitySummary 为 USER_ACTIVITY_SCREEN_DIM
nextTimeout = mLastUserActivityTime + screenOffTimeout;
if (now < nextTimeout) {
mUserActivitySummary = USER_ACTIVITY_SCREEN_DIM;
}
}
}
//判断和 USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS 标记相关,如果带有此标记,才会进入该 if
if (mUserActivitySummary == 0
&& mLastUserActivityTimeNoChangeLights >= mLastWakeTime) {
//下次超时时间 = 上次用户活动时间 + 灭屏时间
nextTimeout = mLastUserActivityTimeNoChangeLights + screenOffTimeout;
//根据当前时间和 nextTimeout 设置 mUserActivitySummary
if (now < nextTimeout) {
if (mDisplayPowerRequest.policy == DisplayPowerRequest.POLICY_BRIGHT
|| mDisplayPowerRequest.policy == DisplayPowerRequest.POLICY_VR) {
mUserActivitySummary = USER_ACTIVITY_SCREEN_BRIGHT;
} else if (mDisplayPowerRequest.policy == DisplayPowerRequest.POLICY_DIM) {
mUserActivitySummary = USER_ACTIVITY_SCREEN_DIM;
}
}
}
//不满足以上条件时,此时 mUserActivitySummary 为 0,
//这种情况应该为当 mUserActivitySummary 经历了 USER_ACTIVITY_SCREEN_BRIGHT
//和 USER_ACTIVITY_SCREEN_DIM 之后才会执行到这里
if (mUserActivitySummary == 0) {
if (sleepTimeout >= 0) {
//获取上次用户活动时间的最后一次时间
final long anyUserActivity = Math.max(mLastUserActivityTime,
mLastUserActivityTimeNoChangeLights);
if (anyUserActivity >= mLastWakeTime) {
nextTimeout = anyUserActivity + sleepTimeout;
//将 mUserActivitySummary 值置为 USER_ACTIVITY_SCREEN_DREAM,表示屏保
if (now < nextTimeout) {
mUserActivitySummary = USER_ACTIVITY_SCREEN_DREAM;
}
}
} else {
//将 mUserActivitySummary 值置为 USER_ACTIVITY_SCREEN_DREAM,表示屏保
mUserActivitySummary = USER_ACTIVITY_SCREEN_DREAM;
nextTimeout = -1;
}
}
if (mUserActivitySummary != USER_ACTIVITY_SCREEN_DREAM && userInactiveOverride) {
if ((mUserActivitySummary &
(USER_ACTIVITY_SCREEN_BRIGHT | USER_ACTIVITY_SCREEN_DIM)) != 0) {
// Device is being kept awake by recent user activity
if (nextTimeout >= now && mOverriddenTimeout == -1) {
// Save when the next timeout would have occurred
mOverriddenTimeout = nextTimeout;
}
}
mUserActivitySummary = USER_ACTIVITY_SCREEN_DREAM;
nextTimeout = -1;
}
if (mUserActivitySummary != 0 && nextTimeout >= 0) {
//发送一个异步 Handler 定时消息
Message msg = mHandler.obtainMessage(MSG_USER_ACTIVITY_TIMEOUT);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, nextTimeout);
}
} else {//当 wakefulness = Sleep 的时候,直接将 mUserActivitySummary 置为 0
mUserActivitySummary = 0;
}
}
}
Handler 的调用处理逻辑如下:
//frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_USER_ACTIVITY_TIMEOUT:
handleUserActivityTimeout();
break;
}
}
private void handleUserActivityTimeout() { // runs on handler thread
synchronized (mLock) {
mDirty |= DIRTY_USER_ACTIVITY;
updatePowerStateLocked();
}
}
当执行到这个方法后,现在就统计得到了 mWakeLockSummary
和 mUserActivitySummary
的值,现在我们看下一个方法 updateWakefulnessLocked()
。
//frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
private boolean updateWakefulnessLocked(int dirty) {
boolean changed = false;
if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_USER_ACTIVITY | DIRTY_BOOT_COMPLETED
| DIRTY_WAKEFULNESS | DIRTY_STAY_ON | DIRTY_PROXIMITY_POSITIVE
| DIRTY_DOCK_STATE)) != 0) {
//isItBedTimeYetLocked() 判断是否需要"睡觉"了,详见【2.3.1】
if (mWakefulness == WAKEFULNESS_AWAKE && isItBedTimeYetLocked()) {
final long time = SystemClock.uptimeMillis();
if (shouldNapAtBedTimeLocked()) {//进入屏保,详见【2.3.2】
changed = napNoUpdateLocked(time, Process.SYSTEM_UID);
} else {//开始休眠,详见【2.3.3】
changed = goToSleepNoUpdateLocked(time,
PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, 0, Process.SYSTEM_UID);
}
}
}
return changed;
}
这个方法中可以看到,首先根据 isItBedTimeYetLocked()
和 mWakefulness 来决定是否执行,然后根据shouldNapAtBedTimeLocked()
决定进入屏保还是休眠。
该方法如果返回值为 true,则说明此时屏幕状态发生改变(在 goToSleepNoUpdateLocked()
和 napNoUpdateLocked()
中会分别设置 mWakefulness 为 DREAM 和 ASLEEP),因此将不会跳出 for 循环,再次进行一次循环。这就是为何会设置一个死循环的目的,同时也说明只有超时灭屏才会循环两次,其他情况下都会只执行一次 for 循环就退出。
回到该方法中,我们继续看看 isItBedTimeYetLocked():
//frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
private boolean isItBedTimeYetLocked() {
return mBootCompleted && !isBeingKeptAwakeLocked();
}
private boolean isBeingKeptAwakeLocked() {
return mStayOn//是否需要保持常亮
|| mProximityPositive//PSensor 是否靠近
|| (mWakeLockSummary & WAKE_LOCK_STAY_AWAKE) != 0//当前是否有 Wakelock 类型为屏幕相关的锁
|| (mUserActivitySummary & (USER_ACTIVITY_SCREEN_BRIGHT
| USER_ACTIVITY_SCREEN_DIM)) != 0//当前用户活动状态是否为 Dream 或者 0
|| mScreenBrightnessBoostInProgress;//是否处于亮度增强过程中
}
以上代码可以看出,如果有任意一个条件为 true,那么就不能进入休眠或者屏保状态,因此只有全部为 false 时,才会返回 false,从而说明需要“睡觉”了,接着会进入 if 条件中。
如果 shouldNapAtBedTimeLocked()
返回 true,则开始屏保,否则直接休眠。
//frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
private boolean shouldNapAtBedTimeLocked() {
return mDreamsActivateOnSleepSetting
|| (mDreamsActivateOnDockSetting
&& mDockState != Intent.EXTRA_DOCK_STATE_UNDOCKED);
}
当开始休眠时,直接调用了 goToSleepNoUpdateLocked()
方法中了,于是开始走休眠流程,之后的逻辑和 Power 键灭屏一样了。
//frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
@SuppressWarnings("deprecation")
private boolean goToSleepNoUpdateLocked(long eventTime, int reason, int flags, int uid) {
if (DEBUG_SPEW) {
Slog.d(TAG, "goToSleepNoUpdateLocked: eventTime=" + eventTime
+ ", reason=" + reason + ", flags=" + flags + ", uid=" + uid);
}
if (eventTime < mLastWakeTime
|| mWakefulness == WAKEFULNESS_ASLEEP
|| mWakefulness == WAKEFULNESS_DOZING
|| !mBootCompleted || !mSystemReady) {
return false;
}
Trace.traceBegin(Trace.TRACE_TAG_POWER, "goToSleep");
try {
switch (reason) {
case PowerManager.GO_TO_SLEEP_REASON_DEVICE_ADMIN:
Slog.i(TAG, "Going to sleep due to device administration policy "
+ "(uid " + uid +")...");
break;
case PowerManager.GO_TO_SLEEP_REASON_TIMEOUT:
Slog.i(TAG, "Going to sleep due to screen timeout (uid " + uid +")...");
break;
case PowerManager.GO_TO_SLEEP_REASON_LID_SWITCH:
Slog.i(TAG, "Going to sleep due to lid switch (uid " + uid +")...");
break;
case PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON:
Slog.i(TAG, "Going to sleep due to power button (uid " + uid +")...");
break;
case PowerManager.GO_TO_SLEEP_REASON_SLEEP_BUTTON:
Slog.i(TAG, "Going to sleep due to sleep button (uid " + uid +")...");
break;
case PowerManager.GO_TO_SLEEP_REASON_HDMI:
Slog.i(TAG, "Going to sleep due to HDMI standby (uid " + uid +")...");
break;
default:
Slog.i(TAG, "Going to sleep by application request (uid " + uid +")...");
reason = PowerManager.GO_TO_SLEEP_REASON_APPLICATION;
break;
}
mLastSleepTime = eventTime;
mSandmanSummoned = true;
setWakefulnessLocked(WAKEFULNESS_DOZING, reason);
// Report the number of wake locks that will be cleared by going to sleep.
int numWakeLocksCleared = 0;
final int numWakeLocks = mWakeLocks.size();
for (int i = 0; i < numWakeLocks; i++) {
final WakeLock wakeLock = mWakeLocks.get(i);
switch (wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) {
case PowerManager.FULL_WAKE_LOCK:
case PowerManager.SCREEN_BRIGHT_WAKE_LOCK:
case PowerManager.SCREEN_DIM_WAKE_LOCK:
numWakeLocksCleared += 1;
break;
}
}
EventLog.writeEvent(EventLogTags.POWER_SLEEP_REQUESTED, numWakeLocksCleared);
// Skip dozing if requested.
if ((flags & PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE) != 0) {
reallyGoToSleepNoUpdateLocked(eventTime, uid);
}
} finally {
Trace.traceEnd(Trace.TRACE_TAG_POWER);
}
return true;
}
整个超时灭屏的流程分析就到这里了,从以上流程中可以看到,mWakeLockSummary 和 mUserActivitySummary 的作用相当重要。到这里超时灭屏过程已经分析完了,我们来看下整体流程。
欢迎你「扫一扫」下面的二维码,关注我的公众号,可以接受最新的文章推送,有丰厚的抽奖活动和福利等着你哦!😍
如果你有什么疑问或者问题,可以 点击这里 提交 issue,也可以发邮件给我 jeanboy@foxmail.com。