PMS服务之updatePowerStateLocked方法分析

Posted zhenjie_chang

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了PMS服务之updatePowerStateLocked方法分析相关的知识,希望对你有一定的参考价值。

updatePowerStateLocked更新电源的状态,是PowerManagerService的核心方法,当电源状态发生改变最终都会调用到updatePowerStateLocked方法,下面分析这个方法的实现。

private void updatePowerStateLocked() 
        if (!mSystemReady || mDirty == 0) 
            return;
        
        
        try 
            // 第一步:更新基本状态
            updateIsPoweredLocked(mDirty);
            updateStayOnLocked(mDirty);
            updateScreenBrightnessBoostLocked(mDirty);

            // 第二步: 更新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;
                
            

            // 第三步:更新display power state
            boolean displayBecameReady = updateDisplayPowerStateLocked(dirtyPhase2);

            // 第四步:更新dream state
            updateDreamLocked(dirtyPhase2, displayBecameReady);

            // 第五步:发送通知
            finishWakefulnessChangeIfNeededLocked();

            //第六步:更新底层wakeLock
            updateSuspendBlockerLocked();
        
    
 

在PowerManagerService服务中有个很重要的变量,就是mDirty,这个变量按位来存贮PowerManagerService中的各种变化状态,每当和PMS相关发生了变化,就会第一时间来更新mDirty的相应的位,将相应的位置为1,mDirty变量按位来存储一些Power相关的标志位,然后该方法根据mDirty变量来更新Power的各个状态。

在SystemReady方法中会更新mDirty这个状态,把mDirty置位为DIRTY_BATTERY_STATE,收BatterySevice发出来的广播,也会设置mDirty这个标志位。这个方法中集中处理了电源状态的更新逻辑,当系统的电源相关发生一些变化的时候都会调用这个方法更新电源状态。

该方法的处理主要分为几个步骤,我们一个一个步骤的来进行分析:

第一步:更新基本的状态信息

updateIsPowerLocked(mDirty)

private void updateIsPoweredLocked(int dirty) 
        if ((dirty & DIRTY_BATTERY_STATE) != 0) 
           //电池状态发生变化
            ……
            mIsPowered = mBatteryManagerInternal.isPowered(BatteryManager.BATTERY_PLUGGED_ANY);
            //充电类型
            mPlugType = mBatteryManagerInternal.getPlugType();
            //当前电量
            mBatteryLevel = mBatteryManagerInternal.getBatteryLevel();
            //当前是否低电量
            mBatteryLevelLow = mBatteryManagerInternal.getBatteryLevelLow();


            if (wasPowered != mIsPowered || oldPlugType != mPlugType) 
                mDirty |= DIRTY_IS_POWERED;

              ……
              //插拔充电器或者USB是否唤醒屏幕
(shouldWakeUpWhenPluggedOrUnpluggedLocked(wasPowered, oldPlugType,
                        dockedOnWirelessCharger)) 
                    //更新唤醒屏幕的信息
                    wakeUpNoUpdateLocked(now, "android.server.power:POWER", Process.SYSTEM_UID,
                            mContext.getOpPackageName(), Process.SYSTEM_UID);
                
                //触发一次用户活动,修改用户活动事件的时间,重置屏幕灭屏的时间
                userActivityNoUpdateLocked(
                        now, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);

                ……
            
            //如果充电状态或者低电量状态发生变化,调用updateLowPowerLocked 更新电源信息
            if (wasPowered != mIsPowered || oldLevelLow != mBatteryLevelLow) 
                ……
                updateLowPowerModeLocked();
            
        
    


 

该方法中更新了电池当前的电量,当前是否正常充电,充电的类型,以及batteryLow的电量和mBatteryLevelLow是否是低电量状态

当电源的充电状态或者充电类型发生变化, 根据设置来决定是否唤醒系统

