第 29 章:电源管理
电源管理是 Android 最关键的子系统之一。手机想要维持一整天续航,每毫安电流都至关重要。Android 依靠多层架构实现续航能力:从 Linux 内核挂起机制,电源与热控硬件抽象层,再到框架层服务;框架层服务负责跟踪唤醒锁、执行 Doze 与应用待机策略、将功耗统计归属到各个 UID。本章以 AOSP 真实源码为主要参考,逐层剖析上述各个组件。29.1 电源管理架构29.1.1 高层概述Android 电源管理由 Linux 内核、厂商定制 HAL、Android 框架协同工作。高层架构如下:应用 | v PowerManager(android.os.PowerManager) -- 公开 API | v PowerManagerService(system_server) -- 策略引擎 | ├── Notifier(广播、电池统计) ├── PowerGroup(每个显示分组状态) ├── SuspendBlocker(阻止内核挂起) | v Power HAL(IPower) -- 厂商插件 | v Linux 内核(cpufreq、suspend、wakeup_sources)框架服务PowerManagerService处于核心位置。接收应用请求(唤醒锁、用户活动事件),与策略模块(Doze、省电模式、显示控制器)交互,通过 Power HAL 以及原生 JNI 调用驱动硬件,向下访问内核。源码文件:frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java29.1.2 核心组件组件源码路径作用PowerManagerServiceframeworks/base/services/core/java/com/android/server/power/PowerManagerService.java核心策略引擎PowerGroupframeworks/base/services/core/java/com/android/server/power/PowerGroup.java每个显示分组的唤醒状态管理Notifierframeworks/base/services/core/java/com/android/server/power/Notifier.java电源事件广播发送器SuspendBlockerframeworks/base/services/core/java/com/android/server/power/SuspendBlocker.java底层 CPU 保活控制WakeLockLogframeworks/base/services/core/java/com/android/server/power/WakeLockLog.java压缩环形缓冲区日志DeviceIdleControllerframeworks/base/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.javaDoze(轻度 + 深度)状态机AppStandbyControllerframeworks/base/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java应用待机分组ThermalManagerServiceframeworks/base/services/core/java/com/android/server/power/thermal/ThermalManagerService.java热事件分发BatteryStatsImplframeworks/base/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java能耗统计记账IPower HALhardware/interfaces/power/aidl/android/hardware/power/IPower.aidl厂商电源 Hint 接口IThermal HALhardware/interfaces/thermal/aidl/android/hardware/thermal/IThermal.aidl温度监控29.1.3 设备唤醒状态Android 使用四状态机跟踪设备唤醒状态,常量定义在PowerManagerInternal:// frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java import static android.os.PowerManagerInternal.WAKEFULNESS_ASLEEP; import static android.os.PowerManagerInternal.WAKEFULNESS_AWAKE; import static android.os.PowerManagerInternal.WAKEFULNESS_DOZING; import static android.os.PowerManagerInternal.WAKEFULNESS_DREAMING;状态数值说明WAKEFULNESS_AWAKE1屏幕点亮,用户正在交互WAKEFULNESS_DREAMING2屏保(Dream)正在运行WAKEFULNESS_DOZING3低功耗常显(AOD)WAKEFULNESS_ASLEEP0屏幕熄灭,CPU 可以进入挂起状态流转:29.1.4 Dirty Bits(脏位)机制PowerManagerService使用名为mDirty的位掩码,记录电源状态发生变更、需要重新计算的项。这是核心优化点:微小事件到来时不会全量重算电源策略,只重新计算被标记为脏的部分。// PowerManagerService.java,第201‑232行 // Dirty bit:mWakeLocks发生变更 private static final int DIRTY_WAKE_LOCKS = 1 0; // Dirty bit:mWakefulness发生变更 private static final int DIRTY_WAKEFULNESS = 1 1; // Dirty bit:用户被触发活动或者可能超时 private static final int DIRTY_USER_ACTIVITY = 1 2; // Dirty bit:实际显示电源状态已异步更新 private static final int DIRTY_ACTUAL_DISPLAY_POWER_STATE_UPDATED = 1 3; // Dirty bit:mBootCompleted发生变更 private static final int DIRTY_BOOT_COMPLETED = 1 4; // Dirty bit:设置项发生变更 private static final int DIRTY_SETTINGS = 1 5; // Dirty bit:mIsPowered发生变更 private static final int DIRTY_IS_POWERED = 1 6; // Dirty bit:mStayOn发生变更 private static final int DIRTY_STAY_ON = 1 7; // Dirty bit:电池状态发生变更 private static final int DIRTY_BATTERY_STATE = 1 8; // Dirty bit:距离传感器状态发生变更 private static final int DIRTY_PROXIMITY_POSITIVE = 1 9; // Dirty bit:底座状态发生变更 private static final int DIRTY_DOCK_STATE = 1 10; // Dirty bit:亮度增强状态变更 private static final int DIRTY_SCREEN_BRIGHTNESS_BOOST = 1 11; // Dirty bit:sQuiescent发生变更 private static final int DIRTY_QUIESCENT = 1 12; // Dirty bit:attentive计时器可能超时 private static final int DIRTY_ATTENTIVE = 1 14; // Dirty bit:显示分组唤醒状态变更 private static final int DIRTY_DISPLAY_GROUP_WAKEFULNESS = 1 16; // Dirty bit:设备姿态状态变更 private static final int DIRTY_POSTURED_STATE = 1 17;任意属性发生变更,通过或运算设置对应脏位,调用updatePowerStateLocked()重新计算所有脏项。29.1.5 电源状态更新循环PowerManagerService的核心是updatePowerStateLocked()方法。每当电源相关状态变化,该方法执行多阶段循环。// PowerManagerService.java,第2689行 private void updatePowerStateLocked() { if (!mSystemReady || mDirty == 0 || mUpdatePowerStateInProgress) { return; } Trace.traceBegin(Trace.TRACE_TAG_POWER, "updatePowerState"); mUpdatePowerStateInProgress = true; try { // 阶段0:基础状态更新 updateIsPoweredLocked(mDirty); updateStayOnLocked(mDirty); updateScreenBrightnessBoostLocked(mDirty); // 阶段1:更新唤醒状态 // 使用循环:唤醒锁、用户活动计算会被唤醒状态变更影响 final long now = mClock.uptimeMillis(); int dirtyPhase2 = 0; for (;;) { int dirtyPhase1 = mDirty; dirtyPhase2 |= dirtyPhase1; mDirty = 0; updateWakeLockSummaryLocked(dirtyPhase1); updateUserActivitySummaryLocked(now, dirtyPhase1); updateAttentiveStateLocked(now, dirtyPhase1); if (!updateWakefulnessLocked(dirtyPhase1)) { break; } } // 阶段2:更新不再活跃、不再保活的锁配置 updateProfilesLocked(now); // 阶段3:更新所有PowerGroup电源状态 final boolean powerGroupsBecameReady = updatePowerGroupsLocked(dirtyPhase2); // 阶段4:更新屏保状态(依赖PowerGroup就绪信号) updateDreamLocked(dirtyPhase2, powerGroupsBecameReady); // 阶段5:按需发送通知 finishWakefulnessChangeIfNeededLocked(); // 阶段6:按需通知屏幕超时策略变更 notifyScreenTimeoutPolicyChangesLocked(); // 阶段7:更新SuspendBlocker // 此处可能释放最后一个suspend blocker,必须保证前面所有逻辑执行完毕 updateSuspendBlockerLocked(); } finally { Trace.traceEnd(Trace.TRACE_TAG_POWER); mUpdatePowerStateInProgress = false; } }七个阶段构成 Android 电源管理的心跳。阶段 1 内部循环,因为唤醒状态变更会让唤醒锁、用户活动汇总结果失效,需要再次迭代。29.1.6 完整更新流程详解以用户按下电源键熄屏场景,追踪各阶段协同工作流程:goToSleepInternal()逆序遍历 PowerGroup,非默认分组优先完成状态切换,之后再处理默认分组:// PowerManagerService.java,第7561行 private void goToSleepInternal(IntArray groupIds, long eventTime, int reason, int flags) { // ... synchronized (mLock) { // 逆序遍历,保证所有非默认显示分组先完成状态变更,再处理默认分组 for (int i = groupIds.size() - 1; i = 0; i--) { int groupId = groupIds.get(i); PowerGroup powerGroup = mPowerGroups.get(groupId); if ((flags PowerManager.GO_TO_SLEEP_FLAG_SOFT_SLEEP) != 0) { if (!powerGroup.hasWakeLockKeepingScreenOnLocked()) { mNotifier.showDismissibleKeyguard(); } continue; } if (isNoDoze) { sleepPowerGroupLocked(powerGroup, eventTime, reason, uid); } else { dozePowerGroupLocked(powerGroup, eventTime, reason, uid, false); } } } }注意GO_TO_SLEEP_FLAG_SOFT_SLEEP分支:不会真正熄屏,而是展示可关闭的锁屏界面,供部分无障碍流程使用。29.1.7 PowerGroup 唤醒状态切换每一个PowerGroup实现真实的唤醒状态切换。wakePowerGroupLocked()负责唤醒操作:// PowerManagerService.java,第2315行 private void wakePowerGroupLocked(final PowerGroup powerGroup, long eventTime, @WakeReason int reason, String details, int uid, String opPackageName, int opUid) { if (mForceSuspendActive || !mSystemReady || (powerGroup == null) || hasWakeLockKeepingGroupAsleep( powerGroup.getWakeLockSummaryLocked())) { return; } powerGroup.wakeUpLocked(eventTime, reason, details, uid, opPackageName, opUid, LatencyTracker.getInstance(mContext)); }进入 Doze 状态包含多显示器场景特殊逻辑:如果默认分组存在相邻显示分组仍然处于交互状态,则默认分组直接进入睡眠,而不是 Doze。// PowerManagerService.java,第2363行 private boolean dozePowerGroupLocked(final PowerGroup powerGroup, long eventTime, @GoToSleepReason int reason, int uid, boolean allowSleepToDozeTransition) { if (powerGroup.getGroupId() != Display.DEFAULT_DISPLAY_GROUP) { return sleepPowerGroupLocked(powerGroup, eventTime, reason, uid); } // 折叠屏/多显示器:如果相邻分组仍然交互,默认分组直接睡眠而非Doze if (com.android.server.display.feature.flags.Flags.separateTimeouts()) { boolean shouldSleep = (isDefaultAdjacentGroupInteractiveLocked()) || (powerGroup.getWakefulnessLocked() == WAKEFULNESS_ASLEEP !doAnyAdjacentGroupsExistLocked()); if (shouldSleep powerGroup.isDefaultOrAdjacentGroup()) { return sleepPowerGroupLocked(powerGroup, eventTime, reason, uid); } } return powerGroup.dozeLocked(eventTime, uid, reason, allowSleepToDozeTransition); }29.1.8 PowerManagerService 初始化PowerManagerService在system_server启动阶段完成实例化与注册。构造函数创建 Handler 线程、SuspendBlocker 以及原生封装对象。// PowerManagerService.java,第1216行 public PowerManagerService(Context context) { this(context, new Injector()); }Injector注入模式支持单元测试,可以将真实实现替换为 Mock 对象。关键初始化步骤:创建 Handler 线程,线程优先级THREAD_PRIORITY_DISPLAY创建 3 个 SuspendBlocker:Booting、WakeLocks、DisplayJNI 调用nativeInit(),完成与 Power HAL 连接开机默认关闭 auto‑suspend,开启 interactive 模式读取系统属性ro.boot.quiescent得到sQuiescent标记// PowerManagerService.java,第1304行 synchronized (mLock) { mBootingSuspendBlocker = mInjector.createSuspendBlocker(this, "PowerManagerService.Booting"); mWakeLockSuspendBlocker = mInjector.createSuspendBlocker(this, "PowerManagerService.WakeLocks"); mDisplaySuspendBlocker = mInjector.createSuspendBlocker(this, "PowerManagerService.Display"); // ... mNativeWrapper.nativeInit(this); mNativeWrapper.nativeSetAutoSuspend(false); mNativeWrapper.nativeSetPowerMode(Mode.INTERACTIVE, true); mNativeWrapper.nativeSetPowerMode(Mode.DOUBLE_TAP_TO_WAKE, false); }systemReady()阶段,服务连接DisplayManagerInternal、DreamManagerInternal、WindowManagerPolicy、BatteryManagerInternal;创建默认显示分组对应的PowerGroup,注册DisplayGroupListener监听动态新增显示器。29.2 PowerManagerService29.2.1 服务架构PowerManagerService继承SystemService,同时实现Watchdog.Monitor。对外发布两套服务接口:Binder 服务(Context.POWER_SERVICE):IPowerManagerAIDL,应用访问的公开接口本地服务(PowerManagerInternal):系统内部进程间调用接口,供其他系统服务使用// PowerManagerService.java,第1361行 @Override public void onStart() { publishBinderService(Context.POWER_SERVICE, mBinderService, /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL); publishLocalService(PowerManagerInternal.class, mLocalService); Watchdog.getInstance().addMonitor(this); Watchdog.getInstance().addThread(mHandler); }向 Watchdog 注册至关重要:如果PowerManagerService发生死锁,Watchdog 会杀死并重启system_server。29.2.2 唤醒锁汇总位掩码服务内部维护所有活跃唤醒锁的汇总位掩码,避免每次决策遍历全部唤醒锁列表。// PowerManagerService.java,第234‑244行 // 汇总所有活跃唤醒锁状态 static final int WAKE_LOCK_CPU = 1 0; static final int WAKE_LOCK_SCREEN_BRIGHT = 1 1; static final int WAKE_LOCK_SCREEN_DIM = 1 2; static final int WAKE_LOCK_BUTTON_BRIGHT = 1 3; static final int WAKE_LOCK_PROXIMITY_SCREEN_OFF = 1 4; static final int WAKE_LOCK_STAY_AWAKE = 1 5; static final int WAKE_LOCK_DOZE = 1 6; static final int WAKE_LOCK_DRAW = 1 7; static final int WAKE_LOCK_SCREEN_TIMEOUT_OVERRIDE = 1 8; static final int WAKE_LOCK_PARTIAL_SLEEP = 1 9;用户活动状态同样使用位掩码汇总:// 汇总用户活动状态 static final int USER_ACTIVITY_SCREEN_BRIGHT = 1 0; static final int USER_ACTIVITY_SCREEN_DIM = 1 1; static final int USER_ACTIVITY_SCREEN_DREAM = 1 2;29.2.3 用户活动与超时机制用户活动是维持屏幕点亮的机制。触摸、按键等交互调用userActivity()重置无活动计时器。屏幕超时由Settings.System.SCREEN_OFF_TIMEOUT控制。用户活动调用流程:内部记录lastUserActivityTime,依据screen_off_timeout计算超时时间。29.2.4 休眠与唤醒goToSleep()与wakeUp()驱动设备在各个唤醒状态之间切换;可以由电源按键、超时、代码调用触发。休眠原因(来自 PowerManager):GO_TO_SLEEP_REASON_APPLICATION应用请求休眠GO_TO_SLEEP_REASON_DEVICE_ADMIN设备管理员强制休眠GO_TO_SLEEP_REASON_TIMEOUT用户无操作超时GO_TO_SLEEP_REASON_LID_SWITCH笔记本合盖GO_TO_SLEEP_REASON_POWER_BUTTON用户按下电源键GO_TO_SLEEP_REASON_HDMIHDMI 断开GO_TO_SLEEP_REASON_SLEEP_BUTTON专用休眠按键GO_TO_SLEEP_REASON_ACCESSIBILITY无障碍动作GO_TO_SLEEP_REASON_FORCE_SUSPEND强制挂起GO_TO_SLEEP_REASON_DISPLAY_GROUPS_TURNED_OFF所有显示分组关闭GO_TO_SLEEP_REASON_DISPLAY_GROUP_REMOVED显示分组被移除GO_TO_SLEEP_REASON_QUIESCENT静默启动模式唤醒原因(来自 PowerManager):WAKE_REASON_POWER_BUTTON按下电源键WAKE_REASON_APPLICATION应用请求唤醒WAKE_REASON_PLUGGED_IN接入充电器WAKE_REASON_GESTURE检测到手势WAKE_REASON_WAKE_KEY唤醒按键按下WAKE_REASON_WAKE_MOTION检测到运动WAKE_REASON_DREAM_FINISHED屏保结束WAKE_REASON_DISPLAY_GROUP_ADDED新显示器接入WAKE_REASON_DISPLAY_GROUP_TURNED_ON显示器开启29.2.5 PowerGroupAndroid12 起,PowerManagerService以显示分组为单位管理电源状态,每一个DisplayGroup(一组共享电源状态的显示器)对应一个PowerGroup对象。// PowerGroup.java,第61行 public class PowerGroup { private final int mGroupId; private int mWakefulness; private int mWakeLockSummary; private int mUserActivitySummary; private long mLastWakeTime; private long mLastSleepTime; final DisplayPowerRequest mDisplayPowerRequest = new DisplayPowerRequest(); // ... }默认显示分组永久存在;外接显示器、虚拟显示器接入时动态创建额外分组。非默认分组可以标记为default‑group‑adjacent,代表与默认分组共用电源按键、锁屏行为。// PowerManagerService.java,第872行 private boolean isDefaultGroupAdjacent(int groupId) { long flags = mDisplayManagerInternal.getDisplayGroupFlags(groupId); return (flags DisplayGroup.FLAG_DEFAULT_GROUP_ADJACENT) != 0; }PowerGroupWakefulnessChangeListener监听唤醒状态变更:// PowerManagerService.java,第729行 private final class PowerGroupWakefulnessChangeListener implements PowerGroup.PowerGroupListener { @Override public void onWakefulnessChangedLocked(int groupId, int wakefulness, long eventTime, int reason, int uid, int opUid, String opPackageName, String details) { mWakefulnessChanging = true; mDirty |= DIRTY_WAKEFULNESS; // ... mDirty |= DIRTY_DISPLAY_GROUP_WAKEFULNESS; mNotifier.onGroupWakefulnessChangeStarted(groupId, wakefulness, reason, eventTime); updateGlobalWakefulnessLocked(eventTime, reason, uid, opUid, opPackageName, details); updatePowerStateLocked(); } }29.2.6 显示电源集成PowerManagerService通过DisplayManagerInternal控制显示器电源。每个PowerGroup维护DisplayPowerRequest,描述期望显示状态(ON/OFF/DOZE)以及亮度。显示电源请求在电源状态更新阶段 3updatePowerGroupsLocked()中计算完成,调用:mDisplayManagerInternal.requestPowerState(groupId, mDisplayPowerRequest)显示控制器异步执行状态变更,通过mDisplayPowerCallbacks回调;回调内部设置DIRTY_ACTUAL_DISPLAY_POWER_STATE_UPDATED,触发新一轮电源状态评估。29.2.7 NotifierNotifier负责向系统其他组件广播电源状态变更,发送广播例如:ACTION_SCREEN_ON/ACTION_SCREEN_OFFACTION_DREAMING_STARTED/ACTION_DREAMING_STOPPEDACTION_DEVICE_IDLE_MODE_CHANGEDNotifier运行在主线 Looper,而不是电源管理 Handler 线程,避免干扰电源管理关键时序。// PowerManagerService.java,第1420行 mNotifier = mInjector.createNotifier(Looper.getMainLooper(), mContext, mBatteryStats, mInjector.createSuspendBlocker(this, "PowerManagerService.Broadcasts"), mPolicy, mFaceDownDetector, mScreenUndimDetector, BackgroundThread.getExecutor(), mFeatureFlags);29.2.8 消息处理PowerManagerService使用消息机制处理延迟任务:// PowerManagerService.java,第182‑199行 private static final int MSG_USER_ACTIVITY_TIMEOUT = 1; private static final int MSG_SANDMAN = 2; private static final int MSG_SCREEN_BRIGHTNESS_BOOST_TIMEOUT = 3; private static final int MSG_CHECK_FOR_LONG_WAKELOCKS = 4; private static final int MSG_ATTENTIVE_TIMEOUT = 5; private static final int MSG_RELEASE_ALL_OVERRIDE_WAKE_LOCKS = 6; private static final int MSG_PROCESS_FROZEN_STATE_CHANGED = 7; private static final int MSG_FORCE_DISABLE_WAKELOCKS = 8;MSG_SANDMAN消息尤为关键:触发屏保、Doze 状态切换。用户活动计时器超时后调度该消息,最终调用DreamManagerService启动 / 停止屏保 / 常显界面。29.2.9 设置项观察者PowerManagerService注册内容观察者监听大量影响电源行为的系统设置:SCREENSAVER_ENABLED是否启用屏保SCREENSAVER_ACTIVATE_ON_SLEEP休眠时启动屏保SCREENSAVER_ACTIVATE_ON_DOCK底座接入时启动屏保SCREEN_OFF_TIMEOUT屏幕超时时长SLEEP_TIMEOUT附加休眠超时ATTENTIVE_TIMEOUT人眼检测超时STAY_ON_WHILE_PLUGGED_IN充电时保持屏幕常亮DOZE_ALWAYS_ON常显 AOD 开关DOUBLE_TAP_TO_WAKE双击亮屏手势THEATER_MODE_ON影院模式29.2.10 省电模式集成省电模式与PowerManagerService深度集成,相关源码目录:frameworks/base/services/core/java/com/android/server/power/batterysaver/BatterySaverController.javaBatterySaverPolicy.javaBatterySaverStateMachine.javaBatterySavingStats.javaBatterySaverStateMachine依据设备配置条件化创建:// PowerManagerService.java,第1247行 mBatterySaverSupported = mContext.getResources().getBoolean( com.android.internal.R.bool.config_batterySaverSupported); mBatterySaverStateMachine = mBatterySaverSupported ? mInjector.createBatterySaverStateMachine(mLock, mContext) : null;BatterySaverPolicy定义省电模式生效时启用的各项限制:// BatterySaverPolicy.java static final String KEY_LOCATION_MODE = "location_mode"; static final String KEY_DISABLE_VIBRATION = "disable_vibration"; static final String KEY_DISABLE_ANIMATION = "disable_animation"; static final String KEY_SOUNDTRIGGER_MODE = "soundtrigger_mode"; static final String KEY_ENABLE_FIREWALL = "enable_firewall"; static final String KEY_ENABLE_BRIGHTNESS_ADJUSTMENT = "enable_brightness_adjustment"; static final String KEY_ENABLE_DATASAVER = "enable_datasaver";省电模式激活,PowerManagerService通知 Power HAL:nativeSetPowerMode(Mode.LOW_POWER, true);厂商 HAL 可以降低 CPU 频率、关闭大核、降低 GPU 时钟,执行其他省电动作。29.2.11 静默模式 Quiescent Mode静默模式是特殊开机模式:开机后屏幕保持熄灭。用于无屏幕设备或者工厂测试。// PowerManagerService.java,第263行 private static final String SYSTEM_PROPERTY_QUIESCENT = "ro.boot.quiescent"; // 构造函数中读取 sQuiescent = mSystemProperties.get(SYSTEM_PROPERTY_QUIESCENT, "0").equals("1"); // 开机完成后处理 if (sQuiescent) { sleepPowerGroupLocked( mPowerGroups.get(Display.DEFAULT_DISPLAY_GROUP), mClock.uptimeMillis(), PowerManager.GO_TO_SLEEP_REASON_QUIESCENT, Process.SYSTEM_UID); }29.2.12 翻面检测 Face‑Down DetectionPowerManagerService内置FaceDownDetector,使用加速度传感器检测设备屏幕朝下放置。检测到后缩短屏幕超时时间。// PowerManagerService.java,第1333行 private void onFlip(boolean isFaceDown) { synchronized (mLock) { mIsFaceDown = isFaceDown; if (isFaceDown) { final long currentTime = mClock.uptimeMillis(); mLastFlipTime = currentTime; // 触发用户活动,启动更短超时 userActivityInternal(Display.DEFAULT_DISPLAY, currentTime, PowerManager.USER_ACTIVITY_EVENT_FACE_DOWN, PowerManager.USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS, Process.SYSTEM_UID); } } if (isFaceDown) { mFaceDownDetector.setMillisSaved(millisUntilNormalTimeout); } }29.2.13 人眼注意力检测 Attention DetectionAttentionDetector使用设备人眼检测服务(通常基于摄像头)判断用户是否正在观看屏幕。检测到用户注视屏幕时,延长屏幕超时,避免阅读时无触摸自动熄屏。// PowerManagerService.java,第1243行 mAttentionDetector = new AttentionDetector(this::onUserAttention, mLock);29.2.14 屏幕防调暗检测 Screen Undim DetectionScreenUndimDetector跟踪熄屏前调暗阶段用户交互。如果用户频繁阻止屏幕调暗,系统学习并延长屏幕超时。29.2.15 无线充电器检测 Wireless Charger DetectionWirelessChargerDetector借助传感器确认无线充电底座放置状态。避免设备离开充电器时误唤醒,保证底座接入时正确唤醒行为。// PowerManagerService.java,第1439行 mWirelessChargerDetector = mInjector.createWirelessChargerDetector( sensorManager, mInjector.createSuspendBlocker(this, "PowerManagerService.WirelessChargerDetector"), mHandler);29.3 WakeLocks 唤醒锁29.3.1 唤醒锁等级唤醒锁是应用阻止设备(或部分硬件)进入休眠的基础机制。每个唤醒锁拥有等级与可选标记。唤醒锁等级定义在android.os.PowerManager:// frameworks/base/core/java/android/os/PowerManager.java public static final int PARTIAL_WAKE_LOCK = 0x00000001; public static final int SCREEN_DIM_WAKE_LOCK = 0x00000006; // @Deprecated public static final int SCREEN_BRIGHT_WAKE_LOCK = 0x0000000a; // @Deprecated public static final int FULL_WAKE_LOCK = 0x0000001a; // @Deprecated public static final int PROXIMITY_SCREEN_OFF_WAKE_LOCK = 0x00000020; public static final int DOZE_WAKE_LOCK = 0x00000040; // @SystemApi public static final int DRAW_WAKE_LOCK = 0x00000080; // @SystemApi等级CPU屏幕按键背光说明PARTIAL_WAKE_LOCK开启‑‑‑‑最常用;CPU 保持运行,屏幕可以熄灭SCREEN_DIM_WAKE_LOCK开启调暗‑‑已废弃;使用窗口FLAG_KEEP_SCREEN_ONSCREEN_BRIGHT_WAKE_LOCK开启高亮‑‑已废弃FULL_WAKE_LOCK开启高亮高亮已废弃PROXIMITY_SCREEN_OFF_WAKE_LOCK开启熄灭 / 点亮‑‑靠近距离传感器时熄屏DOZE_WAKE_LOCK开启‑‑‑‑仅系统使用;维持 Doze 运行DRAW_WAKE_LOCK开启‑‑‑‑仅系统使用;允许 Doze 模式绘制29.3.2 唤醒锁标记可选标记修改唤醒锁行为:// PowerManager.java public static final int ACQUIRE_CAUSES_WAKEUP = 0x10000000; public static final int ON_AFTER_RELEASE = 0x20000000;ACQUIRE_CAUSES_WAKEUP:获取唤醒锁同时点亮屏幕。Android V + 应用需要持有TURN_SCREEN_ON

