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


本章关键点总结 & 说明:

Android Framework 电源子系统(03)核心方法updatePowerStateLocked分析-1 更新基本状态_ide

本章节主要关注➕ updatePowerStateLocked 方法中 更新基本状态 部分 即可。该章节 主要是 对updatePowerStateLocked中 更新状态 流程的详细解读。


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个章节进行分析。本章节是 1部分。


1 更新基本状态

1.1 updateIsPoweredLocked实现

代码分析如下:

private void updateIsPoweredLocked(int dirty) {
    if ((dirty & DIRTY_BATTERY_STATE) != 0) {
        //电池状态发生变化...
        final boolean wasPowered = mIsPowered;
        final int oldPlugType = mPlugType;
        final boolean oldLevelLow = mBatteryLevelLow;
        mIsPowered = mBatteryManagerInternal.isPowered(BatteryManager.BATTERY_PLUGGED_ANY);
        //主要通过 BatteryService 来 更新下面几个成员变量的值
        //充电类型
        mPlugType = mBatteryManagerInternal.getPlugType();
        //当前电量
        mBatteryLevel = mBatteryManagerInternal.getBatteryLevel();
        //当前是否低电量
        mBatteryLevelLow = mBatteryManagerInternal.getBatteryLevelLow();

        if (wasPowered != mIsPowered || oldPlugType != mPlugType) {
            mDirty |= DIRTY_IS_POWERED;
            // Update wireless dock detection state.
            final boolean dockedOnWirelessCharger = mWirelessChargerDetector.update(
                    mIsPowered, mPlugType, mBatteryLevel);
            final long now = SystemClock.uptimeMillis();
            
            //插拔充电器或者USB是否唤醒屏幕
            if (shouldWakeUpWhenPluggedOrUnpluggedLocked(wasPowered, oldPlugType,
                    dockedOnWirelessCharger)) {
                //更新唤醒屏幕的信息
                wakeUpNoUpdateLocked(now, Process.SYSTEM_UID);
            }
            
            //触发一次用户活动,修改用户活动事件的时间,重置屏幕灭屏的时间
            userActivityNoUpdateLocked(
                    now, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);

            if (dockedOnWirelessCharger) {
                mNotifier.onWirelessChargingStarted();
            }
        }
        //如果充电状态或者低电量状态发生变化,调用updateLowPowerLocked 更新电源信息
        if (wasPowered != mIsPowered || oldLevelLow != mBatteryLevelLow) {
            if (oldLevelLow != mBatteryLevelLow && !mBatteryLevelLow) {
                mAutoLowPowerModeSnoozing = false;
            }
            updateLowPowerModeLocked();
        }
    }
}

该方法中更新了电池当前的电量,是否正常充电,充电类型,batteryLow的电量和mBatteryLevelLow是否是低电量状态,当电源的充电状态或者充电类型发生变化, 根据设置来决定是否唤醒系统。最后如果是充电状态或者是低电量状态时,执行updateLowPowerLocked,代码实现如下:

void updateLowPowerModeLocked() {
    //是否在充电,如果手机在充电,退出LowPowerMode模式
    /*变量说明:
    mLowPowerModeSetting 表示是否可以自动进入低电量省电模式,需满足条件:
     1.未充电 
     2.设置了可以自动进入低电量省电模式 
     3.目前处于低电量的状态
    mLowPowerModeEnabled 表示当前是否进入低电量省电模式
    */
    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() {
                //发送ACTION_POWER_SAVE_MODE_CHANGING的通知
                Intent intent = new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGING)
                        .putExtra(PowerManager.EXTRA_POWER_SAVE_MODE, mLowPowerModeEnabled)
                        .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
                mContext.sendBroadcast(intent);
                ArrayList<PowerManagerInternal.LowPowerModeListener> listeners;
                synchronized (mLock) {
                    listeners = new ArrayList<PowerManagerInternal.LowPowerModeListener>(
                            mLowPowerModeListeners);
                }
                for (int i=0; i<listeners.size(); i++) {
                    //回调关于该模式变化的监听
                    listeners.get(i).onLowPowerModeChanged(lowPowerModeEnabled);
                }
                //通知相关的服务Power Save Mode发生了变化
                intent = new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
                intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
                mContext.sendBroadcast(intent);
            }
        });
    }
}