当电源充电状态发生变化或者低电量的值发生变化的时候,调用updateLowPowerModeLocked()更新低电量的状态。

<pre name="code" class="java">void updateLowPowerModeLocked() 
        if (mIsPowered && mLowPowerModeSetting) 
            Settings.Global.putInt(mContext.getContentResolver(),
                    Settings.Global.LOW_POWER_MODE, 0);
            mLowPowerModeSetting = false;
        
       //是否可以自动进入低电量状态的省电模式
        final boolean autoLowPowerModeEnabled = !mIsPowered && mAutoLowPowerModeConfigured
                && !mAutoLowPowerModeSnoozing && mBatteryLevelLow;
        //当前是否是低电量模式
        final boolean lowPowerModeEnabled = mLowPowerModeSetting || autoLowPowerModeEnabled;

        if (mLowPowerModeEnabled != lowPowerModeEnabled) 
            mLowPowerModeEnabled = lowPowerModeEnabled;
            powerHintInternal(POWER_HINT_LOW_POWER, lowPowerModeEnabled ? 1 : 0);
            BackgroundThread.getHandler().post(new Runnable() 
                @Override
                public void run() 
                    Intent intent = new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGING)
                            .putExtra(PowerManager.EXTRA_POWER_SAVE_MODE, mLowPowerModeEnabled)
                            .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
                   ……intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
                    mContext.sendBroadcast(intent);
                
            );
        
    


 

该方法中首先判断手机是否在充电,如果手机在充电,退出LowPowerMode模式,同时把设置中的mLowPowerModeSetting置为false。 autoLowPowerModeEnabled变量表示是否可以自动进入低电量省电模式,需要满足几个条件:

1.未充电 2.设置了可以自动进入低电量省电模式 3.目前处于低电量的状态

mLowPowerModeEnabled变量表示当前是否进入低电量省电模式

当手机的低电量模式发生了变化,就发送ACTION_POWER_SAVE_MODE_CHANGING的通知,回调关于该模式变化的监听,通知相关的服务Power Save Mode发生了变化。

PowerUI接收到低电量省电模式的广播,就会弹出低电量省电模式的提醒界面

updateStayOnLocked(mDirty)

该方法根据当前的充电状态,来判断充电的时候手机是否保持唤醒。

<pre name="code" class="java">private void updateStayOnLocked(int dirty) 
        if ((dirty & (DIRTY_BATTERY_STATE | DIRTY_SETTINGS)) != 0) 
            final boolean wasStayOn = mStayOn;
           ……
                mStayOn = mBatteryManagerInternal.isPowered(mStayOnWhilePluggedInSetting);
           ……
            if (mStayOn != wasStayOn) 
                mDirty |= DIRTY_STAY_ON;
            
        
    
 

更新变量mStayOn的值,如果mStayOn如果为true,则屏幕长亮。

在Setting中可以设置充电时候屏幕长亮,如果Setting中设置了该选项,updateIsPoweredLocked检查到正在充电,会将mStayOn置为true。

mBatteryManagerInternal.isPowered()方法根据当前的充电状态来决定充电的时候是否保持系统唤醒

updateScreenBrightnessBoostLocked(mDirty)

该方法主要用来更新屏幕是否保持最大亮度

我们知道PowerManager中有个方法boostScreenBrightness(long time),该方法的作用是让屏幕在一段时间内保持最大的亮度,使屏幕在强光下有更好的可读性。