相关新闻

2026年7月白城市新房价格深度分析报告

2026年7月白城市新房价格深度分析报告

一、报告背景与数据说明本报告基于2026年7月白城市新房实际成交案例,结合成交价格、成交面积、成交区位等多维数据,对白城市当前新房市场进行深度分析。报告旨在为购房者、投资者及行业从业者提供真实、客观的市场参考。数据来源说明:本报告所…

2026/8/21 9:25:03 阅读更多 →
35 · 容量规划与成本估算

35 · 容量规划与成本估算

35 容量规划与成本估算一句话:先估"向量数据 索引"要多少内存和磁盘,再倒推该用什么配置、花多少钱,避免上线才发现内存不够。1. 为什么要提前估算 向量检索要快,索引最好在内存里。上线前不估算,常见翻车…

2026/8/21 9:25:03 阅读更多 →
2026年7月齐齐哈尔市新房价格深度分析报告

2026年7月齐齐哈尔市新房价格深度分析报告

一、报告背景与数据说明本报告基于2026年7月齐齐哈尔市新房市场实际成交案例,结合区域分布、楼盘类型、成交价格等多维度数据,对当前市场行情进行深度剖析。报告旨在为购房者、投资者及行业从业者提供客观、真实的价格参考与趋势判断。数据来源说明&…

