Android 7系统休眠唤醒(五)休眠全链路

系列目录第一篇:电源管理架构全景图 | 第二篇:开机全链路---BootROM到Launcher | 第三篇:关机/重启全链路---ShutdownThread到kernel_power_off | 第四篇:休眠唤醒与开关机---核心差异深度对比 | 第五篇:休眠全链路---PMS到Kernel Suspend | 第六篇:唤醒全链路---Kernel Resume到屏幕点亮 | 第七篇:内核层---wakelock与autosleep机制 | 第八篇:内核层---Alarm定时唤醒与硬件唤醒源 | 第九篇:Native层---libsuspend与Power HAL | 第十篇:实战调试与问题排查


一、为什么要深入理解休眠流程

你可能遇到过这些问题:

  • 按下电源键后屏幕黑了,但系统真的休眠了吗?为什么有时按电源键没反应?
  • 应用持有 WakeLock 不释放,系统就无法休眠------这个"无法休眠"到底发生在哪一层?
  • 从 Java 层的 goToSleep() 到 CPU 执行 WFI 指令,中间经历了多少次状态跳转?

本篇从 PowerManagerService 一路下穿到内核的 pm_suspend(),完整追踪一次休眠操作的调用链。不讨论原理(原理见第四篇),只追踪代码。


二、休眠全链路概览

在进入细节之前,先建立全局视角。一次完整的休眠操作从 Java 层一路贯穿到内核层,经历以下层次:

复制代码
Java层 (Framework)
  PowerManager.goToSleep()
    → PMS.BinderService.goToSleep()       [权限检查]
      → goToSleepInternal()
        → goToSleepNoUpdateLocked()
            setWakefulnessLocked(WAKEFULNESS_DOZING, reason)
        → updatePowerStateLocked()         [PMS 核心状态机]
          → updateWakeLockSummaryLocked()  [汇总 WakeLock]
          → updateUserActivitySummaryLocked() [检测超时]
          → updateWakefulnessLocked()      [判断 bedtime]
          → reallyGoToSleepNoUpdateLocked() [DOZING → ASLEEP]
          → updateDisplayPowerStateLocked() [关闭屏幕]
          → updateSuspendBlockerLocked()   [释放 suspend_blocker]
            → nativeSetAutoSuspend(true)

JNI层
  nativeSetAutoSuspend(true)
    → autosuspend_enable()

Native层 (libsuspend)
  autosuspend_enable()
    → wakeup_count方式: 休眠线程 read → write wakeup_count → write "mem"
    → autosleep方式:    直接写入 "mem" 到 /sys/power/autosleep

Kernel层
  state_store()                            [sysfs回调]
    → pm_suspend(state)
      → enter_state(state)
        → sys_sync()                       [文件系统同步]
        → suspend_prepare()                [挂起前准备]
        → freeze_processes()               [冻结所有用户进程]
        → suspend_devices_and_irq()        [挂起设备、禁用IRQ]
        → suspend_enter()
          → arch_suspend_disable_irqs()    [禁用中断]
          → syscore_suspend()              [挂起系统核心]
          → suspend_ops->enter()           [平台相关: WFI指令]
          → CPU进入deep sleep

下面按调用链顺序逐层拆解。


三、休眠触发入口

休眠有三种触发方式,但最终都汇聚到 goToSleepNoUpdateLocked()------修改唤醒状态标记的核心方法。区别在于进入方式:电源键和应用调用通过 Binder 入口 goToSleep() 进入,超时则通过状态机 updateWakefulnessLocked() 进入。

3.1 应用调用 PowerManager.goToSleep()

任何持有 android.permission.DEVICE_POWER 权限的应用都可以调用 PowerManager.goToSleep() 主动触发休眠:

源码路径frameworks/base/core/java/android/os/PowerManager.java

java 复制代码
public final class PowerManager {
    // ...
    final IPowerManager mService;  // Binder 代理,指向 PowerManagerService

    public void goToSleep(long time) {
        goToSleep(time, GO_TO_SLEEP_REASON_APPLICATION, 0);
    }

    public void goToSleep(long time, int reason, int flags) {
        try {
            mService.goToSleep(time, reason, flags);  // Binder 调用到 PMS
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }
}

源码路径frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

java 复制代码
public final class PowerManagerService extends SystemService {

    private final class BinderService extends IPowerManager.Stub {
        @Override // Binder call
        public void goToSleep(long eventTime, int reason, int flags) {
            goToSleepInternal(eventTime, reason, flags, uid);
        }
    }

    private void goToSleepInternal(long eventTime, int reason, int flags, int uid) {
        synchronized (mLock) {
            if (goToSleepNoUpdateLocked(eventTime, reason, flags, uid)) {
                updatePowerStateLocked();
            }
        }
    }

    protected void updatePowerStateLocked() {
        updateSuspendBlockerLocked();
    }

    private void setHalAutoSuspendModeLocked(boolean enable) {
        nativeSetAutoSuspend(enable);
    }
}

关键设计PowerManager.goToSleep() 只是一个 Binder 代理层------通过 mServiceIPowerManager)跨进程调用 PowerManagerService,三种触发方式在此汇聚。

完整调用链

复制代码
应用调用 PowerManager.goToSleep()
  → mService.goToSleep(time, reason, flags)  [Binder]
    → PowerManagerService.goToSleepInternal()

3.2 超时自动休眠

用户一段时间不操作屏幕,屏幕超时后自动进入休眠。这是最常见的休眠场景。

1. 定时器设置updateUserActivitySummaryLocked() 根据用户最后活动时间计算下一次超时时间,并通过 Handler 发送延时消息:

源码路径frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

java 复制代码
public final class PowerManagerService extends SystemService {
    // ...
    private int mUserActivitySummary;        // 用户活动状态标志
    private long mLastUserActivityTime;      // 最后一次用户活动时间
    private final PowerManagerHandler mHandler;  // PMS 内部 Handler

    private void updateUserActivitySummaryLocked(long now, int dirty) {
        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) {
                final int screenOffTimeout = getScreenOffTimeoutLocked(sleepTimeout);
                final int screenDimDuration = getScreenDimDurationLocked(screenOffTimeout);

                mUserActivitySummary = 0;
                if (mLastUserActivityTime >= mLastWakeTime) {
                    // 计算屏幕变暗的时间点
                    nextTimeout = mLastUserActivityTime + screenOffTimeout - screenDimDuration;
                    if (now < nextTimeout) {
                        mUserActivitySummary = USER_ACTIVITY_SCREEN_BRIGHT;
                    } else {
                        // 计算屏幕关闭的时间点
                        nextTimeout = mLastUserActivityTime + screenOffTimeout;
                        if (now < nextTimeout) {
                            mUserActivitySummary = USER_ACTIVITY_SCREEN_DIM;
                        }
                    }
                }

                // 如果用户活动已超时,发送延时消息
                if (mUserActivitySummary == 0) {
                    // ... 省略: 处理 sleepTimeout 和 dream 模式 ...
                }

                // 发送超时消息
                if (mUserActivitySummary != 0 && nextTimeout >= 0) {
                    Message msg = mHandler.obtainMessage(MSG_USER_ACTIVITY_TIMEOUT);
                    msg.setAsynchronous(true);
                    mHandler.sendMessageAtTime(msg, nextTimeout);  // 在 nextTimeout 时刻触发
                }
            }
        }
    }
}

