android6.0系统 PowerManager深入分析(非常详细)

2019-07-14 00:41发布

概述 一直以来,电源管理是电子产品设计中非常重要的环节,也是任何电子设备中最为重要的系统模块之一,优秀的电源管理方案,能够提供持久的续航能力,良好的用户体验,更能提升电子产品的竞争力。 移动设备的电量主要由两种元件消耗:CPU和显示屏,因此设法降低这两种元件的耗电量就是电源管理的关键,为移动设备设计的CPU大多有两种工作频率,为了省电,大部分时间内cpu都工作在降低频率下,只有进行密集计算时,如视频解码才会切换到高频状态,而显示屏省电的方法是尽量减少亮屏时间,但是显示屏的开关和应用有很大的关系,因此系统中需要有一套机制来控制显示屏的开关和亮度,这也是电源管理的主要工作。  

电源管理架构

Android的电源管理主要是通过wakelock机制管理系统的状态,整个android电源管理,可以分为四个层次:应用接口层(PowerManager.Java),Framework层(PowerManagerService.java),HAL层(Power.c),和内核层(kernel/Power)。 应用接口层:PowerManager中开放给应用一系列接口,应用可以调用PM的接口申请wakelock,唤醒系统,使系统进入睡眠等操作; Framework层:应用调用PowerManager开放的接口,来对系统进行一些列的操作是在PowerManagerService中完成的,PowerManagerService计算系统中和Power相关的计算,是整个电源管理的决策系统。同时协调Power如何与系统其它模块的交互,比如亮屏,暗屏,系统睡眠,唤醒等等。 HAL层:该层只有一个power.c文件,该文件通过上层传下来的参数,向/sys/power/wake_lock或者/sys/power/wake_unlock文件节点写数据来与kernel进行通信,主要功能是申请/释放锁,维持屏幕亮灭   Kernel层:内核层实现电源管理的方案主要包含三个部分: 1、Kernel/power/:实现了系统电源管理框架机制。 2、Arch/arm(ormips or powerpc)/mach-XXX/pm.c:实现对特定板的处理器电源管理。 3、drivers/power:是设备电源管理的基础框架,为驱动提供了电源管理接口。   Android电源管理框架如下图      

电源管理服务——PowerManagerService

PowerManagerServcie是android系统电源管理的核心服务,它在Framework层建立起一个策略控制方案,向下决策HAL层以及kernel层来控制设备待机状态,控制显示屏,背光灯,距离传感器,光线传感器等硬件设备的状态。向上提供给应用程序相应的操作接口,比如听音乐时持续保持系统唤醒,应用通知来临唤醒手机屏幕等场景  