那我们结合这个方法来分析,该方法最终会调用到boostScreenBrightnessInternal()方法中

 private void boostScreenBrightnessInternal(long eventTime, int uid) 
        synchronized (mLock) 
            //时间太晚,系统没有准备好或者当前为Asleep状态,不处理
            if (!mSystemReady || mWakefulness == WAKEFULNESS_ASLEEP
                    || eventTime < mLastScreenBrightnessBoostTime) 
                return;
            
            //记录最亮屏幕的起始时间
            mLastScreenBrightnessBoostTime = eventTime;
            if (!mScreenBrightnessBoostInProgress) 
                //设置最亮屏幕的标志位true
                mScreenBrightnessBoostInProgress = true;
                //通知BatteryStats统计信息
                mNotifier.onScreenBrightnessBoostChanged();
            
            //修改mDirty的值,表示最大屏幕亮度发生了变化
            mDirty |= DIRTY_SCREEN_BRIGHTNESS_BOOST;
            //触发一次用户活动
            userActivityNoUpdateLocked(eventTime,
                    PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, uid);
            //更新电源的状态信息
            updatePowerStateLocked();
        
    

该方法中首先首先使用mLastScreenBrightnessBoostTime变量记录了最大屏幕亮度的起始时间,然后修改最大屏幕亮度的标志位mScreenBrightnessBoostInProgress为true,修改mDirty标志位,表示最大屏幕亮度发生了变化,调用updatePowerStateLocked()方法更新电源状态信息,最终会调用到updateScreenBrightnessBoostLocked()方法中

<pre name="code" class="java">private void updateScreenBrightnessBoostLocked(int dirty) 
        if ((dirty & DIRTY_SCREEN_BRIGHTNESS_BOOST) != 0) 
            //根据mDirty的标志位来判断最大屏幕亮度是否发生了变化
            if (mScreenBrightnessBoostInProgress) 
               //如果当前处于最大屏幕亮度
                final long now = SystemClock.uptimeMillis();
                mHandler.removeMessages(MSG_SCREEN_BRIGHTNESS_BOOST_TIMEOUT);
                if (mLastScreenBrightnessBoostTime > mLastSleepTime) 
                    //计算最大屏幕亮度结束的时间>
                    final long boostTimeout = mLastScreenBrightnessBoostTime +
                            SCREEN_BRIGHTNESS_BOOST_TIMEOUT;
                    if (boostTimeout > now) 
                        当前时间没有到达最屏幕亮度结束的时间
                        Message msg = mHandler.obtainMessage(MSG_SCREEN_BRIGHTNESS_BOOST_TIMEOUT);
                        msg.setAsynchronous(true);
                        mHandler.sendMessageAtTime(msg, boostTimeout);
                        //发送一个延迟的消息,在最大屏幕亮度结束的时间发送消息
                        //退出
                        return;
                    
                 
                //将最大屏幕亮度标志置为false
                mScreenBrightnessBoostInProgress = false;
                //通知最大屏幕亮度发生了变化
                mNotifier.onScreenBrightnessBoostChanged();
                //触发一次用户活动
                userActivityNoUpdateLocked(now,
                        PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);
            
        
    
 

在该方法中,如果最大屏幕亮度发生了变化,才会执行该方法。

第一次进入,屏幕处于最大亮度,判断最大亮度的起始时间是否正常,如果正常则根据最大屏幕亮度的起始时间计算出最大屏幕亮度的结束时间

如果当前时间没有到达最大亮度的结束时间,则创建一个延迟消息,在最大屏幕亮度结束的时候发送。然后调用updateDisplayPowerStateLocked更新屏幕状态为最大亮度

收到延迟消息后,再次调用updatePowerStateLocked方法,进入到updateScreenBrightnessBoostLocked方法中,此时在此进入的时候肯定超出最大屏幕亮度结束时间,将最大屏幕亮度标志mScreenBrightnessBoostInProgress置为false。

在后面的方法中调用updateDisplayPowerStateLocked方法中来更新屏幕亮度

最大屏幕亮度变化的基本流程总结如下

updateWakefulnessLocked方法分析

更新wakeLock和用户活动的逻辑是个无限循化,那么看下这个循环break的条件。

if (!updateWakefulnessLocked(dirtyPhase1)) 
                    break;
                

即updateWakefulnessLocked()方法返回false的时候,退出循环。

该方法的具体实现。

