PowerManager学习笔记-超时灭屏流程

3,268 阅读5分钟

本文基于Android S源码梳理

系列回顾

PowerManager学习笔记-Power键亮屏流程

PowerManager学习笔记-Power键灭屏流程

学习目标

通过AOSP源码,对超时灭屏流程进行梳理

简介

日常使用手机过程中,如果不去可以设置屏幕常亮,手机将会一定时间内,无任何使用状态下进入灭屏。这个过程就叫超时灭屏。

这里如果没有了解过此流程的同学,不妨去试想一下,如果让你设计一个超时灭屏的功能,你将如何设计?

通过上面提到的需求,首先肯定需要在PMS中设计一个超时机制,这个可以通过Handler进行实现,timeout将会是读取SettingsProvider中的值。而如何判断一段时间内无任何使用操作呢?这个可以通过input模块进行监听判断。那么接下来我们就来看下源码中的设计是否与此类似?

时序图

未命名文件.jpg

流程解析

活动监听

这里先交代一个背景,在用户对手机进行操作后,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中发出灭屏申请,完成灭屏流程。