启动过程

  SystemServer在系统启动的时候会启动三类服务:引导关键服务,核心服务,其他服务;PowerManagerService是在SystemServer中创建的,并将其作为一个系统服务加入到ServiceManager中: mPowerManagerService = mSystemServiceManager.startService(PowerManagerService.class);   在启动引导关键服务调用startBootstrapServices(),其中各种服务都是通过SystemServiceManager中的startService()函数来启动: public extends SystemService> T startService(Class serviceClass) { final String name = serviceClass.getName();  final T service;             Constructor constructor = serviceClass.getConstructor(Context.class);             service = constructor.newInstance(mContext);   mServices.add(service);//注册服务到服务列表中去    service.onStart();//启动服务 } 在启动PowerManagerService时,传入的参数类是PowerManagerService,在startService()中首先调用PowerManagerService的构造函数,然后调用其onStart()函数 PowerManagerServcie的构造函数: mHandler = new PowerManagerHandler(mHandlerThread.getLooper());

synchronized (mLock) {
mWakeLockSuspendBlocker = createSuspendBlockerLocked("PowerManagerService.WakeLocks");
mDisplaySuspendBlocker = createSuspendBlockerLocked("PowerManagerService.Display");
mDisplaySuspendBlocker.acquire();
mWakefulness = WAKEFULNESS_AWAKE;
nativeInit();
创建一个处理消息和发送消息的线程mHandler,并且两种标志flag的wakelock锁PowerManagerService.WakeLocksPowerManagerService.Display,前者是传入到底层是控制cpu唤醒状态,后者则是控制屏幕亮灭。在构造函数最后调用nativeInit();在native层初始化相关资源。将mWakefulness 置成WAKEFULNESS_AWAKE状态,mWakefulness 标识系统当前状态共有四种定义: WAKEFULNESS_ASLEEP:表示系统当前处于休眠状态,只能被wakeUp()调用唤醒。 WAKEFULNESS_AWAKE:表示系统目前处于正常运行状态。 WAKEFULNESS_DREAMING:表示系统当前正处于屏保的状态。 WAKEFULNESS_DOZING:表示系统正处于“doze”状态。这种状态下只有低耗电的“屏保”可以运行,其他应用进程都被挂起。   在SystemServer中startService中调用到PowerManagerService构造函数做完初始化操作之后便会调用PowerManagerService的onStart()函数: publishBinderService(Context.POWER_SERVICE, new BinderService()); publishLocalService(PowerManagerInternal.classnew LocalService());         Watchdog.getInstance().addMonitor(this);         Watchdog.getInstance().addThread(mHandler);   Onstart完成的工作就是将POWER_SERVICE作为Binder的服务端,注册到SystemService中去;将PowerManagerInternal注册到本地服务中,将自己加到watchdog的监控队列中去;将之前在构造函数中创建的mHandler对象加入到watchdog的中,用于监视mHandler的looper是否空闲;

系统准备工作

SystemServer在调用PowerManagerService之后还会调用其SystemReady相当于在系统准备就绪后对PowerManagerService再进行一些初始化工作。SystemReady()方法代码如下: mAppOps = appOps; mDreamManager = getLocalService(DreamManagerInternal.class); mDisplayManagerInternal = getLocalService(DisplayManagerInternal.class); mPolicy = getLocalService(WindowManagerPolicy.class); mBatteryManagerInternal = getLocalService(BatteryManagerInternal.class); PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); mScreenBrightnessSettingMinimum = pm.getMinimumScreenBrightnessSetting(); mScreenBrightnessSettingMaximum = pm.getMaximumScreenBrightnessSetting(); mScreenBrightnessSettingDefault = pm.getDefaultScreenBrightnessSetting();   获取与PowerManager相关的本地服务,比如屏保(mDreamManager),屏幕显示(mDisplayManagerInternal),窗口策略(mPolicy),电池电量(mBatteryManagerInternal)等服务,然后初始化屏幕最大亮度,最小亮度,和默认亮度;   SensorManager sensorManager = new SystemSensorManager(mContextmHandler.getLooper()); mBatteryStats = BatteryStatsService.getService(); mNotifier new Notifier(Looper.getMainLooper(), mContextmBatteryStats,
        mAppOps, createSuspendBlockerLocked("PowerManagerService.Broadcasts"),
        mPolicy); mWirelessChargerDetector new WirelessChargerDetector(sensorManager,
        createSuspendBlockerLocked("PowerManagerService.WirelessChargerDetector"), mHandler); mSettingsObserver new SettingsObserver(mHandler); mLightsManager = getLocalService(LightsManager.class); mAttentionLight mLightsManager.getLight(LightsManager.LIGHT_ID_ATTENTION); 创建sensorManager 对象,用于与sensor交互,比如距离传感器,光线传感器,加速度传感器(doze上使用)。获取电池状态服务,和背光服务; 创建mNotifier 对象,在通过mNotifier 发送通知时候,会传入底层申请PowerManagerService.Broadcasts的wakelock锁。 创建mSettingsObserver 监听系统设置变化,比如亮屏时间,自动背光,屏幕亮度,屏保,低电模式等等   总而言之在SystemReady方法中完成的主要工作如下: 获取与PowerManagerServcie相关的系统服务以及本地服务; 获取屏幕最大,最小以及默认亮度值; 创建SensorManager 对象,用于和SensorService交互; 创建Notifier对象,用于通知系统中电源状态的改变; 创建WirelessChargerDetector对象,用于检测无线充电的传感器(市面上支持的手机较少) 调用DisplayManagerService的initPowerManagement()方法来初始化Power显示模块。 注册SettingsObserver监听系统设置的变化   PowerManagerServcie的启动初始化过程如下:    

相关接口

PowerManager向应用提供了相应的接口,以供应用程序调用,来改变系统待机状态,屏幕状态,屏幕亮度等,PowerManager是PowerManagerService的代理类,PowerManager向上层应用提供交互的接口,具体的处理工作在PowerManagerService中完成。下面介绍PowerManager中提供的相应接口作用: Wakeup():强制系统从睡眠状态唤醒,此接口对应用是不开放的,应用想唤醒系统必须通过设置亮屏标志(后面即将讲到); gotoSleep():强制系统进入到睡眠状态,此接口也是应用不开放。 userActivity():向PowerManagerService报告影响系统休眠的用户活动,重计算灭屏时间,背光亮度等,例如触屏,划屏,power键等用户活动; Wakelock:wakelock是PowerManager的一个内部类,提供了相关的接口来操作wakelock锁,比如newWakeLock()方法来创建wakelock锁,acquire()和release()方法来申请和释放锁。 isDeviceIdleMode():返回设备当前的状态,如果处于Idle状态,则返回true,Idle状态是在手机长时间没有被使用以及没有运动的情况下,手机进入到一种Doze低功耗的模式下,这种状态下手机可能会关掉网络数据访问,可以通过监视DEVICE_IDLE_MODE_CHANGED这个广播信息,来监控手机状态的改变

唤醒——wakeup

PowerManager的wakeup接口属性是@hide的,所以对于上层应用是不可见的,上层应用要唤醒系统大都依靠两种方式:1.在应用启动Activity时候设置相应的window的flags,通过WMS来唤醒系统;2.在应用申请wakelock锁时附带ACQUIRE_CAUSES_WAKEUP标志; Wakeup流程如下图所示   PowerManager的wakeup接口,可供应用程序调用,来强制唤醒系统,如果该设备处于睡眠状态,调用该接口会立即唤醒系统,比如按Power键,来电,闹钟等场景都会调用该接口。唤醒系统需要android.Manifest.permission#DEVICE_POWER的权限; 我们来看看PowerManagerServcie中wakeup接口的代码: mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWERnull); final int uid = Binder.getCallingUid(); final long ident = Binder.clearCallingIdentity();   wakeUpInternal(eventTime, reason, uid, opPackageName, uid);   Wakeup接口中仅仅是对调用者的权限进行检查;然后放到wakeUpInternal()中处理,wakeUpInternal()中没有做操作,只是调用wakeUpNoUpdateLocked()函数,然后更新调用updatePowerStateLocked()更新电源状态 wakeUpNoUpdateLocked()关键代码  mLastWakeTime = eventTime;
    setWakefulnessLocked(WAKEFULNESS_AWAKE0);

    mNotifier.onWakeUp(reason, reasonUid, opPackageName, opUid);
    userActivityNoUpdateLocked(
            eventTime, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, reasonUid);   return true; setWakefulnessLocked()函数将mWakefulness赋值为wakefulness和mDirty |= DIRTY_WAKEFULNESS;这两个标志在后面更新电源状态时有重要作用,同时调用到mNotifier中的onWakefulnessChangeStarted调用到handleEarlyInteractiveChange调用到PhoneWindowManager的startedWakingUp函数,来通知到PhoneWindowManager屏幕开始启动; 调用mNotifier向系统中通知系统被唤醒; 更新用户活动,将mDirty |= DIRTY_USER_ACTIVITY置位;来重新计算亮屏时间。   在updatePowerStateLocked()中更新电源状态,updatePowerStateLocked为PowerManagerService的核心函数,后面会详细介绍,这里简单介绍在wakeup中的流程。在updatePowerStateLocked()中的updateDisplayPowerStateLocked()函数中将mDisplayPowerRequest.policy设置成POLICY_BRIGHT;然后调用: mDisplayReady mDisplayManagerInternal.requestPowerState(mDisplayPowerRequest,mRequestWaitForNegativeProximity);   通过调用到DisplayManagerService中对屏幕状态作出相应的改变,通过去与WindowManagerService执行亮屏之前的屏幕绘制过程,与LightServcie交互来点亮屏幕背光灯。其调用过程和具体逻辑在亮屏流程文档中有详细分析。    

睡眠——goToSleep

    PowerManager的gotoSleep()接口是@hide属性,因此对于上层应用是不开放的,设备强制进入睡眠状态,在处理一些灭屏按键事件时,会通过WMS来调用PowerManager的gotoSleep接口,一般在系统一段时间没有被操作时,系统将会自动调用gotoSleep函数,让其进入到睡眠模式;     与wakeup唤醒一样,PowerManager的gotoSleep()在PowerManagerService中处理,PMS中的gotoSleep()首先检查调用者是否拥有android.Manifest.permission.DEVICE_POWER权限。然后调用到goToSleepInternal()中处理:           if (goToSleepNoUpdateLocked(eventTime, reason, flags, uid)) {
            updatePowerStateLocked();
        
在goToSleepNoUpdateLocked()中完成发送了将要休眠的通知,然后修改了Wakefulness,将其置成WAKEFULNESS_DOZING,将mDirty |= DIRTY_WAKEFULNESS置位,更多的实际工作在updatePowerStateLocked()中完成。在updateDreamLocked中完成真正进入睡眠的过程; 其调用过程大致如下:
  在reallyGoToSleepNoUpdateLocked中将mWakefulness置成WAKEFULNESS_ASLEEP,在updateWakeLockSummaryLocked中有如下: if (mWakefulness == WAKEFULNESS_ASLEEP
        || (mWakeLockSummary WAKE_LOCK_DOZE) != 0) {
    mWakeLockSummary &= ~(WAKE_LOCK_SCREEN_BRIGHT WAKE_LOCK_SCREEN_DIM
            WAKE_LOCK_BUTTON_BRIGHT); mWakeLockSummary列表中的wakelock锁所形成的集合变量mWakeLockSummary 中将WAKE_LOCK_SCREEN_BRIGHT,WAKE_LOCK_SCREEN_DIM,WAKE_LOCK_BUTTON_BRIGHT三种wakelock锁置为无效,再次调用updatePowerStateLocked更新电源状态时候,会在updateDisplayPowerStateLocked中做灭屏操作,其流程与wakeup唤醒系统亮屏操作流程大致一样。  

用户活动——userActivity

userActivity()接口用于用户进程向PowerManagerService报告用户影响系统休眠的活动。例如, 用户点击屏幕时,系统会调用该方法来告诉PowerManagerService用户点击的时间,这样PowerManagerService将更新内部保存的时间值,从而推迟系统休眠的时间。userActivity()方法主要通过调用内部的userActivityInternal()方法来完成工作 userActivityInternal()中并没有做任何操作,仅仅是将mLastUserActivityTime 更新为当前event的时间eventTime,mDirty |= DIRTY_USER_ACTIVITY;置位操作。具体操作仍然是在PowerManagerService中的核心函数updatePowerStateLocked()中完成; 在updateUserActivitySummaryLocked()中 final int sleepTimeout = getSleepTimeoutLocked();
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;
        }
    } 重新计算睡眠超时时间,灭屏超时时间,暗屏超时时间,将mUserActivitySummary 置为0,通过计算上一次的用户事件时间与超时时间作对比,来判断将屏幕置为亮屏 (USER_ACTIVITY_SCREEN_BRIGHT)还是暗屏(USER_ACTIVITY_SCREEN_DIM),前提是手机处于非睡眠状态。 if (mUserActivitySummary != && nextTimeout >= 0) {
    Message msg = mHandler.obtainMessage(MSG_USER_ACTIVITY_TIMEOUT);
    msg.setAsynchronous(true);
    mHandler.sendMessageAtTime(msg, nextTimeout);
} 如果时间还没到,则返回发送一个MSG_USER_ACTIVITY_TIMEOUT的定时消息,当处理时间到了,会在消息的处理方法handleUserActivityTimeout中重新调用updatePowerStateLocked()电源状态。再次调用