<pre name="code" class="java">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()) 
                if (DEBUG_SPEW) 
                    Slog.d(TAG, "updateWakefulnessLocked: Bed time...");
                
                final long time = SystemClock.uptimeMillis();
                //是否满足进入Dream状态
                if (shouldNapAtBedTimeLocked()) 
                    //进入Dream状态
                    changed = napNoUpdateLocked(time, Process.SYSTEM_UID);
                 else 
                    //进入sleep状态
                    changed = goToSleepNoUpdateLocked(time,
                            PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, 0, Process.SYSTEM_UID);
                
            
        
        return changed;
    
 

根据代码可以看出,如果不满足第一个if条件直接返回false,退出循环,循环执行一次。

第一个if条件很容易满足,即mDirty中有相应的标志位。第二个条件要求mWakefulness == WAKEFULNESS_AWAKE,方法isBeingKeptAwakeLocked()结果为true,该方法是系统马上要进入休眠状态的时候返回为true。

shouldNapAtBedTimeLocked()方法表示是否可以进入互动屏保阶段

如果可以进入互动屏保阶段,则调用napNoUpdateLocked方法,将mWakefulness状态修改为WAKEFULNESS_DREAMING

如果不需要屏保,则调用goToSleepNoUpdateLocked方法将mWakefulness状态修改为WAKEFULNESS_DOZING,直接休眠系统。

此时可知mWakefulness状态发生了变化,即第二次循环的话就会直接退出。所以该无限循环最多看似是无限循环,其实最多只执行两次。

明白了该循环的退出条件,那我们在来看该循环内的两个方法

updateWakeLockSummaryLocked方法

<pre name="code" class="java">private void updateWakeLockSummaryLocked(int dirty) 
        if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_WAKEFULNESS)) != 0) 
            mWakeLockSummary = 0;

            final int numWakeLocks = mWakeLocks.size();
           //遍历所有的wakeLock将所有的wakeLock类型按位存储到mWakeLockSummary上
            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:
                        if (!wakeLock.mDisabled) 
                      ……
                    case PowerManager.DRAW_WAKE_LOCK:
                        mWakeLockSummary |= WAKE_LOCK_DRAW;
                        break;
                
            
            //如果不是Dozing状态,移除相应的wakeLock标志位
            if (mWakefulness != WAKEFULNESS_DOZING) 
                mWakeLockSummary &= ~(WAKE_LOCK_DOZE | WAKE_LOCK_DRAW);
            
            //如果当前为Asleep或者有Doze的wakeLock锁的时候,应该移除掉屏幕亮度相关的wakeLock锁
            if (mWakefulness == WAKEFULNESS_ASLEEP
                    || (mWakeLockSummary & WAKE_LOCK_DOZE) != 0) 
                mWakeLockSummary &= ~(WAKE_LOCK_SCREEN_BRIGHT | WAKE_LOCK_SCREEN_DIM
                        | WAKE_LOCK_BUTTON_BRIGHT);
                if (mWakefulness == WAKEFULNESS_ASLEEP) 
                    mWakeLockSummary &= ~WAKE_LOCK_PROXIMITY_SCREEN_OFF;
                
            
            //如果持有屏幕亮度相关的锁,需要同时持有CPU的wakeLock
            if ((mWakeLockSummary & (WAKE_LOCK_SCREEN_BRIGHT | WAKE_LOCK_SCREEN_DIM)) != 0) 
                if (mWakefulness == WAKEFULNESS_AWAKE) 
                    mWakeLockSummary |= WAKE_LOCK_CPU | WAKE_LOCK_STAY_AWAKE;
                 else if (mWakefulness == WAKEFULNESS_DREAMING) 
                    //如果为屏保状态,也要添加CPU的wakeLock
                    mWakeLockSummary |= WAKE_LOCK_CPU;
                
            
            //如果有WakeLockDraw的锁,同时也要持有CPU的wakeLock
            if ((mWakeLockSummary & WAKE_LOCK_DRAW) != 0) 
                mWakeLockSummary |= WAKE_LOCK_CPU;
            

        
    


 