关键设计 :超时检测通过 Handler.sendMessageAtTime() 实现精确定时。每次用户操作时调用 userActivityNoUpdateLocked() 更新 mLastUserActivityTime,然后 updateUserActivitySummaryLocked() 重新计算超时时间并发送新消息。

2. 超时触发 :消息超时后,handleUserActivityTimeout() 被调用:

java 复制代码
public final class PowerManagerService extends SystemService {
    // ...
    private int mDirty;  // 状态变更标记位
    private int mWakefulness;  // 当前唤醒状态
    
    private void handleUserActivityTimeout() { // runs on handler thread
        synchronized (mLock) {
            mDirty |= DIRTY_USER_ACTIVITY;  // 标记用户活动超时
            updatePowerStateLocked();        // 触发状态机重新计算
        }
    }

    protected void updatePowerStateLocked() {
        updateWakefulnessLocked(dirtyPhase1);   
    }

    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) {
            if (mWakefulness == WAKEFULNESS_AWAKE && isItBedTimeYetLocked()) {
                // isItBedTimeYetLocked() 检查:
                // - 所有 WakeLock 是否已释放
                // - 用户活动是否已超时
                // - 是否满足其他休眠条件
                changed = goToSleepNoUpdateLocked(SystemClock.uptimeMillis(),
                        PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, 0, Process.SYSTEM_UID);
            }
        }
        return changed;
    }

    private boolean goToSleepNoUpdateLocked(long eventTime, int reason, int flags, int uid) {

    }
}

关键设计 :超时休眠的完整路径是:定时器超时 → handleUserActivityTimeout()updatePowerStateLocked()updateWakefulnessLocked()goToSleepNoUpdateLocked()setWakefulnessLocked(WAKEFULNESS_DOZING)。状态先跳转到 DOZING(Doze 模式),Doze 结束后再由 reallyGoToSleepNoUpdateLocked() 跳转到 ASLEEP。

完整调用链

复制代码
用户操作超时
  → updateUserActivitySummaryLocked() 计算超时时间
    → mHandler.sendMessageAtTime(MSG_USER_ACTIVITY_TIMEOUT, nextTimeout)
      → [超时后] handleUserActivityTimeout()
        → mDirty |= DIRTY_USER_ACTIVITY
        → updatePowerStateLocked()
          → updateWakefulnessLocked()
            → goToSleepNoUpdateLocked()

3.3 电源键短按

用户短按电源键,屏幕立即关闭并进入休眠。

1. 按键拦截PhoneWindowManagerinterceptKeyBeforeQueueing() 中拦截电源键事件,清除 ACTION_PASS_TO_USER 标志,阻止事件传递给应用层:

源码路径frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java

java 复制代码
public class PhoneWindowManager implements WindowManagerPolicy {
    // ...

    public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
        // ...
        switch (event.getKeyCode()) {
            case KeyEvent.KEYCODE_POWER: {
                result &= ~ACTION_PASS_TO_USER;  // 不向上传递给应用
                isWakeKey = false;
                if (down) {
                    interceptPowerKeyDown(event, interactive);  // 按键按下
                } else {
                    interceptPowerKeyUp(event, interactive, canceled);  // 按键释放
                }
                break;
            }
        }
        return result;
    }
}

关键设计 :电源键事件在 PhoneWindowManager 被拦截,ACTION_PASS_TO_USER 标志被清除,应用不会收到电源键事件。goToSleep() 的调用不在按键按下时,而是在按键释放时。

2. 按键释放触发休眠interceptPowerKeyUp() 在按键释放时调用 powerPress(),根据短按行为配置决定是否休眠:

java 复制代码
public class PhoneWindowManager implements WindowManagerPolicy {
    // ...
    private int mPowerKeyPressCounter;  // 按键计数(用于多击检测)

    private void interceptPowerKeyUp(KeyEvent event, boolean interactive, boolean canceled) {
        final boolean handled = canceled || mPowerKeyHandled;
        // ... 省略: 取消截图组合键等 ...

        if (!handled) {
            mPowerKeyPressCounter += 1;

            final int maxCount = getMaxMultiPressPowerCount();
            final long eventTime = event.getDownTime();
            if (mPowerKeyPressCounter < maxCount) {
                // 可能是多击,等待确认
                Message msg = mHandler.obtainMessage(MSG_POWER_DELAYED_PRESS,
                        interactive ? 1 : 0, mPowerKeyPressCounter, eventTime);
                msg.setAsynchronous(true);
                mHandler.sendMessageDelayed(msg, ViewConfiguration.getDoubleTapTimeout());
                return;
            }

            // 确认是单次按键,立即处理
            powerPress(eventTime, interactive, mPowerKeyPressCounter);
        }

        finishPowerKeyPress();
    }
}

3. powerPress() 根据配置执行休眠mShortPressOnPowerBehavior 决定短按行为,默认是 SHORT_PRESS_POWER_GO_TO_SLEEP

java 复制代码
public class PhoneWindowManager implements WindowManagerPolicy {
    // ...
    private int mShortPressOnPowerBehavior;  // 短按电源键行为
    private PowerManager mPowerManager;      // 电源管理器

    private void powerPress(long eventTime, boolean interactive, int count) {
        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:
                    mPowerManager.goToSleep(eventTime,
                            PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON,
                            PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);  // 跳过 Doze 直接深度休眠
                    break;
                case SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP_AND_GO_HOME:
                    mPowerManager.goToSleep(eventTime,
                            PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON,
                            PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);
                    launchHomeFromHotKey();  // 休眠后回到桌面
                    break;
                case SHORT_PRESS_POWER_GO_HOME:
                    // ... 省略: 回到桌面 ...
                    break;
            }
        }
    }
}

关键设计 :电源键休眠的完整路径是:按键释放 → interceptPowerKeyUp()powerPress()mPowerManager.goToSleep()mShortPressOnPowerBehavior 配置决定短按行为:普通休眠、跳过 Doze 直接深度休眠、或休眠后回到桌面。

完整调用链

复制代码
KEYCODE_POWER 短按
  → interceptKeyBeforeQueueing()           [拦截事件,不传递给应用]
    → [按下] interceptPowerKeyDown()       [设置长按定时器、截图组合键检测等]
    → [释放] interceptPowerKeyUp()
      → powerPress(eventTime, interactive, count=1)
        → mShortPressOnPowerBehavior == SHORT_PRESS_POWER_GO_TO_SLEEP
        → mPowerManager.goToSleep(eventTime, GO_TO_SLEEP_REASON_POWER_BUTTON, 0)
          → BinderService.goToSleep()
            → PowerManagerService.goToSleepInternal()

四、PMS.goToSleep() --- 休眠的 Java 层入口

所有休眠触发方式最终都汇聚到 PowerManagerServicegoToSleepInternal()。这个方法是休眠的 Java 层入口。

源码路径frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

java 复制代码
public final class PowerManagerService extends SystemService {
    // ...
    private final Object mLock = new Object();  // PMS 内部锁

