Android Framework 电源子系统(05)核心方法updatePowerStateLocked分析-3 更新屏保 & 发送通知 & 更新wakelock

112 篇文章 88 订阅

该系列文章总纲链接:专题分纲目录 Android Framework 电源子系统


本章关键点总结 & 说明:

本章节主要关注➕ updatePowerStateLocked 方法中 更新屏保 和 发送通知 并 更新wakelock锁 部分 即可。该章节 主要是 对更新屏保 和 发送通知 并 更新wakelock锁 部分 进行 详细解读。

PMS核心方法updatePowerStateLocked

updatePowerStateLocked 方法是整个PMS中的核心方法,它用来更新整个电源状态的改变,并进行重新计算。PMS中使用一个int值mDirty作为标志位判断电源状态是否发生变化,当电源状态发生改变时,如亮灭屏、电池状态改变、暗屏…都会调用该方法,它的代码实现如下:

private void updatePowerStateLocked() {
    //...
    Trace.traceBegin(Trace.TRACE_TAG_POWER, "updatePowerState");
    try {
        // 1 更新基本状态
        // 1.1 更新mIsPowered,mPlugType,mBatteryLevel
        updateIsPoweredLocked(mDirty);
        // 1.2 更新mStayon
        updateStayOnLocked(mDirty);
        updateScreenBrightnessBoostLocked(mDirty);

        // 2 更新wakefulness和用户活动
        final long now = SystemClock.uptimeMillis();
        int dirtyPhase2 = 0;
        for (;;) {
            int dirtyPhase1 = mDirty;
            dirtyPhase2 |= dirtyPhase1;
            mDirty = 0;
            // 2.1 得到当前终端整体的信息,保存到mWakeLockSummary变量中
            updateWakeLockSummaryLocked(dirtyPhase1);
            // 2.2 根据用户最后的活动来决定当前屏幕的状态
            updateUserActivitySummaryLocked(now, dirtyPhase1);
            // 2.3 判定 第二阶段的电源状态更新是否结束
            if (!updateWakefulnessLocked(dirtyPhase1)) {
                break;
            }
        }

        // 3 更新显示设备状态
        boolean displayBecameReady = updateDisplayPowerStateLocked(dirtyPhase2);

        // 4 更新屏保状态
        updateDreamLocked(dirtyPhase2, displayBecameReady);

        // 5 发送通知 并 更新底层wakelock
        // 5.1 发送通知
        if (mDisplayReady) {
            finishWakefulnessChangeLocked();
        }

        // 5.2 更新底层wakelock
        updateSuspendBlockerLocked();
    } finally {
        Trace.traceEnd(Trace.TRACE_TAG_POWER);
    }
}

该部分代码共分为5部分,因为篇幅较长,所以拆分成3个章节进行分析。本章节是 4 5部分。


4 更新屏保 updateDreamLocked分析

updateDreamLocked函数主要用于更新屏保状态,当设备进入或者退出屏保的时候都会触发这个方法:

private void updateDreamLocked(int dirty, boolean displayBecameReady) {
    if ((dirty & (DIRTY_WAKEFULNESS
            | DIRTY_USER_ACTIVITY
            | DIRTY_WAKE_LOCKS
            | DIRTY_BOOT_COMPLETED
            | DIRTY_SETTINGS
            | DIRTY_IS_POWERED
            | DIRTY_STAY_ON
            | DIRTY_PROXIMITY_POSITIVE
            | DIRTY_BATTERY_STATE)) != 0 || displayBecameReady) {
        if (mDisplayReady) {
            //mDirty满足条件,同时屏幕状态调整完毕,才进入下一步
            scheduleSandmanLocked();
        }
    }
}

这里 关键分析 scheduleSandmanLocked,代码实现如下:

private void scheduleSandmanLocked() {
    if (!mSandmanScheduled) {
		//让MessageQueue中仅保留一个MSG_SANDMAN
        mSandmanScheduled = true;
		//由handleSandman处理
        Message msg = mHandler.obtainMessage(MSG_SANDMAN);
        msg.setAsynchronous(true);
        mHandler.sendMessage(msg);
    }
}