2026/8/21 9:25:03 阅读更多 →

最新新闻

处理器占用的排查路径

处理器占用的排查路径

处理器占用的排查路径阅读说明:本文以RPC 框架中的典型故障链路说明排查和设计方法。文中的告警、数字与“线上”叙述如未给出来源,均应视为示例条件;落地前请在自己的版本、负载和资源约束下复测。1. 微服务级联级联故障事故:重试…

2026/8/21 10:48:07 阅读更多 →
【LangChain实战】大模型 RAG 必知必会:海量文档加载与智能文本切分(附完整预处理工程)

【LangChain实战】大模型 RAG 必知必会:海量文档加载与智能文本切分(附完整预处理工程)

在构建 RAG(检索增强生成)系统或企业本地知识库时,绝大多数企业数据都以静态文件形式存在——例如 PDF 员工手册、Markdown 售后规则、TXT 产品说明书 等。如何把这些异构文档高效读取并处理成大模型和向量数据库能理解的格式?核心…

2026/8/21 10:48:07 阅读更多 →
训练重试怎样避免重复消耗资源

训练重试怎样避免重复消耗资源

训练重试怎样避免重复消耗资源 1. 盲目重试为什么会压垮共享依赖 本文围绕“PyTorch 训练流程优化与分布式训练实践:超时重试怎样才不放大故障”整理一个可复查的技术检查点。文中的容量、时延和故障情形只用于说明验证方法;实际判断应以锁定的代码版本、…

