PowerManagerService 电源管理架构初识

Posted 法迪

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了PowerManagerService 电源管理架构初识相关的知识,希望对你有一定的参考价值。

1. 电源管理架构

PowerManagerService是android系统电源管理的核心服务。
PowerManagerService在Framework层本质为策略控制方案,其作用为:

  1. 向上提供给应用程序接口,例如音频场景中保持系统唤醒、消息通知中唤醒手机屏幕场景;
  2. 向下决策HAL层以及Kernel层来控制设备待机状态,控制显示屏、背光灯、距离传感器、光线传感器等硬件设备的状态;

2. 电源管理的4个层次

  • 应用接口层(PowerManager.java)
    PowerManager 中开发给应用一系列接口,应用可以调用PM的接口申请wakelock,让系统唤醒或休眠;
  • Framework层(PowerManagerService.java)
    应用调用PowerManager开发的接口的实现方为 PowerManagerService;
    PowerManagerService是整个电源管理的决策系统,例如亮灭屏、系统唤醒休眠;
  • Hal层(Power.c)
    该层只有一个power.c文件。通过接受上层参数,向/sys/power/wake_lock或/sys/power/wake_unlock文件节点写数据来与Kernel进行通信。主要功能为申请/释放锁,维持屏幕亮灭;
  • 内核层(Kernel/Power)
  1. 实现系统电源管理框架机制:Kernel/power/
  2. 实现对特定板的处理器电源管理:Arch/arm/match-xxx/pm.c
  3. 电源驱动为设备电源管理提供基础框架: Drivers/power

3. 电源管理服务 - PowerManagerService

frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
负责管理、协调设备电源管理的系统服务之一,设备中常见功能如亮灭屏、亮度调节、低电量模式、保持CPU唤醒都是通过PMS进行协调和处理。

3.1 SystemServer startService PowerManagerService

PowerManagerService 继承SystemSerive即具备生命周期, SystemService系的服务都是在SystemServer中启动、注册到系统服务中,通过Binder和其他组件进行交互

故我们先看下SystemServer的启动流程。
SystemServer.main
->SystemServer.run
->->SystemServer.startBootstrapServices() 启动引导服务
->->->mPowerManagerService = mSystemServiceManager.startService(PowerManagerService.class); // 通过Java的newInstance()反射启动电源管理服务,好处是代码很解耦

PowerManagerService DreamManagerService DisplayManagerService DisplayPowerController onStart():publishBinderService/publishLocalService onBootPhase():mDirty |= DIRTY_BOOT_COMPLETED systemReady():mDreamManager systemReady():mDisplayManagerInternal initPowerManagement PowerManagerService DreamManagerService DisplayManagerService DisplayPowerController

3.2 PowerManagerService 的构造函数

  1. 建立Handle: 消息发送与处理
  2. 2种WakeLock锁,分别为 mWakeLockSuspendBlocker 和 mDisplaySuspendBlocker
    a. mDisplaySuspendBlocker: 上层调用,控制屏幕的点亮和息屏
    b. mWakeLockSuspendBlocker: 系统内部调用,控制CPU的唤醒
PowerManagerService(Context context, Injector injector) 
    ...
    mHandlerThread = new ServiceThread(TAG,Process.THREAD_PRIORITY_DISPLAY, false /*allowIo*/);
    mHandlerThread.start();
    mHandler = injector.createHandler(mHandlerThread.getLooper(),new PowerManagerHandlerCallback());
    ...
    // 控制CPU的唤醒
    mWakeLockSuspendBlocker =mInjector.createSuspendBlocker(this, "PowerManagerService.WakeLocks");
    // 控制屏幕的点亮和息屏
    mDisplaySuspendBlocker =mInjector.createSuspendBlocker(this, "PowerManagerService.Display");
    ...


看到上述的2个锁,让我联系到经常使用的如下Flag
 Flag Value                 CPU        Screen      Keyboard
 PARTIAL_WAKE_LOCK            On           Off         Off 0x00000001 1
 SCREEN_DIM_WAKE_LOCK         On           Dim         Off 0x00000006 6
 SCREEN_BRIGHT_WAKE_LOCK      On           Bright      Off 0x0000000a 10
 FULL_WAKE_LOCK               On           Bright      Bright 0x0000001a 26