处理该消息 使用handleSandman函数,它比较复杂,主要用于决定设备是否应该停留在dreaming或dozing状态。 代码如下:

private void handleSandman() { // runs on handler thread
    //1 是否开始进入屏保
    final boolean startDreaming;
    final int wakefulness;
    synchronized (mLock) {
        mSandmanScheduled = false;
        wakefulness = mWakefulness;
        //当updateWakefulnessLocked判断进入dozing或sleep状态时,
        //会将mSandmanSummoned置为true
        //mDisplayReady主要确保前面屏幕状态更新完毕
        if (mSandmanSummoned && mDisplayReady) {
            //判断device是否可以dream或dozing
            startDreaming = canDreamLocked() || canDozeLocked();
            mSandmanSummoned = false;
        } else {
            startDreaming = false;
        }
    }

    //2 表示是否正在屏保
    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);
        }
        //startDream成功后,一般isDreaming就会返回true
        isDreaming = mDreamManager.isDreaming();
    } else {
        isDreaming = false;
    }

    //3 更新屏保状态.
    synchronized (mLock) {
        //记录进入屏保时的电池电量
        if (startDreaming && isDreaming) {
            mBatteryLevelWhenDreamStarted = mBatteryLevel;
        }

        if (mSandmanSummoned || mWakefulness != wakefulness) {
            return; // wait for next cycle
        }

        // Determine whether the dream should continue.
        if (wakefulness == WAKEFULNESS_DREAMING) {
            if (isDreaming && canDreamLocked()) {
                if (mDreamsBatteryLevelDrainCutoffConfig >= 0
                        && mBatteryLevel < mBatteryLevelWhenDreamStarted
                                - mDreamsBatteryLevelDrainCutoffConfig
                        && !isBeingKeptAwakeLocked()) {
                } else {
                    return; // continue dreaming
                }
            }

            // Dream has ended or will be stopped.  Update the power state.
            if (isItBedTimeYetLocked()) {
                //休眠
                goToSleepNoUpdateLocked(SystemClock.uptimeMillis(),
                        PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, 0, Process.SYSTEM_UID);
                updatePowerStateLocked();
            } else {
                //唤醒
                wakeUpNoUpdateLocked(SystemClock.uptimeMillis(), Process.SYSTEM_UID);
                updatePowerStateLocked();
            }
        //如果处于Doze状态,在power键灭屏时,首次会将wakefulness设置为该值
        } else if (wakefulness == WAKEFULNESS_DOZING) {
            if (isDreaming) {
                return; // continue dozing
            }

            //进入asleep状态
            reallyGoToSleepNoUpdateLocked(SystemClock.uptimeMillis(), Process.SYSTEM_UID);
            updatePowerStateLocked();
        }
    }
    
    //如果正处在Dream,则只要触发updatePowerStateLocked(),立即退出Dream
    if (isDreaming) {
        mDreamManager.stopDream(false /*immediate*/);
    }
}

这里主要分3部分 对这段代码进行分析

4.1 决定是否可以进入屏保状态

    ...
    //1 是否开始进入屏保
    final boolean startDreaming;
    final int wakefulness;
    synchronized (mLock) {
        mSandmanScheduled = false;
        wakefulness = mWakefulness;
        //当updateWakefulnessLocked判断进入dozing或sleep状态时,
        //会将mSandmanSummoned置为true
        //mDisplayReady主要确保前面屏幕状态更新完毕
        if (mSandmanSummoned && mDisplayReady) {
            //判断device是否可以dream或dozing
            startDreaming = canDreamLocked() || canDozeLocked();
            mSandmanSummoned = false;
        } else {
            startDreaming = false;
        }
    }
    ...

这段代码主要用于确定 设备是否可以看是dreaming。 除去前置条件的限制外,此处结果主要由canDreamLocked和canDozeLocked决定。canDreamLocked的实现如下:

private boolean canDreamLocked() {
    //mWakefulness等于WAKEFULNESS_DREAMING
    if (mWakefulness != WAKEFULNESS_DREAMING 
            || !mDreamsSupportedConfig //设备支持dreaming
            || !mDreamsEnabledSetting  //设置开关开启
            || !mDisplayPowerRequest.isBrightOrDim() //屏幕熄灭
            || (mUserActivitySummary & (USER_ACTIVITY_SCREEN_BRIGHT
                    | USER_ACTIVITY_SCREEN_DIM | USER_ACTIVITY_SCREEN_DREAM)) == 0
            || !mBootCompleted) {
        return false;
    }
    
    //不处于唤醒态
    if (!isBeingKeptAwakeLocked()) {
        //没充电,电源选项也未配置,不可dreaming
        if (!mIsPowered && !mDreamsEnabledOnBatteryConfig) {
            return false;
        }
        //没充电,且电池电量过低,不可dreaming
        if (!mIsPowered
                && mDreamsBatteryLevelMinimumWhenNotPoweredConfig >= 0
                && mBatteryLevel < mDreamsBatteryLevelMinimumWhenNotPoweredConfig) {
            return false;
        }
        //充电,但电池电量过低,不可dreaming
        if (mIsPowered
                && mDreamsBatteryLevelMinimumWhenPoweredConfig >= 0
                && mBatteryLevel < mDreamsBatteryLevelMinimumWhenPoweredConfig) {
            return false;
        }
    }
    return true;
}

从上面的代码可以看出,充电和未充电分别有一个最低的dreaming电量门限,同时 dreaming除了对终端当前的状态、配置项有关外,在非唤醒状态下还与当前的电池电量有关系。

canDozeLocked的实现简单很多,仅仅是 mWakefulness的一个判断,代码如下:

private boolean canDozeLocked() {
    return mWakefulness == WAKEFULNESS_DOZING;
}

这个过程就是 判定 是否可以进入屏保状态。接下来 满足条件 则进入到屏保状态

4.2 在必要时,进入屏保状态

进入屏保状态后,这一部分就开始进行实际的工作。该部分代码如下:

    ...
    //2 表示是否正在屏保
    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);
        }
        //startDream成功后,一般isDreaming就会返回true
        isDreaming = mDreamManager.isDreaming();
    } else {
        isDreaming = false;
    }
    ...

 

mDreamManager为DreamManagerService的Binder代理。 我们重点看看DreamManagerService的startDream函数,stopDream的工作内容与startDream相反。

public void startDream(boolean doze) {
    startDreamInternal(doze);
}

继续分析startDreamInternal,代码如下:

private void startDreamInternal(boolean doze) {
    final int userId = ActivityManager.getCurrentUser();
    //获取屏保对象
    final ComponentName dream = chooseDreamForUser(doze, userId);
    if (dream != null) {
        synchronized (mLock) {
            startDreamLocked(dream, false /*isTest*/, doze, userId);
        }
    }
}

继续分析startDreamLocked,代码如下:

private void startDreamLocked(final ComponentName name,
        final boolean isTest, final boolean canDoze, final int userId) {
    //申请的屏保与当前的一致,不用进行修改
    if (Objects.equal(mCurrentDreamName, name)
            && mCurrentDreamIsTest == isTest
            && mCurrentDreamCanDoze == canDoze
            && mCurrentDreamUserId == userId) {
        return;
    }
    //停止当前屏保
    stopDreamLocked(true /*immediate*/);
    final Binder newToken = new Binder();
    mCurrentDreamToken = newToken;
    mCurrentDreamName = name;
    mCurrentDreamIsTest = isTest;
    mCurrentDreamCanDoze = canDoze;
    mCurrentDreamUserId = userId;

    mHandler.post(new Runnable() {
        @Override
        public void run() {
            //调用DreamController的startDream函数
            mController.startDream(newToken, name, isTest, canDoze, userId);
        }
    });
}