2026/8/21 10:48:07 阅读更多 →
Web摇一摇功能实现:基于DeviceMotionEvent的移动端交互开发

Web摇一摇功能实现:基于DeviceMotionEvent的移动端交互开发

最近在开发一个趣味性小应用时,想实现一个类似“摇一摇”抽奖或互动的功能,既能增加用户参与感,又能作为技术练手。网上关于“摇一摇”的实现方案很多,但大多集中在移动端原生开发,对于想用Web技术快速实现一个轻量级、…

2026/8/21 10:48:07 阅读更多 →
Java全栈面试技巧:技术深度与工程思维实战解析

Java全栈面试技巧:技术深度与工程思维实战解析

1. Java全栈开发面试全景解析 最近帮团队面试了二十多位Java全栈方向的候选人,发现很多技术不错的同学在面试中容易陷入"技术正确但表达失分"的困境。作为经历过三次职业跃迁的面试官,我想分享一套经过实战检验的面试应对策略。 全栈面试本质…

2026/8/21 10:47:06 阅读更多 →
分布式事务重试怎样避免扩大故障

分布式事务重试怎样避免扩大故障

分布式事务重试怎样避免扩大故障 在微服务和分布式数据库中,2PC、TCC、Saga 等协议对超时的处理并不相同。遇到网络抖动或下游变慢时,客户端和协调器很容易加上无限重试或固定间隔重试;这往往掩盖了请求状态不明的问题。 重试不是默认补救手段…