3.3 PowerManagerService.onStart()

Onstart的作用:

  1. 将 POWER_SERVICE 注册到 Binder 服务端,这样其他模块就可以通过Binder获取实例。同理当进行本地注册后,只有在Sysem进程才能获得到其实例。
  2. 设置 Watchdog 监视 PowerManagerService 和 Handler
      @Override
      public void onStart() 
          // 其他模块就可以通过Binder获取实例
          publishBinderService(Context.POWER_SERVICE, mBinderService, /* allowIsolated= */ false,DUMP_FLAG_PRIORITY_DEFAULT | DUMP_FLAG_PRIORITY_CRITICAL);
          // 当进行本地注册后,只有在Sysem进程才能获得到其实例
          publishLocalService(PowerManagerInternal.class, mLocalService);
  
          Watchdog.getInstance().addMonitor(this);
          Watchdog.getInstance().addThread(mHandler);
      

3.4 PowerManagerService.systemReady()

systemReady()函数作用:初始化工作,即我们可以看到电源管理策略的需要用的功能初始化。

  1. 初始化互动屏保
  2. 初始化屏幕显示管理服务
  3. 初始化电池管理服务
  4. 获取屏幕亮度信息
  5. 获取传感器管理服务
  6. 初始化电量统计服务
  7. SettingsProvier监听
  8. LED指示灯管理服务
  9. 初始化屏幕显示服务
  10. 广播相关:电池广播、用户切换广播
  11. 更新电源相关信息

例如我们在做冻结策略、场景识别策略的ROM定制也大体都是在 systemReady 进行init

public void systemReady(IAppOpsService appOps) 
    ...
    mDreamManager = getLocalService(DreamManagerInternal.class); // 1. 初始化互动屏保
    mDisplayManagerInternal = getLocalService(DisplayManagerInternal.class);// 2. 初始化屏幕显示管理服务
    mBatteryManagerInternal = getLocalService(BatteryManagerInternal.class);// 3. 初始化电池管理服务
    SensorManager sensorManager = new SystemSensorManager(mContext, mHandler.getLooper());//5. 获取传感器管理服务
    mBatteryStats = BatteryStatsService.getService();//6. 初始化电量统计服务
    mLightsManager = getLocalService(LightsManager.class);//8. LED指示灯管理服务
    mAttentionLight = mLightsManager.getLight(LightsManager.LIGHT_ID_ATTENTION);//8. LED指示灯管理服务
    mDisplayManagerInternal.initPowerManagement(mDisplayPowerCallbacks, mHandler, sensorManager);// 9. 初始化屏幕显示服务
    // 整个 PMS 中最重要的方法
    updatePowerStateLocked();// 11. 更新电源相关信息
    ...

总结一下:SystemReady完成的工作主要如下:

  1. 获取与PowerManangerService相关的系统服务以及本地服务;
  2. 获取屏幕最大、最小以及默认亮度值
  3. 创建SensorManager对象用于与SensorService交互
  4. 创建Notifier对象,用户通知系统电源状态的改变
  5. 调用DisplayManangerService的initPowerMannagerMent()方法来初始化Power显示模块
  6. 注册SettingsObserver监听系统设置的变化

3.5 PowerManager 的相关接口

PowerMananger 是PowerManangerService的代理类,PowerMananger向上层应用提供交互的接口。上层调用对应的接口后,具体的工作内容交予PowerManangerService完成。可以重点关注如下接口:

  1. wakeUp()
    hide接口,不开放给应用。作用:强制系统从睡眠状态唤醒。
  2. gotoSleep()
    hide接口,不开放给应用。作用:强制系统进入睡眠状态
  3. userActivity()
    向PowerManagerService报告影响系统休眠的用户活动,重新计算灭屏时间,背光亮度,例如触屏、滑屏、power键等用户行为。
  4. Wakelock(特别常用)
    wakelock是PowerManager的一个内部类,提供了相关的接口来操作wakelock锁。应用层可以使用newWakeLock方法创建wakelock锁,使用acquire()和release()来申请和释放锁。例如如下demo的使用
package com.sufadi.commlib.utils

import android.annotation.SuppressLint
import android.content.Context
import android.os.PowerManager