    // Binder 回调内部类
    private final class BinderService extends IPowerManager.Stub {
        // ...
        @Override // Binder call
        public void goToSleep(long eventTime, int reason, int flags) {
            // 权限检查
            mContext.enforceCallingOrSelfPermission(
                    android.Manifest.permission.DEVICE_POWER, null);

            final int uid = Binder.getCallingUid();
            final long ident = Binder.clearCallingIdentity();
            try {
                goToSleepInternal(eventTime, reason, flags, uid);
            } finally {
                Binder.restoreCallingIdentity(ident);
            }
        }
    }

    private void goToSleepInternal(long eventTime, int reason, int flags, int uid) {
        synchronized (mLock) {
            if (goToSleepNoUpdateLocked(eventTime, reason, flags, uid)) {
                updatePowerStateLocked();  // 核心状态机
            }
        }
    }
}

关键设计 :核心两步------goToSleepNoUpdateLocked() 修改状态标记,返回 true 时调用 updatePowerStateLocked() 执行实际状态变更。权限检查在 Binder 线程完成,状态操作在持锁后执行。


五、goToSleepNoUpdateLocked() --- 状态标记修改

goToSleepNoUpdateLocked() 只修改标记,不做实际操作。这是 PMS 的典型设计模式:NoUpdateLocked 系列方法只负责状态计算,真正的副作用由 updatePowerStateLocked() 统一执行。

源码路径frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

java 复制代码
public final class PowerManagerService extends SystemService {
    // ...
    private int mWakefulness;       // 当前唤醒状态
    private int mDirty;             // 状态变更标记位
    private long mLastSleepTime;    // 最近一次休眠时间
    private boolean mSandmanSummoned;  // 是否正在召唤 Doze

    private boolean goToSleepNoUpdateLocked(long eventTime, int reason, int flags, int uid) {
        // 前置校验:时间异常或已处于休眠/Doze状态则跳过
        if (eventTime < mLastWakeTime
                || mWakefulness == WAKEFULNESS_ASLEEP
                || mWakefulness == WAKEFULNESS_DOZING
                || !mBootCompleted || !mSystemReady) {
            return false;
        }

        // ... 省略: 根据 reason 打印不同日志 ...

        mLastSleepTime = eventTime;
        mSandmanSummoned = true;
        setWakefulnessLocked(WAKEFULNESS_DOZING, reason);  // 状态跳转:AWAKE → DOZING

        // ... 省略: 统计将被清除的 WakeLock 数量 ...

        // 如果指定了 NO_DOZE 标志,跳过 Doze 直接进入深度休眠
        if ((flags & PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE) != 0) {
            reallyGoToSleepNoUpdateLocked(eventTime, uid);
        }
        return true;
    }
}

关键设计 :方法名中的 NoUpdateLocked 表明它只修改标记、不触发副作用。核心操作是 setWakefulnessLocked(WAKEFULNESS_DOZING, reason) 将状态从 AWAKE 跳转到 DOZING(而非直接到 ASLEEP)。DOZING 是 Doze 模式的过渡状态,如果指定了 NO_DOZE 标志则跳过 Doze 直接调用 reallyGoToSleepNoUpdateLocked()

mWakefulness 的四个取值:

常量 含义
0 WAKEFULNESS_AWAKE 清醒(正在使用)
1 WAKEFULNESS_DREAMING 屏保/互动屏保(Android 4.2+)
2 WAKEFULNESS_DOZING 正在进入休眠(过渡状态)
3 WAKEFULNESS_ASLEEP 已休眠

六、updatePowerStateLocked() --- 状态机核心调度

updatePowerStateLocked() 是 PMS 最核心的函数。每当 mDirty 不为零时调用,根据标记位按 Phase 顺序执行相应的状态更新。

源码路径frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

java 复制代码
public final class PowerManagerService extends SystemService {
    // ...
    private boolean mSystemReady;   // 系统是否就绪
    private int mDirty;             // 状态变更标记位
    private boolean mDisplayReady;  // 显示是否就绪

    protected void updatePowerStateLocked() {
        if (!mSystemReady || mDirty == 0) {
            return;
        }

        // Phase 0: 基础状态更新
        updateIsPoweredLocked(mDirty);
        updateStayOnLocked(mDirty);
        updateScreenBrightnessBoostLocked(mDirty);

        // Phase 1: 更新唤醒状态(循环,因为 wakefulness 变化会影响 WakeLock 计算)
        final long now = SystemClock.uptimeMillis();
        int dirtyPhase2 = 0;
        for (;;) {
            int dirtyPhase1 = mDirty;
            dirtyPhase2 |= dirtyPhase1;
            mDirty = 0;

            updateWakeLockSummaryLocked(dirtyPhase1);
            updateUserActivitySummaryLocked(now, dirtyPhase1);
            if (!updateWakefulnessLocked(dirtyPhase1)) {
                break;
            }
        }

        // Phase 2: 更新显示电源状态(屏幕开关)
        boolean displayBecameReady = updateDisplayPowerStateLocked(dirtyPhase2);

        // Phase 3: 更新 Dream 状态
        updateDreamLocked(dirtyPhase2, displayBecameReady);

        // Phase 4: 完成唤醒状态变更通知
        finishWakefulnessChangeIfNeededLocked();

        // Phase 5: 管理 suspend blocker(必须最后执行)
        updateSuspendBlockerLocked();
    }
}

关键设计:六个 Phase 按固定顺序执行。Phase 1 使用循环结构------因为唤醒状态的变化可能影响 WakeLock 和用户活动的计算,需要迭代直到稳定。Phase 5 必须最后执行,因为释放 suspend blocker 可能触发内核休眠。

当执行休眠路径时,各 Phase 行为如下:

6.1 updateWakeLockSummaryLocked() --- 汇总 WakeLock

遍历所有活跃 WakeLock,将它们的类型汇总到 mWakeLockSummary 标志位中。休眠路径关心的是 WAKE_LOCK_CPU 标志------如果为 0,说明没有应用持有 CPU 唤醒锁。

源码路径frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

java 复制代码
public final class PowerManagerService extends SystemService {
    // ...
    private int mWakeLockSummary;  // 活跃 WakeLock 聚合标志位

    private void updateWakeLockSummaryLocked(int dirty) {
        if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_WAKEFULNESS)) != 0) {
            mWakeLockSummary = 0;

            // 遍历所有 WakeLock,按类型汇总标志位
            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.PARTIAL_WAKE_LOCK:
                        mWakeLockSummary |= WAKE_LOCK_CPU;
                        break;
                    case PowerManager.SCREEN_BRIGHT_WAKE_LOCK:
                        mWakeLockSummary |= WAKE_LOCK_SCREEN_BRIGHT;
                        break;
                    case PowerManager.SCREEN_DIM_WAKE_LOCK:
                        mWakeLockSummary |= WAKE_LOCK_SCREEN_DIM;
                        break;
                    case PowerManager.DOZE_WAKE_LOCK:
                        mWakeLockSummary |= WAKE_LOCK_DOZE;
                        break;
                    // ... 省略: 其他 WakeLock 类型 ...
                }
            }

            // 根据当前状态裁剪无意义的标志位
            if (mWakefulness != WAKEFULNESS_DOZING) {
                mWakeLockSummary &= ~(WAKE_LOCK_DOZE | WAKE_LOCK_DRAW);
            }
            if (mWakefulness == WAKEFULNESS_ASLEEP) {
                mWakeLockSummary &= ~(WAKE_LOCK_SCREEN_BRIGHT | WAKE_LOCK_SCREEN_DIM
                        | WAKE_LOCK_BUTTON_BRIGHT | WAKE_LOCK_PROXIMITY_SCREEN_OFF);
            }
        }
    }
}