继续分析 DreamController 的 startDream 函数,代码实现如下:

public void startDream(Binder token, ComponentName name,
        boolean isTest, boolean canDoze, int userId) {
	//移除当前屏保并回调通知
	stopDream(true /*immediate*/);
    //...
        // Close the notification shade. Don't need to send to all, but better to be explicit.
        mContext.sendBroadcastAsUser(mCloseNotificationShadeIntent, UserHandle.ALL);
        mCurrentDream = new DreamRecord(token, name, isTest, canDoze, userId);

        try {//屏幕相关的准备工作
            mIWindowManager.addWindowToken(token, WindowManager.LayoutParams.TYPE_DREAM);
        } catch (RemoteException ex) {
            stopDream(true /*immediate*/);
            return;
        }

        Intent intent = new Intent(DreamService.SERVICE_INTERFACE);
        intent.setComponent(name);
        intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
        try { //拉起屏保服务
            if (!mContext.bindServiceAsUser(intent, mCurrentDream,
                    Context.BIND_AUTO_CREATE, new UserHandle(userId))) {
                stopDream(true /*immediate*/);
                return;
            }
        } catch (SecurityException ex) {
            stopDream(true /*immediate*/);
            return;
        }

        mCurrentDream.mBound = true;
        //在DREAM_CONNECTION_TIMEOUT到期时,bind服务还未成功,runnable就负责结束dream
        mHandler.postDelayed(mStopUnconnectedDreamRunnable, DREAM_CONNECTION_TIMEOUT);
    //...
}

这里 所谓的屏保其实就是拉起一个特殊的服务。

4.3 更新屏保状态

    ...
    //3 更新屏保状态.
    synchronized (mLock) {
        //记录进入屏保时的电池电量
        if (startDreaming && isDreaming) {
            mBatteryLevelWhenDreamStarted = mBatteryLevel;
        }

        if (mSandmanSummoned || mWakefulness != wakefulness) {
            return; // wait for next cycle
        }

        // Determine whether the dream should continue.
        if (wakefulness == WAKEFULNESS_DREAMING) {
            if (isDreaming && canDreamLocked()) {
                if (mDreamsBatteryLevelDrainCutoffConfig >= 0
                        && mBatteryLevel < mBatteryLevelWhenDreamStarted
                                - mDreamsBatteryLevelDrainCutoffConfig
                        && !isBeingKeptAwakeLocked()) {
                } else {
                    return; // continue dreaming
                }
            }

            // Dream has ended or will be stopped.  Update the power state.
            if (isItBedTimeYetLocked()) {
                //休眠
                goToSleepNoUpdateLocked(SystemClock.uptimeMillis(),
                        PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, 0, Process.SYSTEM_UID);
                updatePowerStateLocked();
            } else {
                //唤醒
                wakeUpNoUpdateLocked(SystemClock.uptimeMillis(), Process.SYSTEM_UID);
                updatePowerStateLocked();
            }
        //如果处于Doze状态,在power键灭屏时,首次会将wakefulness设置为该值
        } else if (wakefulness == WAKEFULNESS_DOZING) {
            if (isDreaming) {
                return; // continue dozing
            }

            //进入asleep状态
            reallyGoToSleepNoUpdateLocked(SystemClock.uptimeMillis(), Process.SYSTEM_UID);
            updatePowerStateLocked();
        }
    }
    
    //如果正处在Dream,则只要触发updatePowerStateLocked(),立即退出Dream
    if (isDreaming) {
        mDreamManager.stopDream(false /*immediate*/);
    }
    ...

说明:每次更新状态后都会重新调用updatePowerStateLocked,然后再次进入到handleSandman函数中。整个过程就是就是 在条件合适后,这个方法中将启动屏保。

5 发送通知 并 更新 wakelock锁

5.1 发送通知 finishWakefulnessChangeLocked 分析

private void finishWakefulnessChangeLocked() {
    if (mWakefulnessChanging) {
        //通过Notifier进行wakefulness改变后的处理
        mNotifier.onWakefulnessChangeFinished(mWakefulness);
        mWakefulnessChanging = false;
    }
}