该方法中首先将系统中所有的wakelock更新到一个变量中mWakeLockSummary中,因为无论系统中有多少个wakeLock,其中一个wakeLock就可以阻止系统休眠,将所有wakeLock的类型集中到一个变量中,不同的wakeLock置不同的位,这样来集中控制系统的休眠

然后在根据不同的条件,去掉该条件下不该有的wakeLock

然后对一些wakeLock同时需要CPU运行,所以根据情况添加CPU的wakeLock锁

updateUserActivitySummaryLocked方法

<pre name="code" class="java">private void updateUserActivitySummaryLocked(long now, int dirty) 
        if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_USER_ACTIVITY
                | DIRTY_WAKEFULNESS | DIRTY_SETTINGS)) != 0) 
            //当wakeLock, userActivity或者Settings发生变化
            //首先移除到还未执行的延迟消息
            mHandler.removeMessages(MSG_USER_ACTIVITY_TIMEOUT);

            long nextTimeout = 0;//下次触发该方法的时间
            if (mWakefulness == WAKEFULNESS_AWAKE
                    || mWakefulness == WAKEFULNESS_DREAMING
                    || mWakefulness == WAKEFULNESS_DOZING) 
                final int sleepTimeout = getSleepTimeoutLocked();
                //获取进入休眠状态的时间sleepTimeout,如果休眠时间sleepTimeout小于屏幕熄灭时间screenOfftime,
                //则休眠时间等于屏幕熄灭时间,因为屏幕亮屏状态不能进入休眠
                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;<span style="color:#009900;">
                    //如果没有到达需要变暗的时间点,那么当前屏幕的状态应该是:USER_ACTIVITY_SCREEN_BRIGHT 亮屏</span>
                     else 
                        //现在已经到达了屏幕变暗的时间点 
                        nextTimeout = mLastUserActivityTime + screenOffTimeout;
                        //计算出屏幕熄灭的时间点
                        if (now < nextTimeout) 
                            //没有到达屏幕熄灭的时间点的时候,当前屏幕的状态应该是:USER_ACTIVITY_SCREEN_DIM 屏幕变暗
                            mUserActivitySummary = USER_ACTIVITY_SCREEN_DIM;
                        
                    
                
                if (mUserActivitySummary == 0
                        && mLastUserActivityTimeNoChangeLights >= mLastWakeTime) 
                    nextTimeout = mLastUserActivityTimeNoChangeLights + screenOffTimeout;
                    //计算下次屏幕熄灭的时间
                    if (now < nextTimeout) 
                        //没有到屏幕熄灭的时间
                        if (mDisplayPowerRequest.policy == DisplayPowerRequest.POLICY_BRIGHT) 
                            //当前屏幕是亮屏,仍然设置为亮屏
                            mUserActivitySummary = USER_ACTIVITY_SCREEN_BRIGHT;
                         else if (mDisplayPowerRequest.policy == DisplayPowerRequest.POLICY_DIM) 
                            //当前屏幕是变暗,仍然设置为变暗
                            mUserActivitySummary = USER_ACTIVITY_SCREEN_DIM;
                        
                    
                
                if (mUserActivitySummary == 0) 
                    if (sleepTimeout >= 0) 
                        //灭屏时间大于0
                        final long anyUserActivity = Math.max(mLastUserActivityTime,
                                mLastUserActivityTimeNoChangeLights);
                        //计算最后的用户活动时间
                        if (anyUserActivity >= mLastWakeTime) 
                            nextTimeout = anyUserActivity + sleepTimeout;
                            if (now < nextTimeout) 
                                //当前时间点小于sleeptime,大于screenOffTime,当前
                                //屏幕状态应该是USER_ACTIVITY_SCREEN_DREAM
                                mUserActivitySummary = USER_ACTIVITY_SCREEN_DREAM;
                            
                        
                     else 
                        mUserActivitySummary = USER_ACTIVITY_SCREEN_DREAM;
                        nextTimeout = -1;
                    
                
                if (mUserActivitySummary != 0 && nextTimeout >= 0) 
                    Message msg = mHandler.obtainMessage(MSG_USER_ACTIVITY_TIMEOUT);
                    msg.setAsynchronous(true);
                    mHandler.sendMessageAtTime(msg, nextTimeout);
                    //根据nextTimeOut延迟发送信息,然后Handler处理,再次进入//到此方法
                
             else 
                mUserActivitySummary = 0;
            

        
    


 