2026/8/21 10:47:06 阅读更多 →

日新闻

机场边检旅客定位系统国产化白皮书:算法、硬件、底座平台全程自主

机场边检旅客定位系统国产化白皮书:算法、硬件、底座平台全程自主

前言随着国家数字基础设施信创替代、关键技术自主可控战略持续深化,口岸智慧安防、边检智能管控领域正全面进入国产化、自主化、安全可控升级周期。当前国内机场边检旅客识别与定位体系长期依赖国外商用视觉算法、进口成像硬件、闭源通用计算平台,存在核…

2026/8/21 0:00:42 阅读更多 →
别再把“数字孪生”当空间智能了!镜像视界揭开四维时空的真正面纱

别再把“数字孪生”当空间智能了!镜像视界揭开四维时空的真正面纱

别再把“数字孪生”当空间智能了!镜像视界揭开四维时空的真正面纱当下数字化建设浪潮中,很多项目将三维可视化、视频贴图叠加的数字孪生等同于空间智能。传统数字孪生更多停留在三维场景复刻,擅长把物理世界“画出来、展示出来”,…

2026/8/21 0:00:42 阅读更多 →
105、车载温度范围-40°C到85°C的影像质量一致性——ISP参数温漂补偿与产线标定策略

105、车载温度范围-40°C到85°C的影像质量一致性——ISP参数温漂补偿与产线标定策略

