前面的博客中,我们已经分析过,当Android中的进程要使用电量时,需要向PMS申请WakeLock;当进程完成工作后,需要释放对应的WakeLock。
PMS收到申请和释放WakeLock的请求后,均需要调用updatePowerStateLocked来更新电源的状态,该函数是PMS的核心方法。
接下来,我们就结合代码,看一下updatePowerStateLocked的工作流程。
/**
* Updates the global power state based on dirty bits recorded in mDirty.
*
* This is the main function that performs power state transitions.
* We centralize them here so that we can recompute the power state completely
* each time something important changes, and ensure that we do it the same
* way each time. The point is to gather all of the transition logic here.
*/
private void updatePowerStateLocked() {
//未启动完毕或mDirty没有记录变化
if (!mSystemReady || mDirty == 0) {
return;
}
..........
try {
// Basic state updates.
// 1、更新基本状态
updateIsPoweredLocked(mDirty);
updateStayOnLocked(mDirty);
updateScreenBrightnessBoostLocked(mDirty);
// Update wakefulness.
// Loop because the wake lock and user activity computations are influenced
// by changes in wakefulness.
// 2、更新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;
}
}
// Update display power state.
// 3、更新display power state
boolean displayBecameReady = updateDisplayPowerStateLocked(dirtyPhase2);
// Update dream state (depends on display ready signal).
// 4、更新dream state
updateDreamLocked(dirtyPhase2, displayBecameReady);
// Send notifications, if needed.
finishWakefulnessChangeIfNeededLocked();
// Update suspend blocker.
// Because we might release the last suspend blocker here, we need to make sure
// we finished everything else first!
// 5、更新suspend blocker
updateSuspendBlockerLocked();
} finally {
..........
}
}
在PMS中有个很重要的变量mDirty,该变量按位存储PMS中的各种变化状态。
例如,之前介绍PMS的acquire WakeLock流程时,就进行了以下操作:
.........
mDirty |= DIRTY_WAKE_LOCKS;
........
每当PMS检测到一些重要事件发生时,就会更新mDirty的相应的位。
从updatePowerStateLocked的代码可以看出,它将根据mDirty中的信息,来更新手机中的电源状态。
根据Android源码中的注释,可以看出updatePowerStateLocked的工作主要分为几个步骤,接下来我们一个一个步骤的来进行分析。
一、更新基本状态信息
1、updateIsPoweredLocked
我们先来看看updateIsPoweredLocked函数:
private void updateIsPoweredLocked(int dirty) {
//DIRTY_BATTERY_STATE位置1时,表示终端的电源状态发生了改变
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);
//得到充电的类型
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, "android.server.power:POWER", Process.SYSTEM_UID,
mContext.getOpPackageName(), Process.SYSTEM_UID);
}
//触发一次用户活动,修改PMS中记录用户活动事件的时间,同时通知BatteryStatsService等
userActivityNoUpdateLocked(
now, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);
// Tell the notifier whether wireless charging has started so that
// it can provide feedback to the user.
//无线充电相关的通知,暂时可以不管
if (dockedOnWirelessCharger) {
mNotifier.onWirelessChargingStarted();
}
}
if (wasPowered != mIsPowered || oldLevelLow != mBatteryLevelLow) {
//结束低电的状态
if (oldLevelLow != mBatteryLevelLow && !mBatteryLevelLow) {
........
//从命名来看,该标志用于决定终端在低电模式下是否“打盹”(接近休眠)
mAutoLowPowerModeSnoozing = false;
}
//更新低电模式相关的操作
updateLowPowerModeLocked();
}
}
}
从以上代码可以看出updateIsPoweredLocked主要用于:
更新PMS中的一些变量,包括记录终端是否在充电、充电的类型、电池的电量及电池电量是否处于低电状态;
当电源的充电状态,或者充电类型发生变化,判断出现插拔充电器等操作时,是否需要点亮或熄灭屏幕;
当电源充电状态发生变化,或者终端是否处于低电量的标志发生变化的时候,终端调用updateLowPowerModeLocked()更新低电模式相关的操作。
我们跟进一下updateLowPowerModeLocked函数:
private void updateLowPowerModeLocked() {
//处于充电状态,并且设置过低电模式的标志位
if (mIsPowered && mLowPowerModeSetting) {
........
// Turn setting off if powered
//更新数据库,关闭低电模式
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;
//调用底层动态库的powerHint函数
powerHintInternal(POWER_HINT_LOW_POWER, lowPowerModeEnabled ? 1 : 0);
//开机完成后才能执行的Runnable对象
postAfterBootCompleted(new Runnable() {
//发送低电模式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);
//PMS提供了registerLowPowerModeObserver的接口
//其它进程可以调用该接口,注册观察者
synchronized (mLock) {
listeners = new ArrayList<PowerManagerInternal.LowPowerModeListener>(
mLowPowerModeListeners);
}
for (int i=0; i<listeners.size(); i++) {
//调用回调接口的onLowPowerModeChanged函数,通知其它进程低电模式发生改变
listeners.get(i).onLowPowerModeChanged(lowPowerModeEnabled);
}
//再次发送CHANGED广播
intent = new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
mContext.sendBroadcast(intent);
// Send internal version that requires signature permission.
mContext.sendBroadcastAsUser(new Intent(
PowerManager.ACTION_POWER_SAVE_MODE_CHANGED_INTERNAL), UserHandle.ALL,
Manifest.permission.DEVICE_POWER);
});
}
}
从上面的代码可以看出updateLowPowerModeLocked函数,
首先判断手机是否在充电,如果手机在充电,退出LowPowerMode模式,同时更新数据库;
当手机的低电量模式发生了变化,就发送广播进行通知,并回调关于监听该模式变化的观察者的接口。
例如:UI对应的APK收到低电量省电模式的广播,就会弹出低电量省电模式的提醒界面。
可以看出这一部分除了更新PMS中的一些变量外,关注的重点还是集中在:
充电状态是否改变;
充电状态的改变,将引出对充电器插拔是否需要亮屏的考虑;
同样,充电状态的改变,将引出对终端的低电模式是否发生改变的考虑。
从这个角度来看,updateIsPoweredLocked函数的命名是实至名归的。
2、updateStayOnLocked
现在我们看看基本状态更新第二部分的updateStayOnLocked函数:
/**
* Updates the value of mStayOn.
* Sets DIRTY_STAY_ON if a change occurred.
*/
private void updateStayOnLocked(int dirty) {
//电源状态或电源设置发生了改变
if ((dirty & (DIRTY_BATTERY_STATE | DIRTY_SETTINGS)) != 0) {
final boolean wasStayOn = mStayOn;
//设置了充电器插入时亮屏(分为AC充电亮屏、USB充电亮屏或无线充电亮屏)
if (mStayOnWhilePluggedInSetting != 0
//判断mMaximumScreenOffTimeoutFromDeviceAdmin的是否处于0与Integer.MAX_VALUE之间
//Android给出的注释是:
//The maximum allowable screen off timeout according to the device
// administration policy
//初始为Integer.MAX_VALUE,因此这里是要求其它进程没有设置这个值
//应该对应于强制息屏时间
&& !isMaximumScreenOffTimeoutFromDeviceAdminEnforcedLocked()) {
//判断是否充电亮屏,定义于BatteryService.java中
//从代码来看,只要mStayOnWhilePluggedInSetting设置了,就会亮屏
mStayOn = mBatteryManagerInternal.isPowered(mStayOnWhilePluggedInSetting);
} else {
mStayOn = false;
}
if (mStayOn != wasStayOn) {
mDirty |= DIRTY_STAY_ON;
}
}
}
这一部分的代码功能比较单一,主要用于更新变量mStayOn的值。
如果mStayOn如果为true,则屏幕保持长亮的状态。
3、updateScreenBrightnessBoostLocked
Android手机定义了一个最大屏幕亮度,用户可以手动或者让终端自动确定最大的屏幕亮度。
updateScreenBrightnessBoostLocked函数主要用于:更新终端可处于最大屏幕亮度的时间。
为了比较