该方法主要根据用户最后的活动来决定当前屏幕的状态,在该方法中用mUserActivitySummary变量俩存储当前屏幕的状态,一共有3中状态

1. USER_ACTIVITY_SCREEN_BRIGHT 点亮屏幕

2. USER_ACTIVITY_SCREEN_DIM 屏幕变暗

3. USER_ACTIVITY_SCREEN_DREAM 互动屏保

现在我们在分析到这个方法之后就可以总结下屏幕变化的逻辑了

首先,屏幕变化跟userActivity活动有关,它根据最后的userActivity活动的时间来决定当前屏幕是点亮屏幕,屏幕变暗还是熄灭屏幕。

很多方法中都会调用userActivityNoUpdateLocked方法,该方法触发一次用户活动,更新用户活动的时间,这样的话屏幕变暗和熄灭时间就会从新计算,这也就是如果一直操控手机,屏幕就不会熄灭或者变暗的原因。

 

我们按照最后一次触发userActivity后进入updatePowerStateLocked方法来分析。

屏幕变化流程亮屏—变暗—熄灭,这个流程中如果操作手机,屏幕会重新回到Screen bright状态

第三步:更新屏幕显示状态

updateDisplayPowerStateLocked方法

该方法根据以上得出的结论,调用DisplayManager来更新屏幕的显示状态,

1. 获得屏幕的显示状态

private int getDesiredScreenPolicyLocked() 
        if (mWakefulness == WAKEFULNESS_ASLEEP) 
            return DisplayPowerRequest.POLICY_OFF;
        

        if (mWakefulness == WAKEFULNESS_DOZING) 
            if ((mWakeLockSummary & WAKE_LOCK_DOZE) != 0) 
                return DisplayPowerRequest.POLICY_DOZE;
            
            if (mDozeAfterScreenOffConfig) 
                return DisplayPowerRequest.POLICY_OFF;
            
        

        if ((mWakeLockSummary & WAKE_LOCK_SCREEN_BRIGHT) != 0
                || (mUserActivitySummary & USER_ACTIVITY_SCREEN_BRIGHT) != 0
                || !mBootCompleted
                || mScreenBrightnessBoostInProgress) 
            return DisplayPowerRequest.POLICY_BRIGHT;
        

        return DisplayPowerRequest.POLICY_DIM;
    

根据分析可知

如果当前是WAKEFULNESS_ASLEEP状态,直接设置屏幕为POLICY_OFF

如果包含以下一种状态,就设置屏幕为POLICY_BRIGHT

1.mWakeLockSummary 如果有 WAKE_LOCK_SCREEN_BRIGHT类型的wakeLock 

2.mUserActivitySummary 屏幕状态为USER_ACTIVITY_SCREEN_BRIGHT

3.当前系统未启动完成

4.当前处于最大屏幕亮度

可以看出屏幕的状态和前面设置的wakeLock,stayon,userActivity,screenBrightness等有关

 

1. 更新屏幕的显示

根据先前得到的屏幕设置调用方法mDisplayManagerInternal.requestPowerState()来更新屏幕

第四步:更新互动屏保

updateDreamStateLocked分析 