105、车载温度范围-40C到85C的影像质量一致性——ISP参数温漂补偿与产线标定策略 去年冬天在北方某车厂做A样评审,凌晨四点的黑河试验场,零下三十三度。客户拿了一台冷启动的车,中控屏上倒车影像全是雪花噪点,暗部细节直接糊成一片。我第一反应是sensor温度没上来,暗电流…

2026/8/21 0:00:42 阅读更多 →

周新闻

基于阿里云与通义千问(Qwen)构建AI应用:从模型调用到生产部署的完整实践指南

基于阿里云与通义千问(Qwen)构建AI应用:从模型调用到生产部署的完整实践指南

如果你是一名开发者,最近可能已经感受到了AI大模型正在从“玩具”变成“生产力工具”的强烈信号。从代码补全到智能Agent,从本地部署到云端API,我们正处在一个技术栈快速重构的节点。然而,面对层出不穷的模型、框架和工具&#xf…

2026/8/21 3:21:33 阅读更多 →
工业通信系统底层逻辑:04 反射——高频能量撞墙之后会发生什么?

工业通信系统底层逻辑:04 反射——高频能量撞墙之后会发生什么?

第四篇:反射——高频能量撞墙之后会发生什么? —— 你以为信号已经过去了,其实它正在回来打你 老Q的现场笔记 第五季,我们正式进入工业神经系统层。这里不再是单个设备的战斗,而是整个工厂“经脉”层面的秩序之战。从这一篇开始,你将第一次看清:看似简单的信号传播,背…