关键设计mWakeLockSummary 是所有活跃 WakeLock 的"或"聚合。休眠时如果 WAKE_LOCK_CPU 为 0,说明没有应用阻止休眠。注意裁剪逻辑:ASLEEP 状态下,屏幕相关的 WakeLock 标志会被清除,因为它们已经没有意义。

6.2 updateUserActivitySummaryLocked() --- 超时检测

计算用户活动超时时间,通过 Handler 发送延时消息。超时后触发 DIRTY_USER_ACTIVITY 标记,驱动状态机进入休眠流程。

源码路径frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

java 复制代码
public final class PowerManagerService extends SystemService {
    // ...
    private int mUserActivitySummary;        // 用户活动状态标志
    private long mLastUserActivityTime;      // 最后一次用户活动时间
    private final PowerManagerHandler mHandler;  // PMS 内部 Handler

    private void updateUserActivitySummaryLocked(long now, int dirty) {
        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) {
                final int screenOffTimeout = getScreenOffTimeoutLocked(sleepTimeout);
                final int screenDimDuration = getScreenDimDurationLocked(screenOffTimeout);

                mUserActivitySummary = 0;
                if (mLastUserActivityTime >= mLastWakeTime) {
                    // 计算屏幕变暗的时间点
                    nextTimeout = mLastUserActivityTime + screenOffTimeout - screenDimDuration;
                    if (now < nextTimeout) {
                        mUserActivitySummary = USER_ACTIVITY_SCREEN_BRIGHT;
                    } else {
                        // 计算屏幕关闭的时间点
                        nextTimeout = mLastUserActivityTime + screenOffTimeout;
                        if (now < nextTimeout) {
                            mUserActivitySummary = USER_ACTIVITY_SCREEN_DIM;
                        }
                    }
                }

                // 发送超时消息
                if (mUserActivitySummary != 0 && nextTimeout >= 0) {
                    Message msg = mHandler.obtainMessage(MSG_USER_ACTIVITY_TIMEOUT);
                    msg.setAsynchronous(true);
                    mHandler.sendMessageAtTime(msg, nextTimeout);
                }
            }
        }
    }
}

关键设计 :超时检测分两个阶段:先 USER_ACTIVITY_SCREEN_BRIGHT(屏幕亮),再 USER_ACTIVITY_SCREEN_DIM(屏幕暗),最后超时。每次用户操作时 mLastUserActivityTime 更新,定时器重新计算。

6.3 updateWakefulnessLocked() --- 状态跳转

mDirty 包含休眠相关标记时,判断是否需要进入休眠。如果是 bedtime,调用 goToSleepNoUpdateLocked() 触发休眠流程:

java 复制代码
public final class PowerManagerService extends SystemService {
    // ...
    private int mWakefulness;  // 当前唤醒状态

    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) {
            if (mWakefulness == WAKEFULNESS_AWAKE && isItBedTimeYetLocked()) {
                // ... 省略: 判断是否应该进入 Dream 模式 ...
                changed = goToSleepNoUpdateLocked(SystemClock.uptimeMillis(),
                        PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, 0, Process.SYSTEM_UID);
            }
        }
        return changed;
    }
}

关键设计updateWakefulnessLocked() 不是直接设置 ASLEEP,而是再次调用 goToSleepNoUpdateLocked() 进入 DOZING 状态。真正的 DOZING → ASLEEP 跳转发生在 Doze 流程结束后,由 reallyGoToSleepNoUpdateLocked() 完成。

6.4 reallyGoToSleepNoUpdateLocked() --- 最终休眠

Doze 流程结束后,调用此方法完成最终的状态跳转:

java 复制代码
public final class PowerManagerService extends SystemService {
    // ...
    private int mWakefulness;  // 当前唤醒状态

    // 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;
        }

        Slog.i(TAG, "Sleeping (uid " + uid + ")...");

        setWakefulnessLocked(WAKEFULNESS_ASLEEP, PowerManager.GO_TO_SLEEP_REASON_TIMEOUT);
        return true;
    }
}

关键设计reallyGoToSleepNoUpdateLocked() 将状态从 DOZING 跳转到 ASLEEP,这是休眠流程中最后一次状态变更。方法注释 "Done dozing, drop everything and go to sleep" 清晰说明了它的职责。

6.5 updateDisplayPowerStateLocked() --- 关闭屏幕

根据 mWakefulness 状态计算屏幕策略,调用 DisplayPowerController 调整屏幕亮度或关闭屏幕。屏幕关闭后设置 mDisplayReady = true

源码路径frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

java 复制代码
public final class PowerManagerService extends SystemService {
    // ...
    private boolean mDisplayReady;  // 显示是否就绪
    private DisplayPowerRequest mDisplayPowerRequest;  // 显示电源请求

    private boolean updateDisplayPowerStateLocked(int dirty) {
        final boolean oldDisplayReady = mDisplayReady;
        if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_USER_ACTIVITY | DIRTY_WAKEFULNESS
                | DIRTY_ACTUAL_DISPLAY_POWER_STATE_UPDATED | DIRTY_BOOT_COMPLETED
                | DIRTY_SETTINGS | DIRTY_SCREEN_BRIGHTNESS_BOOST | DIRTY_VR_MODE_CHANGED)) != 0) {
            // 根据 mWakefulness 计算屏幕策略
            mDisplayPowerRequest.policy = getDesiredScreenPolicyLocked();

            // ... 省略: 计算屏幕亮度、自动亮度调整 ...

            // 更新显示电源请求
            mDisplayPowerRequest.screenBrightness = screenBrightness;
            mDisplayPowerRequest.screenAutoBrightnessAdjustment =
                    screenAutoBrightnessAdjustment;
            mDisplayPowerRequest.brightnessSetByUser = brightnessSetByUser;

            // 调用 DisplayPowerController 应用请求
            mDisplayManagerInternal.requestPowerState(mDisplayPowerRequest,
                    mRequestWaitForNegativeProximity);
            mRequestWaitForNegativeProximity = false;
        }

        // 检查显示是否就绪
        mDisplayReady = !mDisplayManagerInternal.isProximityPositive()
                || !mDisplayPowerRequest.useProximitySensor;

        return mDisplayReady && !oldDisplayReady;
    }

    // 根据 mWakefulness 计算屏幕策略
    private int getDesiredScreenPolicyLocked() {
        switch (mWakefulness) {
            case WAKEFULNESS_ASLEEP:
                return DisplayPowerRequest.POLICY_OFF;
            case WAKEFULNESS_DOZING:
                return DisplayPowerRequest.POLICY_DOZE;
            case WAKEFULNESS_AWAKE:
                return DisplayPowerRequest.POLICY_BRIGHT;
            case WAKEFULNESS_DREAMING:
                return DisplayPowerRequest.POLICY_BRIGHT;
        }
        return DisplayPowerRequest.POLICY_BRIGHT;
    }
}