updateDreamStateLocked方法主要作用是修改互动屏保的状态

该方法会发送一个消息到Handler,最终的处理逻辑在 handleSandman()方法中,当设备进入或者退出互动屏保的时候都会调用这个方法

只有mSandmanSummoned==true的时候,才有可能进入互动屏保状态

if (mSandmanSummoned && mDisplayReady) 
                startDreaming = canDreamLocked() || canDozeLocked();
                mSandmanSummoned = false;
             else 
                startDreaming = false;
            

mSandmanSummoned是由什么决定的呢?在上面的updateWakefulness方法中设置了这个变量

if (mWakefulness == WAKEFULNESS_AWAKE && isItBedTimeYetLocked()) 
                final long time = SystemClock.uptimeMillis();
                if (shouldNapAtBedTimeLocked()) 
                    changed = napNoUpdateLocked(time, Process.SYSTEM_UID);
                 else 
                    changed = goToSleepNoUpdateLocked(time,
                            PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, 0, Process.SYSTEM_UID);
                
            

如果当前手机将要进入sleep状态的时候,会进入到这个方法中。这个方法中决定了是否进入互动屏保

shouldNapAtBedTimeLocked()判断是否需要进入互动屏保

如果可以,调用napNoUpdateLocked方法,将当前的wakeFulness状态修改为WAKEFULNESS_DREAMING同时将mSandmanSummoned变量置为true

否则直接进入Sleep状态

调用goToSleepNoUpdateLocked方法,将当前的wakeFulness状态修改为WAKEFULNESS_ASLEEP同时将mSandmanSummoned变量置为true。

即手机即将要进入sleep状态的时候,mSandmanSummoned会为true,此时会根据状态决定是进入互动屏保还是直接休眠进入sleep状态

当mSandmanSummoned为true后只是表示可以进入,具体能不能进入互动屏保状态还需要看

private boolean canDreamLocked() 
       如果要想计入互动屏保,首先要mWakefulness的状态为WAKEFULNESS_DREAMING,设备支持互动屏保,设置中互动屏保打开,屏幕状态为熄屏,系统启动完成
        if (mWakefulness != WAKEFULNESS_DREAMING
                || !mDreamsSupportedConfig
                || !mDreamsEnabledSetting
                || !mDisplayPowerRequest.isBrightOrDim()
                || (mUserActivitySummary & (USER_ACTIVITY_SCREEN_BRIGHT
                        | USER_ACTIVITY_SCREEN_DIM | USER_ACTIVITY_SCREEN_DREAM)) == 0
                || !mBootCompleted) 
            return false;
        
        手机不能是唤醒状态
        if (!isBeingKeptAwakeLocked()) 
            <span style="color:#009900;">手机不能是未充电状态,且设置中不支持电池状态下使用互动屏保</span>
            if (!mIsPowered && !mDreamsEnabledOnBatteryConfig) 
                return false;
            
            手机不能是未充电状态,且电量不能小于指定的电量
            if (!mIsPowered
                    && mDreamsBatteryLevelMinimumWhenNotPoweredConfig >= 0
                    && mBatteryLevel < mDreamsBatteryLevelMinimumWhenNotPoweredConfig) 
                return false;
            
            ……
        
        return true;
    

根据canDreamLocked()方法可以看出进入互动屏保需要的条件,关键,互动屏保只有在充电状态下才会工作

final boolean isDreaming;
        if (mDreamManager != null) 
            // Restart the dream whenever the sandman is summoned.
            if (startDreaming) 
                mDreamManager.stopDream(false /*immediate*/);
                mDreamManager.startDream(wakefulness == WAKEFULNESS_DOZING);
            
            isDreaming = mDreamManager.isDreaming();
         else 
            isDreaming = false;
        

当都满足进入互动屏保的条件后,调用DreamManager来进入互动屏保

下面看下互动屏保的退出条件