2026/8/21 0:02:09 阅读更多 →
【文章复现】非线性值迭代自适应动态规划(ADP):离散时间非线性系统的策略迭代自适应动态规划算法研究附Matlab代码

【文章复现】非线性值迭代自适应动态规划(ADP):离散时间非线性系统的策略迭代自适应动态规划算法研究附Matlab代码

✅作者简介:热爱科研的Matlab仿真开发者,擅长毕业设计辅导、数学建模、数据处理、建模仿真、程序设计、完整代码获取、论文复现及科研仿真。🍎 往期回顾关注个人主页:Matlab科研工作室👇 关注我领取海量matlab电子书和…

2026/8/21 6:07:56 阅读更多 →

月新闻

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南 【免费下载链接】BaiduNetdiskPlugin-macOS For macOS.百度网盘 破解SVIP、下载速度限制~ 项目地址: https://gitcode.com/gh_mirrors/ba/BaiduNetdiskPlugin-macOS 还在为百度网盘macOS版的龟速下…

2026/8/20 6:11:08 阅读更多 →
终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump 还在为网易云音乐下载的NCM格式文件无法在其他播放器播放而烦恼吗?ncmdump解密工具帮你轻松解决这个困…

2026/8/20 21:46:49 阅读更多 →
HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

AgentCard 智能体卡片:为英语学习 App 打造桌面级学习助手适用平台:HarmonyOS 7.0 (API 26 Beta)一、引言 HarmonyOS 7.0(API 26 Beta)新增了 AgentCard 智能体卡片能力,这是继 HMAF(鸿蒙智能体框架&#x…

2026/8/21 0:14:22 阅读更多 →