关键设计getDesiredScreenPolicyLocked() 根据 mWakefulness 决定屏幕策略:AWAKE/DREAMING → 亮屏,DOZING → Doze 模式(低亮度),ASLEEP → 关屏。mDisplayReady 为 true 表示屏幕状态已稳定,可以进入下一阶段。

6.6 updateSuspendBlockerLocked() --- 管理 suspend blocker

管理两个关键的 suspend_blocker,决定是否允许内核休眠。当两个 blocker 都释放后,调用 nativeSetAutoSuspend(true) 允许内核休眠。

源码路径frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

java 复制代码
public final class PowerManagerService extends SystemService {
    // ...
    private int mWakeLockSummary;  // 活跃 WakeLock 聚合标志位
    private boolean mHoldingWakeLockSuspendBlocker;  // 是否持有 WakeLock blocker
    private boolean mHoldingDisplaySuspendBlocker;   // 是否持有 Display blocker
    private final SuspendBlocker mWakeLockSuspendBlocker;   // WakeLock blocker
    private final SuspendBlocker mDisplaySuspendBlocker;    // Display blocker

    private void updateSuspendBlockerLocked() {
        // 判断是否需要持有 blocker
        final boolean needWakeLockSuspendBlocker = ((mWakeLockSummary & WAKE_LOCK_CPU) != 0);
        final boolean needDisplaySuspendBlocker = needDisplaySuspendBlockerLocked();
        final boolean autoSuspend = !needDisplaySuspendBlocker;

        // 先获取需要的 blocker
        if (needWakeLockSuspendBlocker && !mHoldingWakeLockSuspendBlocker) {
            mWakeLockSuspendBlocker.acquire();
            mHoldingWakeLockSuspendBlocker = true;
        }
        if (needDisplaySuspendBlocker && !mHoldingDisplaySuspendBlocker) {
            mDisplaySuspendBlocker.acquire();
            mHoldingDisplaySuspendBlocker = true;
        }

        // 再释放不需要的 blocker
        if (!needWakeLockSuspendBlocker && mHoldingWakeLockSuspendBlocker) {
            mWakeLockSuspendBlocker.release();
            mHoldingWakeLockSuspendBlocker = false;
        }
        if (!needDisplaySuspendBlocker && mHoldingDisplaySuspendBlocker) {
            mDisplaySuspendBlocker.release();
            mHoldingDisplaySuspendBlocker = false;
        }

        // 如果 Display blocker 不需要了,允许内核自动休眠
        if (autoSuspend && mDecoupleHalAutoSuspendModeFromDisplayConfig) {
            setHalAutoSuspendModeLocked(true);  // → nativeSetAutoSuspend(true)
        }
    }

    // 判断是否需要 Display blocker
    private boolean needDisplaySuspendBlockerLocked() {
        if (!mDisplayReady) {
            return true;  // 显示未就绪,需要保持 blocker
        }
        if (mDisplayPowerRequest.isBrightOrDim()) {
            // 屏幕亮或暗,需要保持 blocker
            return true;
        }
        // 屏幕关闭或 Doze 模式,可以释放 blocker
        return false;
    }

    private void setHalAutoSuspendModeLocked(boolean enable) {
        if (enable != mHalAutoSuspendModeEnabled) {
            mHalAutoSuspendModeEnabled = enable;
            nativeSetAutoSuspend(enable);  // 调用 JNI 层
        }
    }
}

关键设计 :两个 blocker 的释放条件:WakeLockSuspendBlockermWakeLockSummary & WAKE_LOCK_CPU == 0 时释放(无 CPU 唤醒锁);DisplaySuspendBlockerneedDisplaySuspendBlockerLocked() 返回 false 时释放(屏幕关闭且显示就绪)。两个 blocker 都释放后,调用 nativeSetAutoSuspend(true) 允许内核休眠。


七、nativeSetAutoSuspend() --- JNI 跨越

当 PMS 的 updateSuspendBlockerLocked() 判断可以进入休眠后,调用setHalAutoSuspendModeLocked方法,该方法通过 JNI 调用 Native 层的 autosuspend_enable(),开始进入底层休眠流程。

源码路径frameworks/base/services/core/jni/com_android_server_power_PowerManagerService.cpp

cpp 复制代码
static void nativeSetAutoSuspend(JNIEnv* /* env */, jclass /* clazz */, jboolean enable) {
    if (enable) {
        autosuspend_enable();   // 启用自动休眠
    } else {
        autosuspend_disable();  // 禁用自动休眠
    }
}

关键设计 :JNI 层只是一个简单的转发,真正的逻辑在 libsuspend 库中实现。注意函数签名使用了 JNIEnv* /* env */ 注释掉未使用参数------这是 JNI 代码的常见风格。


八、libsuspend --- Native 层休眠触发

libsuspend 是 Android 提供的用户空间库,负责与内核的休眠机制交互。它支持两种方式:wakeup_countautosleep

8.1 autosuspend_enable() --- 入口函数

源码路径system/core/libsuspend/autosuspend.c

c 复制代码
static struct autosuspend_ops *autosuspend_ops;  // 具体实现的函数指针
static bool autosuspend_enabled;

int autosuspend_enable(void) {
    int ret;

    ret = autosuspend_init();  // 首次调用时初始化,选择具体实现
    if (ret) {
        return ret;
    }

    if (autosuspend_enabled) {
        return 0;
    }

    ret = autosuspend_ops->enable();  // 调用具体实现的 enable
    if (ret) {
        return ret;
    }

    autosuspend_enabled = true;
    return 0;
}