注意:这里PowerUI接收到低电量省电模式的广播,就会弹出低电量省电模式的提醒界面。

1.2 updateStayOnLocked(mDirty)

该方法根据当前的充电状态,来判断充电的时候手机是否保持唤醒,代码实现如下:

private void updateStayOnLocked(int dirty) {
    //电源状态或电源设置发生了改变
    if ((dirty & (DIRTY_BATTERY_STATE | DIRTY_SETTINGS)) != 0) {
        final boolean wasStayOn = mStayOn;
        //设置了充电器插入时亮屏(分为AC充电亮屏、USB充电亮屏或无线充电亮屏)
        if (mStayOnWhilePluggedInSetting != 0
                //初始为Integer.MAX_VALUE 对应 强制息屏时间
                && !isMaximumScreenOffTimeoutFromDeviceAdminEnforcedLocked()) {
            //判断是否充电亮屏,设置mStayOnWhilePluggedInSetting 就会亮屏
            mStayOn = mBatteryManagerInternal.isPowered(mStayOnWhilePluggedInSetting);
        } else {
            mStayOn = false;
        }

        if (mStayOn != wasStayOn) {
            mDirty |= DIRTY_STAY_ON;
        }
    }
}

这里主要是更新变量mStayOn的值,如果mStayOn如果为true,则屏幕长亮。在Setting中可以设置充电时候屏幕长亮,如果Setting中设置了该选项,updateIsPoweredLocked检查到正在充电,会将mStayOn置为true。

1.3 updateScreenBrightnessBoostLocked方法

这里从PowerManagerService的 对外接口 boostScreenBrightness 来分析,逐步到updateScreenBrightnessBoostLocked方法实现,该方法 是让屏幕在一段时间内保持最大亮度,代码实现如下:

public void boostScreenBrightness(long eventTime) {
    if (eventTime > SystemClock.uptimeMillis()) {
        throw new IllegalArgumentException("event time must not be in the future");
    }

    mContext.enforceCallingOrSelfPermission(
            android.Manifest.permission.DEVICE_POWER, null);

    final int uid = Binder.getCallingUid();
    final long ident = Binder.clearCallingIdentity();
    try {
        boostScreenBrightnessInternal(eventTime, uid);
    } finally {
        Binder.restoreCallingIdentity(ident);
    }
}

这里主要分析boostScreenBrightnessInternal,代码如下:

private void boostScreenBrightnessInternal(long eventTime, int uid) {
    synchronized (mLock) {
        //系统没有准备好或者当前为Asleep状态, 不处理新到的事件
        if (!mSystemReady || mWakefulness == WAKEFULNESS_ASLEEP
                //过时的事件不处理
                || eventTime < mLastScreenBrightnessBoostTime) {
            return;
        }

        //最亮屏幕状态的起始时间
        mLastScreenBrightnessBoostTime = eventTime;
        //设置最亮屏幕的标志位true 
        mScreenBrightnessBoostInProgress = true;
        //修改mDirty的值,表示最大屏幕亮度的状态发生了变化
        mDirty |= DIRTY_SCREEN_BRIGHTNESS_BOOST;
        //记录
        userActivityNoUpdateLocked(eventTime,
                PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, uid);
        //更新电源的状态信息
        updatePowerStateLocked();
    }
}

最后,调用updatePowerStateLocked方法更新电源状态信息。 我们已经知道,updatePowerStateLocked将会调用到updateScreenBrightnessBoostLocked,该方法主要用来更新屏幕是否保持最大亮度,代码实现如下:

private void updateScreenBrightnessBoostLocked(int dirty) {
    //根据mDirty的标志位来判断最大屏幕亮度是否发生了变化
    if ((dirty & DIRTY_SCREEN_BRIGHTNESS_BOOST) != 0) {
        //如果当前处于最大屏幕亮度
        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;
            //触发一次用户活动,写入mDirty标志位
            userActivityNoUpdateLocked(now,
                    PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);
            //注意:在后面的方法中调用updateDisplayPowerStateLocked方法中来更新屏幕亮度。
        }
    }
}

如果最大屏幕亮度发生了变化,才会执行该方法。至此,PMS 更新基本状态信息的流程结束。