码迷,mamicode.com
首页 > 移动开发 > 详细

android4.4 PowerManagerService流程分析

时间:2015-08-04 15:48:25      阅读:452      评论:0      收藏:0      [点我收藏+]

标签:android4.4   电源管理   powermanger   service   powermanagerservice   

这几日,闲来无事,想研究一下android Powermanager机制,之前也通过网络的论坛之类文章有了一定的了解,此一番本打算是重温旧梦,结果发现4.4的这一块代码较之之前的android低版本还是有较大变动的,于是,将系统休眠或唤醒的流程跟了一遍,现将自己的理解分析如下:

首先我们来一个追本溯源,看看这个service是在哪里启动,如何起来的,我们知道系统的启动顺序是uboot->kernel->android,而kernel完成硬件设备的初始化以及系统资源的初始化,分配管理等工作后,最终会启动根目录下的init程序,此程序对于android来说算是鼻祖了,此文件解析各种*.rc文件,设置系统属性,启动service,有些service是在此时起来的,而有些是在zygote起来之后,fork()出system进程,system进程再将service注册至SM(servicemanager)而后启动的,本文所述的powermanagerservice(后文PMS)就是属于后者,起于system进程。PowerManagerService在init里面会启动一个HandlerThread一个后台消息循环来提供任务的延迟发送,就可以使用Handler来在定制推迟某一任务的执行时间,从而实现状态机的循环(此处涉及watchdog机制,有兴趣的可做延伸学习)。

其次,系统进入休眠模式一般有三种渠道,其一,用户持有window且无任何操作即为超时,则屏幕先会切到Dim,再过几秒钟就会全部变暗,背光完全关闭;其二,用户特有动作触发休眠模式,比如PowerKey操作;其三,应用程序直接调用PMS的goToSleep方法,直接强制系统休眠。相比较而言,第一种的代码调用流程无非是watchdog一直监控调用PMS的handlerlooper查看当前window有多长时间未操作,是否超时,近乎比较自然的休眠模式,整个过程行云流水基本都是在framework中完成,最终通过native调用hal接口,操作底层,呈现相应状态给用户(开屏,关屏等);后两种渠道敝人认为流程有部分是一致的,区别在于第二种是从底层捕获的事件,而后分析再向下设置,第二种最终还是会调用goToSleep接口,因此殊途同归了。

下面我们先从第二种说起:

系统启动之初,InputManagerService开始监控底层的所有输入事件,其开启了两个主要的线程,一者为InputReader,二者为InputDispatcher;两者协调合作,可谓不亦乐乎,前者是底层input事件的直属首脑,所有input事件均通过前者捕获,后者则会将事件做粗略解析,判断出上层谁关心这个input事件,于是会将其post给相应的用户处理。

上代码:

inputReader.cpp                                 KeyboardInputMapper::processKey

                                                                       getDispatcher()->notifyKey

inputDispacher.cpp                            InputDispatcher::notifyKey

                                                                   mPolicy->interceptKeyBeforeQueueing

com_android_server_inputManager.cpp                NativeInputManager::interceptKeyBeforeQueueing

InputManager.java                                               interceptKeyBeforeQueueing

                                                                                  mWindowManagerService.mInputMonitor.interceptKeyBeforeQueueing

WindowmanagerService.java                             InputMonitor::interceptKeyBeforeQueueing

                                                                                  mPolicy.interceptKeyBeforeQueueing

PhonewindowManager.java                                interceptKeyBeforeQueueing   

                                                                                     //同上面的分析,此处返回的action是被或上了ACTION_GO_TO_SLEEP的(见1975行对KeyEvent.KEYCODE_POWER的处理).......一级一级的返回后.....

 com_android_server_inputManager.cpp                               NativeInputManager::interceptKeyBeforeQueueing      //返回值中含有gotosleep的flag,故走到gotosleep分支,

此处提一句,4.4给了IMS不小的权利,其将key事件传递给WMS处理之后,WMS会将返回值传递给IMS,后者赋值给wmActions,后续IMS会调用handleInterceptActions方法做相应操作,是sleep,wakeup,还是passuser。此时,我们的逻辑是走了sleep的情况,继而调用android_server_PowerManagerService_goToSleep(when),注意此方法的妙用,此方法直击PMS的goToSleepFromNative,接着调用updatePowerStateLocked(),下面细细分析一下该方法:

updatePowerStateLocked