关键设计autosuspend_enable() 首次调用时会执行 autosuspend_init(),按优先级选择具体实现:先尝试 earlysuspend,再尝试 autosleep(当前被 #if 0 禁用),最后回退到 wakeup_count。之后通过 autosuspend_ops->enable() 调用选中实现的 enable 函数。

8.2 wakeup_count 方式 --- 线程模型

源码路径system/core/libsuspend/autosuspend_wakeup_count.c

c 复制代码
static int state_fd;                  // /sys/power/state 的文件描述符
static int wakeup_count_fd;           // /sys/power/wakeup_count 的文件描述符
static pthread_t suspend_thread;      // 休眠线程
static sem_t suspend_lockout;         // 信号量:控制休眠线程是否运行
static const char *sleep_state = "mem";

// 休眠线程:循环执行 read → wait → write → write 流程
static void *suspend_thread_func(void *arg) {
    while (1) {
        usleep(100000);
        // 1. 读取当前 wakeup_count
        lseek(wakeup_count_fd, 0, SEEK_SET);
        read(wakeup_count_fd, wakeup_count, sizeof(wakeup_count));

        // 2. 等待信号量(enable 时 post,disable 时 wait)
        sem_wait(&suspend_lockout);

        // 3. 写入 wakeup_count 确认无新唤醒事件
        write(wakeup_count_fd, wakeup_count, wakeup_count_len);

        // 4. 写入 "mem" 到 /sys/power/state 触发休眠
        write(state_fd, sleep_state, strlen(sleep_state));

        sem_post(&suspend_lockout);
    }
}

static int autosuspend_wakeup_count_enable(void) {
    // 释放信号量,让休眠线程开始工作
    sem_post(&suspend_lockout);
    return 0;
}

static int autosuspend_wakeup_count_disable(void) {
    // 获取信号量,阻塞休眠线程
    sem_wait(&suspend_lockout);
    return 0;
}

关键设计wakeup_count 方式使用独立线程循环执行休眠逻辑。enable() 只是 sem_post() 释放信号量让线程开始工作,disable() 通过 sem_wait() 阻塞线程。休眠线程内部通过 read → write wakeup_count → write "mem" 三步完成休眠,信号量机制解决了竞态问题。

8.3 autosleep 方式 --- 内核自动管理

源码路径system/core/libsuspend/autosuspend_autosleep.c

c 复制代码
static int autosleep_fd;                   // /sys/power/autosleep 的文件描述符
static const char *sleep_state = "mem";    // 休眠状态
static const char *on_state = "off";       // 唤醒状态

static int autosuspend_autosleep_enable(void) {
    // 写入 "mem" 到 /sys/power/autosleep
    write(autosleep_fd, sleep_state, strlen(sleep_state));
    return 0;
}

static int autosuspend_autosleep_disable(void) {
    // 写入 "off" 到 /sys/power/autosleep
    write(autosleep_fd, on_state, strlen(on_state));
    return 0;
}

关键设计autosleep 方式比 wakeup_count 简单得多------只需写入 /sys/power/autosleep,内核自己管理休眠时机。但注意在当前源码中,autosleep#if 0 禁用,实际默认使用 wakeup_count 方式。


九、内核层 --- pm_suspend() 完整路径

/sys/power/state 被写入 "mem" 后,内核执行休眠。内核的休眠流程分为五个阶段:准备、冻结、挂起设备、进入休眠、恢复。

9.1 state_store() --- sysfs 回调入口

源码路径kernel/msm-3.18/kernel/power/main.c

c 复制代码
static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
                           const char *buf, size_t n) {
    suspend_state_t state;

    error = pm_suspend(state);  ← 进入内核休眠主入口
    return error ? error : n;
}

关键设计state_store() 是 sysfs 的写回调,用户空间写入 /sys/power/state 时触发。

9.2 pm_suspend() 与 enter_state() --- 休眠主入口

源码路径kernel/msm-3.18/kernel/power/suspend.c

c 复制代码
int pm_suspend(suspend_state_t state) {
int pm_suspend(suspend_state_t state)
{
	int error;

	if (state <= PM_SUSPEND_ON || state >= PM_SUSPEND_MAX)
		return -EINVAL;

	pm_suspend_marker("entry");
	error = enter_state(state);//进入具体休眠流程
	if (error) {
		suspend_stats.fail++;
		dpm_save_failed_errno(error);
	} else {
		suspend_stats.success++;
	}
	pm_suspend_marker("exit");
	return error;
}
}

static int enter_state(suspend_state_t state) {
    // 1. 同步文件系统
    sys_sync();

    // 2. 挂起前准备(冻结进程等)
    error = suspend_prepare(state);

    // 3. 挂起设备并进入休眠(核心流程)
    error = suspend_devices_and_enter(state);

    // 4. 收尾清理( thaw 进程等)
    suspend_finish();
    return error;
}

关键设计enter_state() 是休眠的主框架,但它本身不做设备挂起------而是委托给 suspend_devices_and_enter()。注意函数名是 suspend_devices_and_enter,不是某些文档中写的 suspend_devices_and_irq

suspend_devices_and_enter() 的内部流程:

c 复制代码
int suspend_devices_and_enter(suspend_state_t state) {
    // 1. 平台相关初始化
    error = platform_suspend_begin(state);

    // 2. 挂起控制台
    suspend_console();

    // 3. 挂起所有设备(第一阶段)
    error = dpm_suspend_start(PMSG_SUSPEND);

    // 4. 进入休眠(可能循环执行,支持 suspend_again)
    do {
        error = suspend_enter(state, &wakeup);
    } while (!error && !wakeup && platform_suspend_again(state));

    // 5. 恢复设备
    dpm_resume_end(PMSG_RESUME);
    resume_console();
    return error;
}

关键设计suspend_devices_and_enter() 先挂起所有设备,然后调用 suspend_enter() 进入休眠。注意 do...while 循环------某些平台支持 suspend_again,即休眠后立即被某个事件唤醒但希望再次进入休眠。

9.3 freeze_processes() --- 冻结所有用户进程

源码路径kernel/msm-3.18/kernel/power/process.c

c 复制代码
int freeze_processes(void) {
    // 遍历所有用户进程
    for_each_process_thread(g, p) {
        freeze_task(p);  ← 冻结单个进程
    }
    return 0;
}

bool freeze_task(struct task_struct *p) {
    // 1. 设置 TIF_FREEZE 标志
    set_tsk_thread_flag(p, TIF_FREEZE);

    // 2. 发送伪信号唤醒进程
    wake_up_process(p);

    // 3. 进程在返回用户空间前检测 TIF_FREEZE,
    //    调用 try_to_freeze() 进入 TASK_UNINTERRUPTIBLE 状态
}

关键设计 :冻结机制的精妙之处在于不是直接挂起进程,而是在进程即将返回用户空间时拦截 。内核线程需要主动调用 try_to_freeze() 检查标志。

9.4 suspend_enter() --- 进入硬件休眠

源码路径kernel/msm-3.18/kernel/power/suspend.c

c 复制代码
static int suspend_enter(suspend_state_t state, bool *wakeup) {
    // 1. 平台相关准备
    error = platform_suspend_prepare(state);

    // 2. 挂起设备(late 阶段)
    error = dpm_suspend_late(PMSG_SUSPEND);
    error = platform_suspend_prepare_late(state);

    // 3. 挂起设备(noirq 阶段,中断已禁用)
    error = dpm_suspend_noirq(PMSG_SUSPEND);
    error = platform_suspend_prepare_noirq(state);

    // 4. 禁用非启动 CPU
    error = disable_nonboot_cpus();

    // 5. 禁用中断
    arch_suspend_disable_irqs();

    // 6. 挂起系统核心
    error = syscore_suspend();

    // 7. 调用平台相关的休眠代码 → CPU 进入 deep sleep
    error = suspend_ops->enter(state);
    //     → 通常执行:
    //       ① 保存 CPU 寄存器状态
    //       ② 配置唤醒源
    //       ③ 执行 WFI (Wait For Interrupt) 指令
    //       ④ CPU 进入 low-power state

    // === 被唤醒后,从这里继续执行 ===

    // 8. 恢复系统核心
    syscore_resume();

    // 9. 重新启用中断
    arch_suspend_enable_irqs();

    // 10. 重新启用非启动 CPU
    enable_nonboot_cpus();
    return error;
}