/*
 Flag Value                 CPU        Screen      Keyboard
 PARTIAL_WAKE_LOCK            On           Off         Off 0x00000001 1
 SCREEN_DIM_WAKE_LOCK         On           Dim         Off 0x00000006 6
 SCREEN_BRIGHT_WAKE_LOCK      On           Bright      Off 0x0000000a 10
 FULL_WAKE_LOCK               On           Bright      Bright 0x0000001a 26
 */
object AlertWakeLock 
    private val TAG = "AlertWakeLock"
    private var sCpuWakeLock: PowerManager.WakeLock? = null

    @SuppressLint("InvalidWakeLockTag")
    internal fun createPartialWakeLock(context: Context): PowerManager.WakeLock? 
        // 第一步:获取PowerManager的实例
        val pm = context.getSystemService(Context.POWER_SERVICE) as PowerManager ?: return null
        // 第二步:调用PowerManager中的newWakeLock方法创建一个WakeLock对象
        return pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, TAG)
        //return pm.newWakeLock(PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP | PowerManager.ON_AFTER_RELEASE, TAG);
    

    fun acquireCpuWakeLock(context: Context) 
        if (sCpuWakeLock != null) 
            return
        

        sCpuWakeLock = createPartialWakeLock(context)
        // 第三步:acquire()获取相应的锁
        sCpuWakeLock!!.acquire()
    

    fun releaseCpuLock() 
        if (sCpuWakeLock != null) 
            // 最后:release释放
            sCpuWakeLock!!.release()
            sCpuWakeLock = null
        
    

  1. isDeviceIdleMode
    查看是否进入了Doze低功耗模式

Android N DisplayManager服务解析

PowerManagerService:负责协调设备上电源管理功能的服务。

DisplayPowerController:控制屏幕显示相关的电源状态。处理距离传感器、光线传感器和屏幕关闭时的动画等。这个组件在其他电源管理服务中是独立的,也就是说它不会共享任何状态,而只是通过异步回调来通知其他电源管理模块某些状态已经改变。这个类在内部做的一切都是被序列化的,尽管它可能被来自外部的其他线程访问。

DisplayManagerService:它管理显示的整个生命周期,决定怎样基于当前的物理显示设备来配置逻辑显示,并且当状态改变时发生通知给系统和应用。为了发现和配置依附于系统的一系列物理显示设备,DMS依赖于一系列 DisplayAdapter 组件。根据设备的不同分为不同的显示适配器:一个显示适配器用于内置的本地显示器;one for simulated non-functional displays when the system is headless;one for simulated overlay displays used for development;一个用于WiFi显示。通过注册的 DisplayAdapter.Listener ,适配器来和 DMS 异步地交流显示设备的状态。这里有两个主要的原因。 首先它很好的封装了两个类的职责:显示适配器处理各个显示设备,显示管理服务处理全局状态。其次,它消除了异步的查找显示设备时导致的死锁。

DisplayPowerState:控制显示状态。当属性改变的时候,该组件以统一的顺序发生一个回调以应用这些改变。这个组件必须且只能被属于 DPC 的 Looper 线程来创建和访问。

在PMS的systemReady方法中,会初始化各种组件,其中就包括这里的DMI,也就是DisplayManagerInternal,它位于hardware包下,作为显示管理的本地服务借口,而DMS等处于server包下,LocalService就继承于它,而DMS本身继承于SystemService。这SS是运行在系统进程中的用于server的基础类,负责提供了相关的生命周期和回调。

回调到DisplayManagerService LocalService.initPowerManagement
DMS.requestGlobalDisplayStateInternal -> applyGlobalDisplayStateLocked -> updateDisplayStateLocked

LocalDisplayDevice.requestDisplayStateLocked

以下都是在requestDisplayStateLocked返回的Runnable中调用的:
SurfaceControl.setDisplayPowerMode 调到native层
mBacklight.setBrightness

DisplayPowerController

DPC控制屏幕显示相关的电源状态,包括距离传感器和光线传感器等。

这个类比较多庞大,我们逐步来看,首先是构造方法。在这里对它所持有的对象进行了初始化,包括以下内容:

mHandler,内部持有的DisplayControllerHandler,用于分发事件。
mCallbacks,
mBatteryStates,
mSensorManager
mWindowManagerPolicy
mBlanker

调节屏幕电源状态,这里指的是屏幕状态,之后会通过mHandler来发送一条异步的MSG_UPDATE_POWER_STATE消息。

    /**
     * Requests a new power state.
     * The controller makes a copy of the provided object and then
     * begins adjusting the power state to match what was requested.
     *
     * @param request The requested power state.
     * @param waitForNegativeProximity If true, issues a request to wait for
     * negative proximity before turning the screen back on, assuming the screen
     * was turned off by the proximity sensor.
     * @return True if display is ready, false if there are important changes that must
     * be made asynchronously (such as turning the screen on), in which case the caller
     * should grab a wake lock, watch for @link DisplayPowerCallbacks#onStateChanged()
     * then try the request again later until the state converges.
     */
    public boolean requestPowerState(DisplayPowerRequest request,
            boolean waitForNegativeProximity) 
        if (DEBUG) 
            Slog.d(TAG, "requestPowerState: "
                    + request + ", waitForNegativeProximity=" + waitForNegativeProximity);
        

        synchronized (mLock) 
            boolean changed = false;

            if (waitForNegativeProximity
                    && !mPendingWaitForNegativeProximityLocked) 
                mPendingWaitForNegativeProximityLocked = true;
                changed = true;
            

            if (mPendingRequestLocked == null) 
                mPendingRequestLocked = new DisplayPowerRequest(request);
                changed = true;
             else if (!mPendingRequestLocked.equals(request)) 
                mPendingRequestLocked.copyFrom(request);
                changed = true;
            

            if (changed) 
                mDisplayReadyLocked = false;
            

            if (changed && !mPendingRequestChangedLocked) 
                mPendingRequestChangedLocked = true;
                sendUpdatePowerStateLocked();
            

            return mDisplayReadyLocked;
        
    

接下来就看看对于MSG_UPDATE_POWER_STATE消息是如何处理的,在handler中直接调用了updatePowerState()方法。

initialize() 为默认显示设备初始化电源状态,包括根据屏幕状态和亮度反馈给电源Line 508

根据mPowerRequest.policy来设置state和brightness

对距离传感器做相应的操作

执行屏幕状态变化的动画

息屏时亮度设为BRIGHTNESS_OFF

判断和使用自动亮度

boost这个没有理解,再看看源码,

再分别对自动亮度和手动亮度调节做处理

如果低电量模式开启,在亮度的阀值之上,对亮度进行减半

在屏幕点亮状态或休眠时,animate屏幕亮度。如果是息屏、挂起,或者从VR状态转入转出时,跳过动画。

判断对于新的状态请求,显示设备是否就绪

通知policy屏幕已经点亮,真正执行时在mWindowManagerPolicy.screenTurnedOn()

获取锁、通知状态、释放锁


DisplayPowerState

该组件用于控制显示状态,当属性改变的时候,其以统一的顺序将这些改变回调出去。这个组件只能被DisplayPowerController的Looper线程来创建和访问。

DisplayPowerState主要负责设置屏幕状态和屏幕亮度等。这里的dozing也是一种屏幕状态,它是指在低电量模式下,屏幕的休眠状态,但是此时仍然是亮屏的,这是为了让屏幕在没有发生交互时而显示内容的一种优化(其中又分为DOZE和DOZE_SUSPEND两种状态,后者能够实现always-on等功能)。

    /**
     * Sets whether the screen is on, off, or dozing.
     */
    public void setScreenState(int state) 
        if (mScreenState != state) 
            if (DEBUG) 
                Slog.d(TAG, "setScreenState: state=" + state);
            

            mScreenState = state;
            mScreenReady = false;
            scheduleScreenUpdate();
        
    

    /**
     * Sets the display brightness.
     *
     * @param brightness The brightness, ranges from 0 (minimum / off) to 255 (brightest).
     */
    public void setScreenBrightness(int brightness) 
        if (mScreenBrightness != brightness) 
            if (DEBUG) 
                Slog.d(TAG, "setScreenBrightness: brightness=" + brightness);
            

            mScreenBrightness = brightness;
            if (mScreenState != Display.STATE_OFF) 
                mScreenReady = false;
                scheduleScreenUpdate();
            
        
    

