本文基于Android S源码梳理
系列回顾
学习目标
通过AOSP源码,对超时灭屏流程进行梳理
简介
日常使用手机过程中,如果不去可以设置屏幕常亮,手机将会一定时间内,无任何使用状态下进入灭屏。这个过程就叫超时灭屏。
这里如果没有了解过此流程的同学,不妨去试想一下,如果让你设计一个超时灭屏的功能,你将如何设计?
通过上面提到的需求,首先肯定需要在PMS中设计一个超时机制,这个可以通过Handler进行实现,timeout将会是读取SettingsProvider中的值。而如何判断一段时间内无任何使用操作呢?这个可以通过input模块进行监听判断。那么接下来我们就来看下源码中的设计是否与此类似?
时序图
流程解析
活动监听
这里先交代一个背景,在用户对手机进行操作后,InputDispatcher会通过pokeUserActivity告知NativeInputManagerService:
frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
void NativeInputManager::pokeUserActivity(nsecs_t eventTime, int32_t eventType, int32_t displayId) {
ATRACE_CALL();
android_server_PowerManagerService_userActivity(eventTime, eventType, displayId);
}
然后调用NativePowerMangaerService userActivity:
frameworks/base/services/core/jni/com_android_server_power_PowerManagerService.cpp
void android_server_PowerManagerService_userActivity(nsecs_t eventTime, int32_t eventType,
int32_t displayId) {
if (gPowerManagerServiceObj) {
// Throttle calls into user activity by event type.
// We're a little conservative about argument checking here in case the caller
// passes in bad data which could corrupt system state.
if (eventType >= 0 && eventType <= USER_ACTIVITY_EVENT_LAST) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
if (eventTime > now) {
eventTime = now;
}
if (gLastEventTime[eventType] + MIN_TIME_BETWEEN_USERACTIVITIES > eventTime) {
return;
}
gLastEventTime[eventType] = eventTime;
// Tell the power HAL when user activity occurs.
setPowerBoost(Boost::INTERACTION, 0);
}
JNIEnv* env = AndroidRuntime::getJNIEnv();
env->CallVoidMethod(gPowerManagerServiceObj,
gPowerManagerServiceClassInfo.userActivityFromNative,
nanoseconds_to_milliseconds(eventTime), eventType, displayId, 0);
checkAndClearExceptionFromCallback(env, "userActivityFromNative");
}
}
可以看到最后通过JNI调用到PowerManagerService的userActivityFromNative方法:
frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
// Called from native code.
private void userActivityFromNative(long eventTime, int event, int displayId, int flags) {
userActivityInternal(displayId, eventTime, event, flags, Process.SYSTEM_UID);
}
private void userActivityInternal(int displayId, long eventTime, int event, int flags,
int uid) {
synchronized (mLock) {
...
// 更新用户活动时间
if (userActivityNoUpdateLocked(groupId, eventTime, event, flags, uid)) {
// 更新power状态
updatePowerStateLocked();
}
}
}
在userActivityNoUpdateLocked中,主要就是记录当前用户的最后一次活动时间:
frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
private boolean userActivityNoUpdateLocked(int groupId, long eventTime, int event, int flags,
int uid) {
if (DEBUG_SPEW) {
Slog.d(TAG, "userActivityNoUpdateLocked: groupId=" + groupId
+ ", eventTime=" + eventTime + ", event=" + event
+ ", flags=0x" + Integer.toHexString(flags) + ", uid=" + uid);
}
// 判断eventtime是否合法
if (eventTime < mLastSleepTime || eventTime < mLastWakeTime || !mSystemReady) {
return false;
}
Trace.traceBegin(Trace.TRACE_TAG_POWER, "userActivity");
try {
...
// 通知用户活动时间
mNotifier.onUserActivity(event, uid);
mAttentionDetector.onUserActivity(eventTime, event);
// 根据WindowManger调用,告知Power已经不在活动,重置timeout时间
if (mUserInactiveOverrideFromWindowManager) {
mUserInactiveOverrideFromWindowManager = false;
mOverriddenTimeout = -1;
}
final int wakefulness = mDisplayGroupPowerStateMapper.getWakefulnessLocked(groupId);
// 如果屏幕已经是灭屏或者Doze状态等,直接返回
if (wakefulness == WAKEFULNESS_ASLEEP
|| wakefulness == WAKEFULNESS_DOZING
|| (flags & PowerManager.USER_ACTIVITY_FLAG_INDIRECT) != 0) {
return false;
}
// 根据userid记录PowerProfile中lastUserActivityTime
maybeUpdateForegroundProfileLastActivityLocked(eventTime);
// 根据USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS flag来判断是否延迟灭屏
if ((flags & PowerManager.USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS) != 0) {
if (eventTime
> mDisplayGroupPowerStateMapper.getLastUserActivityTimeNoChangeLightsLocked(
groupId)
&& eventTime > mDisplayGroupPowerStateMapper.getLastUserActivityTimeLocked(
groupId)) {
//如果包含此flag,则将值赋给lastUserActivityTimeNoChangeLights
mDisplayGroupPowerStateMapper.setLastUserActivityTimeNoChangeLightsLocked(
groupId, eventTime);
mDirty |= DIRTY_USER_ACTIVITY;
if (event == PowerManager.USER_ACTIVITY_EVENT_BUTTON) {
mDirty |= DIRTY_QUIESCENT;
}
return true;
}
} else {
if (eventTime > mDisplayGroupPowerStateMapper.getLastUserActivityTimeLocked(
groupId)) {
//反之则赋值给lastUserActivityTime
mDisplayGroupPowerStateMapper.setLastUserActivityTimeLocked(groupId, 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,使用后,会再次延长亮屏或者dim(半亮状态)时间。
Timeout计算
接下来就进入更新Power状态的业务,这里面就包含了灭屏相关业务逻辑。
frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
private void updatePowerStateLocked() {
...
try {
...
// Phase 1: Update wakefulness.
// Loop because the wake lock and user activity computations are influenced
// by changes in wakefulness.
final long now = mClock.uptimeMillis();
int dirtyPhase2 = 0;
for (;;) {
int dirtyPhase1 = mDirty;
dirtyPhase2 |= dirtyPhase1;
mDirty = 0;
// 计算当前系统中的wakeLock数量
updateWakeLockSummaryLocked(dirtyPhase1);
// 计算用户活动时间,并计算灭屏时间
updateUserActivitySummaryLocked(now, dirtyPhase1);
...
}
接下来就重点走读一下计算超时灭屏时间的业务:
frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
private void updateUserActivitySummaryLocked(long now, int dirty) {
...
// 移除MSG_USER_ACTIVITY_TIMEOUT,说明用户依然是在活动的
mHandler.removeMessages(MSG_USER_ACTIVITY_TIMEOUT);
// 彻底灭屏的时间,即使当时设备持有wakeLock,默认情况是-1
final long attentiveTimeout = getAttentiveTimeoutLocked();
final long sleepTimeout = getSleepTimeoutLocked(attentiveTimeout);
// 超时灭屏的时间,取决于设置中的配置
long screenOffTimeout = getScreenOffTimeoutLocked(sleepTimeout,
attentiveTimeout);
// Dim进入的时间,随screenOffTimeout而变化
final long screenDimDuration = getScreenDimDurationLocked(screenOffTimeout);
// 手机面朝下,将会大大减少timeout时间
screenOffTimeout =
getScreenOffTimeoutWithFaceDownLocked(screenOffTimeout, screenDimDuration);
final boolean userInactiveOverride = mUserInactiveOverrideFromWindowManager;
long nextTimeout = -1;
boolean hasUserActivitySummary = false;
for (int groupId : mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked()) {
// 用于记录当前用户活动状态
int groupUserActivitySummary = 0;
long groupNextTimeout = 0;
// 当前WAKEFULNESS必须是非灭屏状态
if (mDisplayGroupPowerStateMapper.getWakefulnessLocked(groupId) != WAKEFULNESS_ASLEEP) {
final long lastUserActivityTime =
mDisplayGroupPowerStateMapper.getLastUserActivityTimeLocked(groupId);
final long lastUserActivityTimeNoChangeLights =
mDisplayGroupPowerStateMapper.getLastUserActivityTimeNoChangeLightsLocked(
groupId);
if (lastUserActivityTime >= mLastWakeTime) {
groupNextTimeout = lastUserActivityTime + screenOffTimeout - screenDimDuration;
// 根据groupNextTimeout判断当前是亮屏状态还是DIM状态
if (now < groupNextTimeout) {
groupUserActivitySummary = USER_ACTIVITY_SCREEN_BRIGHT;
} else {
groupNextTimeout = lastUserActivityTime + screenOffTimeout;
if (now < groupNextTimeout) {
groupUserActivitySummary = USER_ACTIVITY_SCREEN_DIM;
}
}
}
// 对应之前userActivityNoUpdateLocked中,如果存在flag PowerManager.USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS,则在原来的延迟上继续增加一些时长
if (groupUserActivitySummary == 0
&& lastUserActivityTimeNoChangeLights >= mLastWakeTime) {
groupNextTimeout = lastUserActivityTimeNoChangeLights + screenOffTimeout;
if (now < groupNextTimeout) {
final DisplayPowerRequest displayPowerRequest =
mDisplayGroupPowerStateMapper.getPowerRequestLocked(groupId);
if (displayPowerRequest.policy == DisplayPowerRequest.POLICY_BRIGHT
|| displayPowerRequest.policy == DisplayPowerRequest.POLICY_VR) {
groupUserActivitySummary = USER_ACTIVITY_SCREEN_BRIGHT;
} else if (displayPowerRequest.policy == DisplayPowerRequest.POLICY_DIM) {
groupUserActivitySummary = USER_ACTIVITY_SCREEN_DIM;
}
}
}
// 此判断表明,当前的时间大于上一次活动时间+自动休眠时间,需要自动灭屏,直接进入Dream状态。
if (groupUserActivitySummary == 0) {
if (sleepTimeout >= 0) {
final long anyUserActivity = Math.max(lastUserActivityTime,
lastUserActivityTimeNoChangeLights);
if (anyUserActivity >= mLastWakeTime) {
groupNextTimeout = anyUserActivity + sleepTimeout;
if (now < groupNextTimeout) {
groupUserActivitySummary = USER_ACTIVITY_SCREEN_DREAM;
}
}
} else {
groupUserActivitySummary = USER_ACTIVITY_SCREEN_DREAM;
groupNextTimeout = -1;
}
}
// 如果收到WMS的通知,用户已经非活动状态,并且是亮屏或者DIM状态,那么直接灭屏
if (groupUserActivitySummary != USER_ACTIVITY_SCREEN_DREAM
&& userInactiveOverride) {
if ((groupUserActivitySummary &
(USER_ACTIVITY_SCREEN_BRIGHT | USER_ACTIVITY_SCREEN_DIM)) != 0) {
// Device is being kept awake by recent user activity
if (mOverriddenTimeout == -1) {
// Save when the next timeout would have occurred
mOverriddenTimeout = groupNextTimeout;
}
}
groupUserActivitySummary = USER_ACTIVITY_SCREEN_DREAM;
groupNextTimeout = -1;
}
// 通知AttentionDetector更新用户活动时间
if ((groupUserActivitySummary & USER_ACTIVITY_SCREEN_BRIGHT) != 0
&& (mDisplayGroupPowerStateMapper.getWakeLockSummaryLocked(groupId)
& WAKE_LOCK_STAY_AWAKE) == 0) {
groupNextTimeout = mAttentionDetector.updateUserActivity(groupNextTimeout,
screenDimDuration);
}
hasUserActivitySummary |= groupUserActivitySummary != 0;
if (nextTimeout == -1) {
nextTimeout = groupNextTimeout;
} else if (groupNextTimeout != -1) {
nextTimeout = Math.min(nextTimeout, groupNextTimeout);
}
}
mDisplayGroupPowerStateMapper.setUserActivitySummaryLocked(groupId,
groupUserActivitySummary);
if (DEBUG_SPEW) {
Slog.d(TAG, "updateUserActivitySummaryLocked: groupId=" + groupId
+ ", mWakefulness=" + wakefulnessToString(
mDisplayGroupPowerStateMapper.getWakefulnessLocked(groupId))
+ ", mUserActivitySummary=0x" + Integer.toHexString(
groupUserActivitySummary)
+ ", nextTimeout=" + TimeUtils.formatUptime(groupNextTimeout));
}
}
final long nextProfileTimeout = getNextProfileTimeoutLocked(now);
if (nextProfileTimeout > 0) {
nextTimeout = Math.min(nextTimeout, nextProfileTimeout);
}
//发送延迟消息MSG_USER_ACTIVITY_TIMEOUT
if (hasUserActivitySummary && nextTimeout >= 0) {
scheduleUserInactivityTimeout(nextTimeout);
}
}
具体的业务已经在代码中备注了,最后根据nextTimeout以及hasUserActivitySummary标志位发送了延迟消息MSG_USER_ACTIVITY_TIMEOUT。
hasUserActivitySummary标志位的变化取决groupUserActivitySummary是否为0,正常灭屏过程,groupUserActivitySummary会经历USER_ACTIVITY_SCREEN_BRIGHT(亮屏) → USER_ACTIVITY_SCREEN_DIM(DIM) → USER_ACTIVITY_SCREEN_DREAM(灭屏)变化,最终进入ASLEEP后,groupUserActivitySummary就会赋值为0。
在此MSG中,再次调用了updatePowerStateLocked:
frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
private void handleUserActivityTimeout() { // runs on handler thread
synchronized (mLock) {
if (DEBUG_SPEW) {
Slog.d(TAG, "handleUserActivityTimeout");
}
mDirty |= DIRTY_USER_ACTIVITY;
updatePowerStateLocked();
}
}
灭屏决策
在updatePowerStateLocked中执行完updateUserActivitySummaryLocked,接下来就是判断是否需要跳出死循环的updateWakefulnessLocked,这里面就藏有判断是否灭屏的业务逻辑:
frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
private void updatePowerStateLocked() {
...
try {
...
// Phase 1: Update wakefulness.
// Loop because the wake lock and user activity computations are influenced
// by changes in wakefulness.
final long now = mClock.uptimeMillis();
int dirtyPhase2 = 0;
for (;;) {
int dirtyPhase1 = mDirty;
dirtyPhase2 |= dirtyPhase1;
mDirty = 0;
// 计算当前系统中的wakeLock数量
updateWakeLockSummaryLocked(dirtyPhase1);
// 计算用户活动时间,并计算灭屏时间
updateUserActivitySummaryLocked(now, dirtyPhase1);
...
if (!updateWakefulnessLocked(dirtyPhase1)) {
break;
}
...
}
}
}
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 | DIRTY_ATTENTIVE | DIRTY_SETTINGS
| DIRTY_SCREEN_BRIGHTNESS_BOOST)) != 0) {
final long time = mClock.uptimeMillis();
for (int id : mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked()) {
if (mDisplayGroupPowerStateMapper.getWakefulnessLocked(id) == WAKEFULNESS_AWAKE
&& isItBedTimeYetLocked(id)) {
if (DEBUG_SPEW) {
Slog.d(TAG, "updateWakefulnessLocked: Bed time for group " + id);
}
// 长时间亮屏灭屏请求
if (isAttentiveTimeoutExpired(id, time)) {
changed = sleepDisplayGroupNoUpdateLocked(id, time,
PowerManager.GO_TO_SLEEP_REASON_TIMEOUT,
PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE, Process.SYSTEM_UID);
} else if (shouldNapAtBedTimeLocked()) { //是否支持屏保
changed = dreamDisplayGroupNoUpdateLocked(id, time, Process.SYSTEM_UID);
} else { // 灭屏
changed = sleepDisplayGroupNoUpdateLocked(id, time,
PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, 0, Process.SYSTEM_UID);
}
}
}
}
return changed;
}
此方法中isItBedTimeYetLocked是决定是否灭屏的关键:
frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
private boolean isItBedTimeYetLocked(int groupId) {
if (!mBootCompleted) {
return false;
}
long now = mClock.uptimeMillis();
if (isAttentiveTimeoutExpired(groupId, now)) { // 彻底灭屏场景
return !isBeingKeptFromInattentiveSleepLocked(groupId);
} else { // 正常情况
return !isBeingKeptAwakeLocked(groupId);
}
}
private boolean isBeingKeptAwakeLocked(int groupId) {
return mStayOn // 判断是否屏幕常亮
|| mProximityPositive // P sensor是否有靠近数据
|| (mDisplayGroupPowerStateMapper.getWakeLockSummaryLocked(groupId)
& WAKE_LOCK_STAY_AWAKE) != 0 // 屏幕是否持有亮屏WAKE LOCK
|| (mDisplayGroupPowerStateMapper.getUserActivitySummaryLocked(groupId) & (
USER_ACTIVITY_SCREEN_BRIGHT | USER_ACTIVITY_SCREEN_DIM)) != 0 // 屏幕是否依然是亮屏或者DIM状态
|| mScreenBrightnessBoostInProgress; //是否正在增强亮度
}
上面isBeingKeptAwakeLocked中条件有任意条件是true,那么不可以进行灭屏。
到这里已经完成PMS中灭屏状态切换,接下来就和Power键灭屏流程一样,往DPC中发出灭屏申请,完成灭屏流程。