关键设计suspend_enter() 是休眠的最后阶段,在禁用中断后执行 syscore_suspend()suspend_ops->enter()。被唤醒后按相反顺序恢复:syscore_resume() → 启用中断 → 启用 CPU。注意 suspend_ops->enter() 是平台相关的回调,由具体 SoC 实现,ARM 架构通常执行 WFI 指令让 CPU 进入低功耗状态。


十、休眠流程完整调用链

复制代码
Java层 (Framework)
  PowerManager.goToSleep()
    → PMS.BinderService.goToSleep()       [权限检查]
      → goToSleepInternal()
        → goToSleepNoUpdateLocked()
            setWakefulnessLocked(WAKEFULNESS_DOZING, reason)
        → updatePowerStateLocked()         [PMS 核心状态机]
          → updateWakeLockSummaryLocked()  [汇总 WakeLock]
          → updateUserActivitySummaryLocked() [检测超时]
          → updateWakefulnessLocked()      [判断 bedtime]
          → reallyGoToSleepNoUpdateLocked() [DOZING → ASLEEP]
          → updateDisplayPowerStateLocked() [关闭屏幕]
          → updateSuspendBlockerLocked()   [释放 suspend_blocker]
            → nativeSetAutoSuspend(true)

JNI层
  nativeSetAutoSuspend(true)
    → autosuspend_enable()

Native层 (libsuspend)
  autosuspend_enable()
    → wakeup_count方式: 休眠线程 read → write wakeup_count → write "mem"
    → autosleep方式:    直接写入 "mem" 到 /sys/power/autosleep

Kernel层
  state_store()                            [sysfs回调]
    → pm_suspend(state)
      → enter_state(state)
        → sys_sync()                       [文件系统同步]
        → suspend_prepare()                [挂起前准备]
        → freeze_processes()               [冻结所有用户进程]
        → suspend_devices_and_irq()        [挂起设备、禁用IRQ]
        → suspend_enter()
          → arch_suspend_disable_irqs()    [禁用中断]
          → syscore_suspend()              [挂起系统核心]
          → suspend_ops->enter()           [平台相关: WFI指令]
          → CPU进入deep sleep

十一、休眠中的关键数据流

11.1 Screen WakeLock 的释放时机

休眠的前提是"没有人在阻止休眠"。在 PMS 中,阻止休眠的机制是 suspend blocker------只要有一个 blocker 没释放,内核就不能进入休眠。

PMS 内部维护两个 suspend blocker:

Blocker 释放条件 谁在阻止它释放
WakeLockSuspendBlocker 没有应用持有 PARTIAL_WAKE_LOCK 持有 CPU 唤醒锁的应用
DisplaySuspendBlocker 屏幕已关闭且显示就绪 屏幕还在亮着

休眠时,两个 blocker 必须全部释放 ,才会调用 nativeSetAutoSuspend(true) 允许内核休眠。释放过程分三步,每步解决一个问题:

复制代码
goToSleepNoUpdateLocked()          updateWakeLockSummaryLocked()         updateSuspendBlockerLocked()
        │                                    │                                     │
  ① 标记"我要休眠了"               ② 重新盘点:哪些 WakeLock            ③ 根据盘点结果
     状态 AWAKE → DOZING               还有效、哪些已失效                    释放 blocker
                                                                          → nativeSetAutoSuspend(true)

goToSleepNoUpdateLocked() --- 标记"我要休眠了"

这个方法只做一件事:把 mWakefulness 从 AWAKE 改为 DOZING,并设置 mSandmanSummoned = true 触发 Doze 流程。它不释放任何 WakeLock ------PMS 的设计模式是 NoUpdateLocked 方法只修改标记,副作用由后续的 updatePowerStateLocked() 统一执行。

源码路径frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

java 复制代码
public final class PowerManagerService extends SystemService {
    // ...
    private int mWakefulness;          // 当前唤醒状态
    private int mDirty;                // 状态变更标记位
    private boolean mSandmanSummoned;  // 是否正在召唤 Doze

    private boolean goToSleepNoUpdateLocked(long eventTime, int reason, int flags, int uid) {
        // ... 省略: 前置校验 ...

        mLastSleepTime = eventTime;
        mSandmanSummoned = true;   // 触发 Doze 流程
        setWakefulnessLocked(WAKEFULNESS_DOZING, reason);
            // → mWakefulness = WAKEFULNESS_DOZING
            // → mDirty |= DIRTY_WAKEFULNESS
        return true;
    }

    private void setWakefulnessLocked(int wakefulness, int reason) {
        if (mWakefulness != wakefulness) {
            mWakefulness = wakefulness;
            mDirty |= DIRTY_WAKEFULNESS;
            mNotifier.onWakefulnessChangeStarted(wakefulness, reason);
        }
    }
}

关键设计 :状态先跳到 DOZING 而不是直接到 ASLEEP------因为 Doze 流程需要时间完成(比如屏幕渐暗、AOD 显示等)。Doze 完成后,reallyGoToSleepNoUpdateLocked() 才会把状态从 DOZING 改为 ASLEEP。

updateWakeLockSummaryLocked() --- 重新盘点:哪些 WakeLock 还有效

状态变更后,mDirty 驱动 updatePowerStateLocked() 重新执行。updateWakeLockSummaryLocked() 遍历所有活跃 WakeLock,按类型聚合到 mWakeLockSummary 标志位中,然后根据当前状态裁剪已失效的标志:

java 复制代码
public final class PowerManagerService extends SystemService {
    // ...
    private int mWakefulness;       // 当前唤醒状态
    private int mWakeLockSummary;   // 活跃 WakeLock 聚合标志位
    private final ArrayList<WakeLock> mWakeLocks;  // 所有活跃 WakeLock 列表

    private void updateWakeLockSummaryLocked(int dirty) {
        if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_WAKEFULNESS)) != 0) {
            mWakeLockSummary = 0;

            // 遍历所有活跃 WakeLock,按类型聚合标志位
            for (int i = 0; i < mWakeLocks.size(); i++) {
                final WakeLock wakeLock = mWakeLocks.get(i);
                switch (wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) {
                    case PowerManager.PARTIAL_WAKE_LOCK:
                        mWakeLockSummary |= WAKE_LOCK_CPU;       // ← 阻止休眠的关键标志
                        break;
                    case PowerManager.DOZE_WAKE_LOCK:
                        mWakeLockSummary |= WAKE_LOCK_DOZE;      // ← Doze 期间保持
                        break;
                    // ... 省略: SCREEN_BRIGHT、SCREEN_DIM 等其他类型 ...
                }
            }

            // 裁剪:ASLEEP 状态下,屏幕相关标志全部失效
            if (mWakefulness == WAKEFULNESS_ASLEEP) {
                mWakeLockSummary &= ~(WAKE_LOCK_SCREEN_BRIGHT | WAKE_LOCK_SCREEN_DIM
                        | WAKE_LOCK_BUTTON_BRIGHT);
            }
            // 裁剪:非 Doze 状态下,Doze 相关标志失效
            if (mWakefulness != WAKEFULNESS_DOZING) {
                mWakeLockSummary &= ~(WAKE_LOCK_DOZE | WAKE_LOCK_DRAW);
            }
        }
    }
}