技术分享
 1     /**
 2      * Updates the global power state based on dirty bits recorded in mDirty.
 3      *
 4      * This is the main function that performs power state transitions.
 5      * We centralize them here so that we can recompute the power state completely
 6      * each time something important changes, and ensure that we do it the same
 7      * way each time.  The point is to gather all of the transition logic here.
 8      */
 9     private void updatePowerStateLocked() {
10         if (!mSystemReady || mDirty == 0) {//如果系统没有准备好,或者power state没有发生任何变化,这个方法可以不用执行的
11             return;
12         }
13 
14         // Phase 0: Basic state updates.
15         updateIsPoweredLocked(mDirty);//更新电池以及充电模式状态变化
16         updateStayOnLocked(mDirty);
17 
18         // Phase 1: Update wakefulness.
19         // Loop because the wake lock and user activity computations are influenced
20         // by changes in wakefulness.
21         final long now = SystemClock.uptimeMillis();
22         int dirtyPhase2 = 0;
23         for (;;) {
24             int dirtyPhase1 = mDirty;
25             dirtyPhase2 |= dirtyPhase1;
26             mDirty = 0;
27 
28             updateWakeLockSummaryLocked(dirtyPhase1);//在前面解释几个变量的时候,就已经提到了WakeLockSummary和UserActivitySummary29             updateUserActivitySummaryLocked(now, dirtyPhase1);//在这里的两个方法中已经开始用到了。 想必通过方法名,大概也已经有所了解其功能了。
30             if (!updateWakefulnessLocked(dirtyPhase1)) {
31                 break;
32             }
33         }
34 
35         // Phase 2: Update dreams and display power state.
36         updateDreamLocked(dirtyPhase2);
37         updateDisplayPowerStateLocked(dirtyPhase2);
38 
39         // Phase 3: Send notifications, if needed.
40         if (mDisplayReady) {
41             sendPendingNotificationsLocked();
42         }
43 
44         // Phase 4: Update suspend blocker.
45         // Because we might release the last suspend blocker here, we need to make sure
46         // we finished everything else first!
47         updateSuspendBlockerLocked();
48     }
技术分享

从这段代码中,很容易就看出,这个方法对于power state的更新时分成四个阶段进行的。从注释中看到,第一阶段: 基本状态的更新;第二阶段:显示内容的更新; 第三阶段:dream和display状态的更新;第四阶段:suspend blocker的更新。之所以放在最后一步才进行suspend blocker的更新,是因为在这里可能会释放suspend blocker。

第一阶段,updateIsPoweredLocked(mDirty),开始,这个方法的功能是判断设备是否处于充电状态中,如果DIRTY_BATTERY_STATE发生了变化,说明设备的电池的状态有过改变,然后通过对比和判断(通过电池的状态前后的变化和充电状态的变化来判断),确定是否处于在充电,充电方式的改变也会在mDirty中标记出来。根据充电状态的变化进行一些相应的处理,同时是否在充电或者充电方式的改变都会认为是一次用户事件或者叫用户活动的发生。

updateStayOnLocked用来更新device是否开启状态。也是通过mStayOn的前后变化作为判断依据,如果device的属性Settings.Global.STAY_ON_WHILE_PLUGGED_IN为置位,并且没有达到电池充电时持续开屏时间的最大值(也就是说,在插入电源后的一段时间内保持开屏状态),那么mStayOn为真。上面这两个方法完成了第一阶段的更新,通过代码我们可以看到,主要是进行了充电状态的判断,然后根据充电的状态更新了一些必要的属性的变化,同时也在更新mDirty。后续是更新wakelock和useractivity的状态,之后是否退出for循环的关卡:updateWakefulnessLocked,该方法返回false,跳出for循环,后面我们看一下这个方法的实现:

private boolean updateWakefulnessLocked(int dirty) {
11         boolean changed = false;
12         if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_USER_ACTIVITY | DIRTY_BOOT_COMPLETED
13                 | DIRTY_WAKEFULNESS | DIRTY_STAY_ON | DIRTY_PROXIMITY_POSITIVE
14                 | DIRTY_DOCK_STATE)) != 0) {
15             if (mWakefulness == WAKEFULNESS_AWAKE && isItBedTimeYetLocked()) {
16                 if (DEBUG_SPEW) {
17                     Slog.d(TAG, "updateWakefulnessLocked: Bed time...");
18                 }
19                 final long time = SystemClock.uptimeMillis();
20                 if (shouldNapAtBedTimeLocked()) {
21                     changed = napNoUpdateLocked(time);
22                 } else {
23                     changed = goToSleepNoUpdateLocked(time,
24                             PowerManager.GO_TO_SLEEP_REASON_TIMEOUT);
25                 }
26             }
27         }
28         return changed;
29     }
我们看到,如果设备现在是wakeup状态,且isItBedTimeYetLocked()判断出用户该睡了,那么系统会进入休眠状态。