f (wakefulness == WAKEFULNESS_DREAMING) 
                if (isDreaming && canDreamLocked()) 
                    if (mDreamsBatteryLevelDrainCutoffConfig >= 0
                            && mBatteryLevel < mBatteryLevelWhenDreamStarted
                                    - mDreamsBatteryLevelDrainCutoffConfig
                            && !isBeingKeptAwakeLocked()) 
                            使用互动屏保手机后,手机电量低于定义的电量,决定退出
                     else 
                        //互动屏保继续
                        return; 
                    
                

                //互动屏保将要退出
                if (isItBedTimeYetLocked()) 
                    //满足进入sleep的条件,进入sleep状态
                    goToSleepNoUpdateLocked(SystemClock.uptimeMillis(),
                            PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, 0, Process.SYSTEM_UID);
                    updatePowerStateLocked();
                 else 
                    //唤醒手机,进入Awake状态
                    wakeUpNoUpdateLocked(SystemClock.uptimeMillis(), "android.server.power:DREAM",
                            Process.SYSTEM_UID, mContext.getOpPackageName(), Process.SYSTEM_UID);
                    updatePowerStateLocked();
                
            

当互动屏保消耗电量过快,或者mWakefulness的状态发生变化,就会退出互动屏保。

分析总结,互动屏保的流程

updatePowerStateLocke方法,满足手机即将进入sleep的条件

如果系统支持互动屏保

调用updateDreamLocked,启动互动屏保

如果当前wakeFullness状态不是Dreaming状态或者使用互动屏保电量消耗过快,退出互动屏保,根据条件进入Sleep状态或者唤醒手机

以下是相关的流程图



updateSuspendBlockerLocked方法分析

该方法主要根据前面的几个方法,来决定持有或者释放CPU和屏幕的锁

mWakeLockSuspendBlocker和 mDisplaySuspendBlocker

两个变量在前面的方法中讲过,前者传入底层来控制CPU的状态,后者来控制屏幕的暗灭。

private void updateSuspendBlockerLocked() 
        final boolean needWakeLockSuspendBlocker = ((mWakeLockSummary & WAKE_LOCK_CPU) != 0);
        //根据是否有CPU的wakelock,来决定cpu是保持否唤醒
        final boolean needDisplaySuspendBlocker = needDisplaySuspendBlockerLocked();
        //根据前面屏幕的状态,屏幕是否需要亮屏,来决定是否需要持有屏幕的锁

        //如果需要,则获取CPU和屏幕的锁
        if (needWakeLockSuspendBlocker && !mHoldingWakeLockSuspendBlocker) 
            mWakeLockSuspendBlocker.acquire();
            mHoldingWakeLockSuspendBlocker = true;
        
        if (needDisplaySuspendBlocker && !mHoldingDisplaySuspendBlocker) 
            mDisplaySuspendBlocker.acquire();
            mHoldingDisplaySuspendBlocker = true;
        

        ……
        //如果不需要,则释放CPU和屏幕的锁
        if (!needWakeLockSuspendBlocker && mHoldingWakeLockSuspendBlocker) 
            mWakeLockSuspendBlocker.release();
            mHoldingWakeLockSuspendBlocker = false;
        
        if (!needDisplaySuspendBlocker && mHoldingDisplaySuspendBlocker) 
            mDisplaySuspendBlocker.release();
            mHoldingDisplaySuspendBlocker = false;
        
        ……

    

到此updatePowerStateLocked方法基本分析完了,下面总结下updatePowerStateLocked方法的处理流程,具体键流程图




以上是关于PMS服务之updatePowerStateLocked方法分析的主要内容,如果未能解决你的问题,请参考以下文章

PMS服务启动原理详解

PMS启动 APK 安装流程详解

Android FrameWork(AMS,WMS,PMS等)的概念及解析,获取系统服务

Framework底层服务,AMS|PMS|WMS原理分析

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

从APK安装过程来认识PMS