这里 Notifier好比PMS的一个喇叭,用来发送广播,和其他组件交互等。

5.2 更新wakelock锁 updateSuspendBlockerLocked 分析

private void updateSuspendBlockerLocked() {
    //根据是否有CPU的wakelock,来决定cpu是保持否唤醒
    final boolean needWakeLockSuspendBlocker = ((mWakeLockSummary & WAKE_LOCK_CPU) != 0);
    //根据前面屏幕相关的状态,来决定是否需要持有屏幕的锁
    final boolean needDisplaySuspendBlocker = needDisplaySuspendBlockerLocked();
    //屏幕如果不需要保持开启状态,那么可自动熄灭
    final boolean autoSuspend = !needDisplaySuspendBlocker;
    //应该是表示屏幕是否是可交互的
    final boolean interactive = mDisplayPowerRequest.isBrightOrDim();

    if (!autoSuspend && mDecoupleHalAutoSuspendModeFromDisplayConfig) {
        //通过native函数,调用底层的autosuspend_disable
        setHalAutoSuspendModeLocked(false);
    }

    //在需要的情况下,获取CPU和屏幕的锁
    if (needWakeLockSuspendBlocker && !mHoldingWakeLockSuspendBlocker) {
        mWakeLockSuspendBlocker.acquire();
        mHoldingWakeLockSuspendBlocker = true;
    }
    if (needDisplaySuspendBlocker && !mHoldingDisplaySuspendBlocker) {
        mDisplaySuspendBlocker.acquire();
        mHoldingDisplaySuspendBlocker = true;
    }
    
    if (mDecoupleHalInteractiveModeFromDisplayConfig) {
        if (interactive || mDisplayReady) {
            //调用底层动态库的setInteractive函数,决定终端是否可以进行交互
            setHalInteractiveModeLocked(interactive);
        }
    }
    
    //如果不需要,则释放CPU和屏幕的锁 
    if (!needWakeLockSuspendBlocker && mHoldingWakeLockSuspendBlocker) {
        mWakeLockSuspendBlocker.release();
        mHoldingWakeLockSuspendBlocker = false;
    }
    if (!needDisplaySuspendBlocker && mHoldingDisplaySuspendBlocker) {
        mDisplaySuspendBlocker.release();
        mHoldingDisplaySuspendBlocker = false;
    }

    //如果需要设置自动休眠模式
    if (autoSuspend && mDecoupleHalAutoSuspendModeFromDisplayConfig) {
        setHalAutoSuspendModeLocked(true);
    }
}

这里 needDisplaySuspendBlockerLocked 方法决定 屏幕是否关闭,代码实现如下:

private boolean needDisplaySuspendBlockerLocked() {
    if (!mDisplayReady) {//显示设备没准备好,不能关闭屏幕
        return true;
    }
    //如果屏幕开着/变暗
    if (mDisplayPowerRequest.isBrightOrDim()) {
        //距离传感器 没有工作,不能关闭屏幕
        if (!mDisplayPowerRequest.useProximitySensor || !mProximityPositive
                || !mSuspendWhenScreenOffDueToProximityConfig) {
            return true;
        }
    }
    if (mScreenBrightnessBoostInProgress) {
        return true;
    }
    // Let the system suspend if the screen is off or dozing.
    return false; //可以关闭屏幕
}

needDisplaySuspendBlockerLocked主要根据屏幕的状态来决定,如果屏幕开着 或 处于 变暗状态时,近距离传感器 也没有工作,不能关闭屏幕。

通过本次源码分析,我们能看出仅管理当前的状态,涉及的细节就非常的琐碎。 而屏幕和CPU的实际控制,还牵扯到大量其它对象和HAL层代码。 Android电源的管理实际上是基于Linux 电源管理策略的,因此若要真正掌握,还需要对Linux的电源管理策略作进一步的了解。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

图王大胜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值