private boolean isItBedTimeYetLocked() {
         return mBootCompleted && !isBeingKeptAwakeLocked();//个人认为mBootCompleted很重要,但是在设备正常使用的过程中我们可以认为其值是true。现在还没有必要讨论其他的情况
     }
 
     /**
      * Returns true if the device is being kept awake by a wake lock, user activity
      * or the stay on while powered setting.
      */
     private boolean isBeingKeptAwakeLocked() {
         return mStayOn
                 || mProximityPositive
                 || (mWakeLockSummary & WAKE_LOCK_STAY_AWAKE) != 0
                 || (mUserActivitySummary & (USER_ACTIVITY_SCREEN_BRIGHT
                         | USER_ACTIVITY_SCREEN_DIM)) != 0;
     }

如果有应用程序持有wakelock,或者有用户活动的产生,或者处于充电状态,那么isBeingKeptAwakeLocked的返回值就是true,相应地isItBedTimeYetLocked返回值就是false,说明还没有到睡眠的时间,因为还有wakelock没释放,或者有用户活动,或者是在充电等。但是,如果wakelock都释放了,并且也没有了用户活动了也没有其他的顾虑了,那么就可以进入睡眠状态了。

这时候我们就要考虑设备由醒着到睡眠的处理过程了。接着看代码updateWakefulnessLocked中的line20 ~ 25的内容,

在line 20中的方法代码如下

    private boolean shouldNapAtBedTimeLocked() {
        return mDreamsActivateOnSleepSetting
                || (mDreamsActivateOnDockSetting
                        && mDockState != Intent.EXTRA_DOCK_STATE_UNDOCKED);
    }

mDreamsActivateOnSleepSetting的默认值为false,mDreamsActivateOnDockSetting的默认值为true。个人认为觉得Dock应该是类似于形似座充,或者能够接入汽车中的一个插孔吧,具体是什么不是很了解。如果按我的理解,在一般的用户手中是没有接入Dock的,所以mDockState != Intent.EXTRA_DOCK_STATE_UNDOCKED应该为false。所以这个函数的返回值应该是false的。

因此updateWakefulnessLocked的if-else会走goToSleepNoUpdateLocked这条线,而这条线仅仅是做了一些属性的更新,并未做实质性休眠的操作,不急,我们继续往下看。。。

第三阶段分析:

updateDreamLocked(dirtyPhase2);根据mDirty的变化结合其他的属性一起判断是否要开始dreaming,其实就是开始屏保。如果需要开始屏保的话,通过DreamManagerService开始dreaming。updateDisplayPowerStateLocked主要功能是每次都要重新计算一下display power state的值,即SCREEN_STATE_OFF,SCREEN_STATE_DIM,SCREEN_STATE_BRIGHT之一。此外,如果在DisplayController中更新了display power state的话,DisplayController会发送消息通知我们,因此我们还要回来重新检查一次。我们可以看看updateDisplayPowerStateLocked是如何实现的:

技术分享
 1     private void updateDisplayPowerStateLocked(int dirty) {
 2         if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_USER_ACTIVITY | DIRTY_WAKEFULNESS
 3                 | DIRTY_ACTUAL_DISPLAY_POWER_STATE_UPDATED | DIRTY_BOOT_COMPLETED
 4                 | DIRTY_SETTINGS | DIRTY_SCREEN_ON_BLOCKER_RELEASED)) != 0) {
 5             int newScreenState = getDesiredScreenPowerStateLocked();//获取display 的power state将要变成的状态
 6             if (newScreenState != mDisplayPowerRequest.screenState) {//mDisplayPowerRequest.screenState是目前display所处的power state
 7                 if (newScreenState == DisplayPowerRequest.SCREEN_STATE_OFF
 8                         && mDisplayPowerRequest.screenState
 9                                 != DisplayPowerRequest.SCREEN_STATE_OFF) {//这个判断意味着:目前display的电源状态不是OFF,但是想要变为OFF
10                     mLastScreenOffEventElapsedRealTime = SystemClock.elapsedRealtime();
11                 }
12 
13                 mDisplayPowerRequest.screenState = newScreenState;
14                 nativeSetPowerState(
15                         newScreenState != DisplayPowerRequest.SCREEN_STATE_OFF,
16                         newScreenState == DisplayPowerRequest.SCREEN_STATE_BRIGHT);
17             }
18 
19             int screenBrightness = mScreenBrightnessSettingDefault;
20             float screenAutoBrightnessAdjustment = 0.0f;
21             boolean autoBrightness = (mScreenBrightnessModeSetting ==
22                     Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);//获取屏幕亮度模式是否为自动变化
23             if (isValidBrightness(mScreenBrightnessOverrideFromWindowManager)) {//mScreenBrightnessOverideFromeWindowManager是WindowManager设置的亮度大小,默认值为-1
24                 screenBrightness = mScreenBrightnessOverrideFromWindowManager;
25                 autoBrightness = false;
26             } else if (isValidBrightness(mTemporaryScreenBrightnessSettingOverride)) {//mTemporaryScreenBrightnessSettingOverride在widget中中设置的临时亮度大小,默认为-1
27                 screenBrightness = mTemporaryScreenBrightnessSettingOverride;
28             } else if (isValidBrightness(mScreenBrightnessSetting)) {//在Settings中的设置的默认亮度,在android4.2中其值为102
29                 screenBrightness = mScreenBrightnessSetting;
30             }
31             if (autoBrightness) {//如果亮度是自动调节的话
32                 screenBrightness = mScreenBrightnessSettingDefault;
33                 if (isValidAutoBrightnessAdjustment(
34                         mTemporaryScreenAutoBrightnessAdjustmentSettingOverride)) {
35                     screenAutoBrightnessAdjustment =
36                             mTemporaryScreenAutoBrightnessAdjustmentSettingOverride;
37                 } else if (isValidAutoBrightnessAdjustment(
38                         mScreenAutoBrightnessAdjustmentSetting)) {
39                     screenAutoBrightnessAdjustment = mScreenAutoBrightnessAdjustmentSetting;
40                 }
41             }
42             screenBrightness = Math.max(Math.min(screenBrightness,
43                     mScreenBrightnessSettingMaximum), mScreenBrightnessSettingMinimum);
44             screenAutoBrightnessAdjustment = Math.max(Math.min(
45                     screenAutoBrightnessAdjustment, 1.0f), -1.0f);
46             mDisplayPowerRequest.screenBrightness = screenBrightness;//从这行向下开始就是配置完成DisplayPowerRequest,然后以此为参数通过requestPowerSTate方法进行设置。
47             mDisplayPowerRequest.screenAutoBrightnessAdjustment =
48                     screenAutoBrightnessAdjustment;
49             mDisplayPowerRequest.useAutoBrightness = autoBrightness;
50 
51             mDisplayPowerRequest.useProximitySensor = shouldUseProximitySensorLocked();
52 
53             mDisplayPowerRequest.blockScreenOn = mScreenOnBlocker.isHeld();
54 
55             mDisplayReady = mDisplayPowerController.requestPowerState(mDisplayPowerRequest,
56                     mRequestWaitForNegativeProximity);
57             mRequestWaitForNegativeProximity = false;
58 
59             if (DEBUG_SPEW) {
60                 Slog.d(TAG, "updateScreenStateLocked: mDisplayReady=" + mDisplayReady
61                         + ", newScreenState=" + newScreenState
62                         + ", mWakefulness=" + mWakefulness
63                         + ", mWakeLockSummary=0x" + Integer.toHexString(mWakeLockSummary)
64                         + ", mUserActivitySummary=0x" + Integer.toHexString(mUserActivitySummary)
65                         + ", mBootCompleted=" + mBootCompleted);
66             }
67         }
68     }
技术分享

根据代码中的注释,这个方法阅读起来应该是没有问题的。其中代码line 55 ~56行实现了对屏幕亮度的请求,并改变了亮度。我们把DisplayPowerState的状态更新为SCREEN_OFF了,然后就没做什么了,到这里之后,这个display state会因为requestPowerState的调用而起作用。这个方法的具体实现时在DisplayPowerStateController中。该类中setScreenOn方法再往下设置screen的开关状态,看代码我们知道该方法封装了DisplayPowerState类的setScreenOn方法,最终操作完成屏幕的ON或OFF,到这里,对于第三阶段的power state的更新已经完成了。

接下来就是最后一个阶段了的power state的更新了。这里对于SuspendBlocker的更新很简单,仅仅是判断现在device是否需要持有CPU或者是否需要CPU继续运行,如果有WakeLock没有释放,或者还有用户活动的话,或者屏幕没有关闭的话等等,这是肯定是需要持有CPU的。所以这里就是更具需求去申请或者释放SuspendBlocker。

到这里,对于PowerManagerService的工作应该有了大致的了解,而且我们也知道了PowerManagerService在Framework中是如何实现的,代码是如何工作的。知道这些之后,对于后面这些问题的分析和解决都是非常有帮助的。

附注:

PowerManagerService部分:frameworks/base/services/java/com/android/server/power

binder调用接口部分:frameworks/native/services/powermanager

JNI接口部分:frameworks/base/services/jni

HAL层:hardware目录下power子目录。







版权声明:本文为博主原创文章,未经博主允许不得转载。

android4.4 PowerManagerService流程分析

标签:android4.4   电源管理   powermanger   service   powermanagerservice   

原文地址:http://blog.csdn.net/dkbdkbdkb/article/details/47257863

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!