其中最重要的就是这个scheduleScreenUpdate方法,它会去在mPhotonicModulator中异步地设置屏幕状态和亮度。

    private final Runnable mScreenUpdateRunnable = new Runnable() 
        @Override
        public void run() 
            mScreenUpdatePending = false;

            int brightness = mScreenState != Display.STATE_OFF
                    && mColorFadeLevel > 0f ? mScreenBrightness : 0;
            if (mPhotonicModulator.setState(mScreenState, brightness)) 
                if (DEBUG) 
                    Slog.d(TAG, "Screen ready");
                
                mScreenReady = true;
                invokeCleanListenerIfNeeded();
             else 
                if (DEBUG) 
                    Slog.d(TAG, "Screen not ready");
                
            
        
    ;

这个PhotonicModulator线程在DPS的构造函数中就已经start了,始终在运行着。那么对于一次setState操作,这个现场里究竟会发生什么呢?

1.在setState之后,该方法就会获取到mLock,这样在run中只会走到for循环里,但不会进到synchronized里面的代码中。
2.在setState中,首先会判断屏幕状态和背光是否发生改变,如果是就继续往下走,将这两个值赋给现场内部的mPending*,这个会在run()中使用。
3.判断状态是否都在改变中,判断完后分别进行赋值,如果不在就调用mLock.notifyAll()通知现场可以进行修改了。然后setState方法返回false,因为还没有设置完毕。
4.此时就轮到run方法中获取mLock了,在这里首先state和backlight会获取到来自setState中存储到线程内的值,如果设置的状态和实际的状态不一样,就不会将InProgress的值设为false,因为现在正是要进行修改。
5.然后就会去调用mBlander去设置屏幕状态和背光。
6.在屏幕状态和背光都设置好之后,因为是run方法,for循环还得继续,此时因为值没有变化,不用修改,所以就会去wait。

        public boolean setState(int state, int backlight) 
            synchronized (mLock) 
                boolean stateChanged = state != mPendingState;
                boolean backlightChanged = backlight != mPendingBacklight;
                if (stateChanged || backlightChanged) 
                    if (DEBUG) 
                        Slog.d(TAG, "Requesting new screen state: state="
                                + Display.stateToString(state) + ", backlight=" + backlight);
                    

                    mPendingState = state;
                    mPendingBacklight = backlight;

                    boolean changeInProgress = mStateChangeInProgress || mBacklightChangeInProgress;
                    mStateChangeInProgress = stateChanged;
                    mBacklightChangeInProgress = backlightChanged;

                    if (!changeInProgress) 
                        mLock.notifyAll();
                    
                
                return !mStateChangeInProgress;
            
        
        @Override
        public void run() 
            for (;;) 
                // Get pending change.
                final int state;
                final boolean stateChanged;
                final int backlight;
                final boolean backlightChanged;
                synchronized (mLock) 
                    state = mPendingState;
                    stateChanged = (state != mActualState);
                    backlight = mPendingBacklight;
                    backlightChanged = (backlight != mActualBacklight);
                    if (!stateChanged) 
                        // State changed applied, notify outer class.
                        postScreenUpdateThreadSafe();
                        mStateChangeInProgress = false;
                    
                    if (!backlightChanged) 
                        mBacklightChangeInProgress = false;
                    
                    if (!stateChanged && !backlightChanged) 
                        try 
                            mLock.wait();
                         catch (InterruptedException ex)  
                        continue;
                    
                    mActualState = state;
                    mActualBacklight = backlight;
                

                // Apply pending change.
                if (DEBUG) 
                    Slog.d(TAG, "Updating screen state: state="
                            + Display.stateToString(state) + ", backlight=" + backlight);
                
                mBlanker.requestDisplayState(state, backlight);
            
        

以上是关于PowerManagerService 电源管理架构初识的主要内容,如果未能解决你的问题,请参考以下文章

PowerManagerService的启动流程

深入了解PowerManagerService之Android 11.0 Power 键亮屏灭屏流程分析

Android 电源管理相关逻辑之PMS

Android N DisplayManager服务解析

Android N DisplayManager服务解析

如何操作一架客运飞机?