关键设计 :裁剪逻辑的核心问题是------即使某个应用还持有 SCREEN_BRIGHT_WAKE_LOCK,一旦进入 ASLEEP 状态,这个标志也会被裁剪掉,不再计入 mWakeLockSummary。而 WAKE_LOCK_CPU(来自 PARTIAL_WAKE_LOCK)不会被裁剪------只要有任何应用持有它,系统就无法休眠。

updateSuspendBlockerLocked() --- 根据盘点结果释放 blocker

mWakeLockSummary 盘点完毕后,updateSuspendBlockerLocked() 检查两个条件,决定是否释放 blocker:

java 复制代码
public final class PowerManagerService extends SystemService {
    // ...
    private int mWakeLockSummary;                        // 活跃 WakeLock 聚合标志位
    private boolean mHoldingWakeLockSuspendBlocker;      // 是否持有 WakeLock blocker
    private boolean mHoldingDisplaySuspendBlocker;       // 是否持有 Display blocker
    private final SuspendBlocker mWakeLockSuspendBlocker;   // WakeLock blocker
    private final SuspendBlocker mDisplaySuspendBlocker;    // Display blocker

    private void updateSuspendBlockerLocked() {
        // 条件一:还有应用持有 PARTIAL_WAKE_LOCK 吗?
        final boolean needWakeLockSuspendBlocker = ((mWakeLockSummary & WAKE_LOCK_CPU) != 0);

        // 条件二:屏幕还在亮着吗?
        final boolean needDisplaySuspendBlocker = needDisplaySuspendBlockerLocked();

        // 释放不再需要的 WakeLockSuspendBlocker
        if (!needWakeLockSuspendBlocker && mHoldingWakeLockSuspendBlocker) {
            mWakeLockSuspendBlocker.release();        // ← 释放
            mHoldingWakeLockSuspendBlocker = false;
        }

        // 释放不再需要的 DisplaySuspendBlocker
        if (!needDisplaySuspendBlocker && mHoldingDisplaySuspendBlocker) {
            mDisplaySuspendBlocker.release();         // ← 释放
            mHoldingDisplaySuspendBlocker = false;
        }

        // 两个 blocker 都释放 → 允许内核自动休眠
        if (!needDisplaySuspendBlocker) {
            setHalAutoSuspendModeLocked(true);        // → nativeSetAutoSuspend(true)
        }
    }

    private boolean needDisplaySuspendBlockerLocked() {
        if (!mDisplayReady) return true;              // 显示未就绪 → 保持
        if (mDisplayPowerRequest.isBrightOrDim()) return true;  // 屏幕亮或暗 → 保持
        return false;                                 // 屏幕关闭 → 可释放
    }
}

关键设计needDisplaySuspendBlockerLocked() 的三层判断------显示未就绪、屏幕亮/暗时都保持 blocker,只有屏幕完全关闭且显示就绪后才释放。两个 blocker 全部释放后,setHalAutoSuspendModeLocked(true) 调用 JNI → libsuspend → 写入 /sys/power/state → 内核开始休眠。

完整链路

复制代码
用户按下电源键
  → goToSleepNoUpdateLocked()          状态 AWAKE → DOZING,触发 Doze
  → [Doze 流程完成]
  → reallyGoToSleepNoUpdateLocked()    状态 DOZING → ASLEEP
  → updateWakeLockSummaryLocked()      裁剪屏幕相关标志,mWakeLockSummary 只剩 WAKE_LOCK_CPU
  → updateSuspendBlockerLocked()       无应用持有 PARTIAL_WAKE_LOCK → 释放两个 blocker
    → nativeSetAutoSuspend(true)       → libsuspend → 内核休眠

11.2 Notifier 的广播

updatePowerStateLocked() 执行过程中,setWakefulnessLocked() 内部调用 mNotifier.onWakefulnessChangeStarted() 向系统发送电源状态变更通知:

源码路径frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

java 复制代码
public final class PowerManagerService extends SystemService {
    // ...
    private Notifier mNotifier;  // 电源状态通知器

    private void setWakefulnessLocked(int wakefulness, int reason) {
        if (mWakefulness != wakefulness) {
            mWakefulness = wakefulness;
            mWakefulnessChanging = true;
            mDirty |= DIRTY_WAKEFULNESS;
            mNotifier.onWakefulnessChangeStarted(wakefulness, reason);  // 广播状态变更
        }
    }
}

关键设计 :Notifier 在状态变更时立即广播,而不是等到 updatePowerStateLocked() 结束。onWakefulnessChangeStarted() 会触发 PhoneWindowManagerDreamManagerService 等服务响应电源状态变化。


十二、关键源码文件索引

文件 职责 所在目录
PowerManagerService.java PMS 核心状态机 frameworks/.../power/
PowerManager.java 应用层 API frameworks/.../os/
PhoneWindowManager.java 电源键拦截 frameworks/.../policy/
Notifier.java 电源状态广播 frameworks/.../power/
...PowerManagerService.cpp JNI 层 frameworks/.../jni/
autosuspend.c libsuspend 入口 system/core/libsuspend/
autosuspend_wakeup_count.c wakeup_count system/core/libsuspend/
autosuspend_autosleep.c autosleep system/core/libsuspend/
suspend.c 内核休眠主流程 kernel/power/
main.c sysfs 接口 kernel/power/
process.c 进程冻结 kernel/power/

十三、小结

休眠全链路从 Java 层一气贯通到内核层,核心路径为:

  1. 触发:电源键/超时 → PMS.goToSleep()
  2. 标记:设置 mWakefulness 跳转和 mDirty 标记位
  3. 状态机:updatePowerStateLocked() 按 Phase 执行
  4. 释放 WakeLock:两个 suspend_blocker 全部释放
  5. Native:libsuspend 写入 /sys/power/state 或 /sys/power/autosleep
  6. 内核:freeze 进程 → 挂起设备 → CPU 进入 deep sleep

整个流程在 1 秒内完成,关键在于"冻结而非杀死"的设计------下一篇将分析唤醒流程,看系统如何从中断触发到点亮屏幕,以同样的速度恢复。

相关推荐
zzq77972 小时前
Android 风险识别机制实测:从录屏悬浮窗到远程控制的边界评估
android·安全·安卓·app加固·御盾安全
码云数智-园园2 小时前
MySQL 慢查询排查完整流程
android
码云骑士2 小时前
【Android Performance】进程关联启动治理详解——从唤醒链分析到自启动管控的完整方案
android
心念枕惊3 小时前
PHP 在领域驱动(DDD)设计中的核心实践
android·开发语言·php
蜡台3 小时前
Android WebView 设计指南
android·java·kotlin
晓梦林4 小时前
[Dest0g3 520迎新赛]EasyPHP-学习笔记
android·笔记·学习
朱涛的自习室4 小时前
Munk AI 桌面端「预告」
android·前端·人工智能
程序员正茂4 小时前
Android studio中使用OpenCV C++库
android·c++·opencv·mobile
恋猫de小郭5 小时前
Android R8 为什么可以让 Kotlin 协程提速 2 倍?
android·前端·flutter