本文基于Android 11源码分析
前言
在Andorid Framework层中,PowerManagerService(以下简称PMS)模块负责协调、管理设备CPU资源,应用层及框架层其他组件对CPU的资源请求就是通过PowerManager模块进行。因此,一些影响系统资源调度的逻辑,都是在PMS模块中实现的。如系统亮灭屏、WakeLock管理、Dreamland、休眠时间等。
PMS模块中的核心服务是PowerManagerService,该类继承自SystemService类,具有SystemService子类的共性,如:有生命周期方法、由SystemServer启动、注册到系统服务中、通过Binder和其他组件进行交互等。在系统启动过程中,统一由SystemServer进行各个阶段的处理。
所有的SystemService类,启动过程中都会依次执行以下生命周期方法:
构造方法:通过反射调用,获取实例;
onStart()方法:开启对应的SystemService;
onBootPhase()方法:在SystemService服务的启动过程中指定服务的启动阶段,每个阶段指定特定的工作。
1.PMS的启动流程
当system_server启动后,PMS将由SystemServer通过反射的方式启动。首先,在SystemServer的main()方法中,调用了自身的run()方法,并在run()方法中启动三类服务:
引导服务:其他系统服务依赖性比较高的服务;
核心服务:一些依赖性不高的基本必要服务;
其他服务:一些乱七八糟的系统服务。
// frameworks/base/services/java/com/android/server/SystemServer.java
private void run() {
//......
try {
startBootstrapServices();//启动引导服务
startCoreServices();//启动核心服务
startOtherServices();//启动其他服务
} catch (Throwable ex) {
}
......
}
其中引导服务中启动中就包括了PMS:
// frameworks/base/services/java/com/android/server/SystemServer.java
private void startBootstrapServices() {
......
//通过SystemManagerService启动PMS服务
mPowerManagerService = mSystemServiceManager.startService(PowerManagerService.class);
......
}
可见其他服务对PMS的依赖还是很高的。
在SystemServiceManager的startService()方法中,将通过反射的方式得到PMS对象:
// frameworks/base/services/core/java/com/android/server/SystemServiceManager.java
public SystemService startService(String className) {
final Class<SystemService> serviceClass;
serviceClass = (Class<SystemService>)Class.forName(className);
return startService(serviceClass);
}
public <T extends SystemService> T startService(Class<T> serviceClass) {
try {
final String name = serviceClass.getName();
// Create the service.
final T service;
try {
// 创建PMS对象
Constructor<T> constructor = serviceClass.getConstructor(Context.class);
service = constructor.newInstance(mContext);
} catch (InstantiationException ex) {
}
startService(service);
return service;
}
}
public void startService(@NonNull final SystemService service) {
// Register it.
mServices.add(service);
// Start it.
long time = SystemClock.elapsedRealtime();
try {
// 执行onStart()
service.onStart();
} catch (RuntimeException ex) {
}
}
在获取了PMS实例后,首先将该实例添加到了一个ArrayList中,这个List中保存了所有的SystemService,在之后的启动流程中会对整个List中的元素进行方法的回调。
然后,调用PMS的onStart()方法。下面就分别来看下这两个方法中的内容。
1.1 构造方法进行初始化
PMS构造方法如下:
// frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
PowerManagerService(Context context, Injector injector) {
super(context);
mContext = context;
mBinderService = new BinderService(); // 用于IPC交互
mLocalService = new LocalService(); // 用户跨线程交互
// 创建一个NativeWrapper对象,管理所有的native交互
mNativeWrapper = injector.createNativeWrapper();
// 内部类Injector对象,管理创建其他对象
mInjector = injector;
// 创建PMS主线程
mHandlerThread = new ServiceThread(TAG,
Process.THREAD_PRIORITY_DISPLAY, false /*allowIo*/);
mHandlerThread.start();
// 创建PMS主线程Handler
mHandler = new PowerManagerHandler(mHandlerThread.getLooper());
// 管理Settings.Global下的常量
mConstants = new Constants(mHandler);
// 创建AmbientDisplayConfiguration对象,管理一些显示配置,如AOD
mAmbientDisplayConfiguration = mInjector.createAmbientDisplayConfiguration(context);
// 创建AttentionDetector对象,用来检查用户活动是否需要再更新
mAttentionDetector = new AttentionDetector(this::onUserAttention, mLock);
// 创建BatterySavingStats对象,用来记录电池耗电率
mBatterySavingStats = new BatterySavingStats(mLock);
// 创建BatterySaverPolicy对象,管理一些省电策略
mBatterySaverPolicy =
mInjector.createBatterySaverPolicy(mLock, mContext, mBatterySavingStats);
// 创建BatterySaverController对象,管理省电策略的切换
mBatterySaverController = new BatterySaverController(mLock, mContext,
BackgroundThread.get().getLooper(), mBatterySaverPolicy,
mBatterySavingStats);
// 创建BatterySaverStateMachine对象,负责省电策略的开启/关闭
mBatterySaverStateMachine = new BatterySaverStateMachine(
mLock, mContext, mBatterySaverController);
.......
synchronized (mLock) {
// 创建SuspendBlockerImpl对象,WakeLock的最终反映,"PowerManagerService.WakeLocks"类型对象负责保活CPU
mWakeLockSuspendBlocker =
mInjector.createSuspendBlocker(this, "PowerManagerService.WakeLocks");
// 创建SuspendBlockerImpl对象,"PowerManagerService.Display"类型对象负责保持屏幕常亮
mDisplaySuspendBlocker =
mInjector.createSuspendBlocker(this, "PowerManagerService.Display");
if (mDisplaySuspendBlocker != null) {
// 申请mDisplaySuspendBlocker,保持屏幕常亮
mDisplaySuspendBlocker.acquire();
mHoldingDisplaySuspendBlocker = true;
}
// auto-suspend模式是否可用
mHalAutoSuspendModeEnabled = false;
// 是否是可交互状态
mHalInteractiveModeEnabled = true;
// 设置设备状态为唤醒状态
mWakefulness = WAKEFULNESS_AWAKE;
// 静默模式,会控制背光的点亮,使用场景不多
sQuiescent = SystemProperties.get(SYSTEM_PROPERTY_QUIESCENT, "0").equals("1");
// mNativeWrapper对象进行native层初始化工作
mNativeWrapper.nativeInit(this);
mNativeWrapper.nativeSetAutoSuspend(false); //设置auto suspend状态
mNativeWrapper.nativeSetInteractive(true); // 设置interactive状态
mNativeWrapper.nativeSetFeature(POWER_FEATURE_DOUBLE_TAP_TO_WAKE, 0);// 设置双击唤醒Feature状态
}
}
以上方法中,做了大量的初始化工作,主要如下:
创建用于IPC的BinderService对象和system_server进程内跨线程交互的LocalService对象;
创建PMS的主线程,并使用该HandlerThread的Looper实例化PowerManagerHandler,将作为PMS主线程Handler进行Message的处理;
相关配置参数的读取,如系统配置各种亮度参数;
获取了两个Suspend锁对象,SuspendBlocker是一种锁机制,上层申请的wakelock锁在PMS中都会反映为SuspendBlocker锁;
通过mNativeWrapper对象和底层进行状态交互。
1.2 onStart()方法注册服务
构造方法执行完毕后,执行onStart()方法:
// frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
@Override
public void onStart() {
//发布到系统服务中
publishBinderService(Context.POWER_SERVICE, mBinderService);
//发布到本地服务
publishLocalService(PowerManagerInternal.class, mLocalService);
//设置Watchdog监听
Watchdog.getInstance().addMonitor(this);
Watchdog.getInstance().addThread(mHandler);
}
这个方法中会进行对PMS的Binder服务和Local服务的注册,BinderService注册后,其他模块中就可以通过ServiceManager获取到对应的Binder对象,进行IPC通信;LocalService注册后,在system_server进程内就可以通过获取LocalService对象跨线程交互。
PMS中的BinderService,是继承自IPowerManager.Stub的一个内部类BinderService,IPowerManager.Stub继承自Binder并且实现了IPowerManager,因此,PMS和其他模块的跨进程交互,实际上就是通过PMS.BinderService实现。
Binder注册的过程在ServiceManager中进行:
// frameworks/base/core/java/android/os/ServiceManager.java
@UnsupportedAppUsage
public static void addService(String name, IBinder service, boolean allowIsolated,
int dumpPriority) {
try {
getIServiceManager().addService(name, service, allowIsolated, dumpPriority);
} catch (RemoteException e) {
Log.e(TAG, "error in addService", e);
}
}
当通过ServiceManager注册后,就可以根据Context.POWER_SERVICE在其他服务中获得IPowerManager.Stub对象,进行跨进程交互了。
LocalService则用于system_server进程内部交互,注册的对象是另一个内部类——继承自PowerManagerInternal的LocalService(带有Internal的类一般都在System进程内使用)。Local Service的注册是在LocalServices中进行,其注册方法如下:
// frameworks/base/core/java/com/android/server/LocalServices.java
public static <T> void addService(Class<T> type, T service) {
synchronized (sLocalServiceObjects) {
if (sLocalServiceObjects.containsKey(type)) {
throw new IllegalStateException("Overriding service registration");
}
// 添加到一个Map中
sLocalServiceObjects.put(type, service);
}
}
可以看到,对Local Service的注册,是直接将其添加到了一个Map中。
1.3 onBootPhase()各启动阶段的处理
回到SystemServer中,startBootstrapServices()方法中PMS的实例化和注册流程执行完成了,接下来会SystemService的生命周期,会开始执行onBootPhase(),这个方法为所有的已进行实例化和注册的服务设置启动阶段,以便在不同的启动阶段进行不同的工作,方法如下:
// frameworks/base/services/core/java/com/android/server/SystemServiceManager.java
public void startBootPhase(final int phase) {
mCurrentPhase = phase;
try {
final int serviceLen = mServices.size();
for (int i = 0; i < serviceLen; i++) {
// 遍历调用SystemService的onBootPhase()
final SystemService service = mServices.get(i);
try {
service.onBootPhase(mCurrentPhase);
} catch (Exception ex) {
}
}
} finally {
}
}
在SystemServiceManager#startBootPhase()中,通过在SystemServiceManager中传入不同的形参,遍历SystemServiceManager#mServices列表,调用各个SystemService#onBootPhase(int)方法,根据参数在方法实现中完成不同的工作。在SystemService中定义了七个代表启动阶段的参数:
PHASE_WAIT_FOR_DEFAULT_DISPLAY:第一个启动阶段,用于在启动PKMS之前,需要确保已经存在默认逻辑屏,只有DisplayManagerService使用该阶段;
PHASE_LOCK_SETTINGS_READY:第二个启动阶段,该阶段的执行,意味这Lock Pattern/Password相关服务已经准备完毕,只有DisplayManagerService使用该阶段;
PHASE_SYSTEM_SERVICES_READY:第三个启动阶段,该阶段的执行,意味这其他服务可以安全地使用核心系统服务;
PHASE_DEVICE_SPECIFIC_SERVICES_READY:第四个启动阶段,该阶段的执行,意味这其他服务可以安全地使用设备指定的一些系统服务,这些服务在config_deviceSpecificSystemServices中进行配置;
PHASE_ACTIVITY_MANAGER_READY:第五个启动阶段,该阶段的执行,意味这其他AMS组件已经启动完成,可以进行广播操作;
PHASE_THIRD_PARTY_APPS_CAN_START:第六个启动阶段,该阶段的执行,意味这系统可以启动APP,并可以进行service的bind/start操作了;
PHASE_BOOT_COMPLETED:第七个启动阶段,该阶段的执行,意味这启动完成,Home应用也已经启动完成,并且可以和设备进行交互了。
PMS#onBootPhase()方法只对以上的2个阶段做了处理:
// frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
@Override
public void onBootPhase(int phase) {
synchronized (mLock) {
if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
// 统计系统启动次数,adb shell settings get global boot_count可查看
incrementBootCount();
} else if (phase == PHASE_BOOT_COMPLETED) {
final long now = SystemClock.uptimeMillis();
mBootCompleted = true; // 表示启动完成
// mDirty是一个位标记
mDirty |= DIRTY_BOOT_COMPLETED;
mBatterySaverStateMachine.onBootCompleted();
// 更新用户活动时间
userActivityNoUpdateLocked(
now, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);
// 更新全局状态信息
updatePowerStateLocked();
}
}
}
mDirty是一个二进制的标记位,用来表示电源状态哪一部分发生了改变,通过对其进行置位(|操作)、清零(~操作),得到二进制数各个位的值(0或1),进行不同的处理,这个变量非常重要。最后,调用updatePowerStateLocked()方法,这是整个PMS中最重要的方法,会在下面进行详细分析。
此时,启动过程中SystemService的生命周期方法全部执行完毕。
1.4 sytemReady()方法
对于PMS的启动,当执行完生命周期方法后,在SystemServer.startOtherServices()中还进行了一步操作:
// frameworks/base/services/java/com/android/server/SystemServer.java
private void startOtherServices() {
// ......
mPowerManagerService.systemReady(mActivityManagerService.getAppOpsService());
// ......
}
这个方法中主要做了以下几个操作,相关方法代码就不再粘贴:
获取各类本地服务和远程服务,如Dreamland服务(DreamMangerService)、窗口服务(PhoneWindowManager)、电池状态监听(BatteryService)等服务;
注册用于和其他SytemService交互的广播;
调用updateSettingsLocked()方法更新Settings中值的变化;
调用readConfigurationLocked()方法读取配置文件中的默认值;
注册SettingsObserver监听。
到此为止,PMS的启动过程完成。以上过程时序图如下:
2.核心方法updatePowerStateLocked()
updatePowerStateLocked()方法是整个PMS模块的核心方法,也是整个PSM中最重要的一个方法,它用来更新整个Power状态。当Power状态发生改变时,如亮灭屏、电池状态改变、暗屏、WakeLock锁申请/释放......都会调用该方法,并调用其他同级方法进行各个状态的更新:
// frameworks/base/services/core/java/com/android/server/SystemService.java
private void updatePowerStateLocked() {
if (!mSystemReady || mDirty == 0) { // 当mDirty变量值为0时,不进行任何更新
return;
}
try {
// Phase 0: 基本状态的更新
updateIsPoweredLocked(mDirty); // 更新充电状态:见2.1
updateStayOnLocked(mDirty); // 更新当前是否为屏幕常亮状态,由mStayOn控制:见2.2
updateScreenBrightnessBoostLocked(mDirty); // 更新是否需要增强亮度变量:见2.3
// Phase 1: 更新唤醒状态
// by changes in wakefulness.
final long now = mClock.uptimeMillis();
int dirtyPhase2 = 0;
// 循环进行更新流程,直到updateWakefulnessLocked()返回false
for (;;) {
int dirtyPhase1 = mDirty;
dirtyPhase2 |= dirtyPhase1;
mDirty = 0; //清空标记
// 更新用于统计wakelock的标记值mWakeLockSummary属性
updateWakeLockSummaryLocked(dirtyPhase1); // 见2.4
// 更新用于统计用户活动状态的标记值mUserActivitySummary属性
updateUserActivitySummaryLocked(now, dirtyPhase1); // 见2.5
// 更新细微模式状态
updateAttentiveStateLocked(now, dirtyPhase1); // 见2.6
// 更新唤醒状态,如果状态改变返回true
if (!updateWakefulnessLocked(dirtyPhase1)) { // 见2.7
break;
}
}
// Phase 2:
updateProfilesLocked(now); // 见2.8
// Phase 3: 更新display状态:见2.9
final boolean displayBecameReady = updateDisplayPowerStateLocked(dirtyPhase2);
// Phase 4: 更新Dreamland状态:见2.10
updateDreamLocked(dirtyPhase2, displayBecameReady);
// Phase 5: 如果wakefulness改变,做最后的收尾工作
finishWakefulnessChangeIfNeededLocked(); // 见2.11
// Phase 6: 更新SuspendBlocker锁状态
updateSuspendBlockerLocked(); // 见2.12
}
}
下面对以上所有方法逐个进行分析。
2.1 updateIsPoweredLocked()更新充电状态
这个方法用于更新mIsPowered属性,它代表当前的充电状态。插拔USB点亮屏幕功能的逻辑,就是在这个方法中。该方法如下:
// frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
private void updateIsPoweredLocked(int dirty) {
if ((dirty & DIRTY_BATTERY_STATE) != 0) { // 只有有标记位DIRTY_BATTERY_STATE时,才会执行这个方法
// 记录旧值
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; // 设置标记位DIRTY_IS_POWERED
// 更新无线充电状态
final boolean dockedOnWirelessCharger = mWirelessChargerDetector.update(
mIsPowered, mPlugType);
final long now = SystemClock.uptimeMillis();
// 插拔USB是否需要亮屏
if (shouldWakeUpWhenPluggedOrUnpluggedLocked(wasPowered, oldPlugType,
dockedOnWirelessCharger)) {
// 亮屏流程
wakeUpNoUpdateLocked(now, PowerManager.WAKE_REASON_PLUGGED_IN,
"android.server.power:PLUGGED:" + mIsPowered, Process.SYSTEM_UID,
mContext.getOpPackageName(), Process.SYSTEM_UID);
}
// 更新用户活动时间
userActivityNoUpdateLocked(
now, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);
// 播放充电提示音和动画
if (mBootCompleted) { //只有在启动完成后,才会播放
if (mIsPowered && !BatteryManager.isPlugWired(oldPlugType)
&& BatteryManager.isPlugWired(mPlugType)) {
mNotifier.onWiredChargingStarted(mForegroundProfile);
} else if (dockedOnWirelessCharger) {
mNotifier.onWirelessChargingStarted(mBatteryLevel, mForegroundProfile);
}
}
}
// 将充电状态更新给BatterySaverStateMachine
mBatterySaverStateMachine.setBatteryStatus(mIsPowered, mBatteryLevel, mBatteryLevelLow);
}
}
只有对mDirty设置了DIRTY_BATTERY_STATE标记时,那么DIRTY_BATTERY_STATE在什么时候会设置呢?当电池信息变化后,由healthd模块上报给BatteryService,BatteryService中则会发出广播Intent.ACTION_BATTERY_CHANGED来通知其他组件,PMS中会接收这个广播并作出处理:
// frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
private void handleBatteryStateChangedLocked() {
mDirty |= DIRTY_BATTERY_STATE;
updatePowerStateLocked();
}
因此,只要电池状态发生变化,就会执行这个方法。在这个方法中:
首先,更新一些如mIsPowered等全局变量;
然后,通过shouldWakeUpWhenPluggedOrUnpluggedLocked()方法判断是否需要亮屏,这就是插拔USB点亮屏幕功能的逻辑;
接下来,执行userActivityNoUpdateLocked()方法更新用户活动时间,这个时间决定了何时会自动灭屏,用户每次操作手机(触摸、按键、Other)都会更新到当前时间,用户最后活动时间 + 设置自动休眠时间 = 最终自动灭屏的时间点,这个方法会在后面部分分析;
再接下来,则调用Notifier对象播放插拔USB音效或动画,Notifier类是PMS模块中用于发送广播、异步通知其他组件的一个类;
最后,将充电状态传递给mBatterySaverStateMachine中,进行省电策略的调整。
2.2 updateStayOnLocked()更新屏幕常亮状态
这个方法用来更新全局变量mStayOn的值,"开发者选项—不锁定屏幕"这个选项开启后,则在充电时将保持常亮不会自动休眠:
// frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
private void updateStayOnLocked(int dirty) {
if ((dirty & (DIRTY_BATTERY_STATE | DIRTY_SETTINGS)) != 0) {
final boolean wasStayOn = mStayOn;
if (mStayOnWhilePluggedInSetting != 0
&& !isMaximumScreenOffTimeoutFromDeviceAdminEnforcedLocked()) {
// 如果任意方式充电(AC/USB/wireless),返回true
mStayOn = mBatteryManagerInternal.isPowered(mStayOnWhilePluggedInSetting);
} else {
mStayOn = false;
}
// 状态发生变化时,向mDirty设置DIRTY_STAY_ON标记
if (mStayOn != wasStayOn) {
mDirty |= DIRTY_STAY_ON;
}
}
}
只有mDirty设置了DIRTY_BATTERY_STATE或DIRTY_SETTINGS标记位后,才会执行该方法。DIRTY_BATTERY_STATE在电池状态发生变化后设置,DIRTY_SETTINGS是在Settings中的值发生变化后设置,mStayOnWhilePluggedInSetting就是从来自于Settings中"不锁定屏幕"的值,如果发生变化,且处于充电状态,则会更新mStayOn变量的值。在自动灭屏流程中,一旦mStayOn值为true,则永远不会灭屏,从而实现了“不锁定屏幕”这个功能。
2.3 updateScreenBrightnessBoostLocked()更新是否增强亮度
这个方法会更新表示增强亮度的全局变量mScreenBrightnessBoostInProgress,PMS.BinderService提供了boostScreenBrightness()方法,允许其他组件通过该接口将亮度调节到最大,并保持5s后恢复:
// frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
private void updateScreenBrightnessBoostLocked(int dirty) {
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;
// 5s后会再次更新
if (boostTimeout > now) {
Message msg = mHandler.obtainMessage(MSG_SCREEN_BRIGHTNESS_BOOST_TIMEOUT);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, boostTimeout);
return;
}
}
// 表示增强亮度结束
mScreenBrightnessBoostInProgress = false;
mNotifier.onScreenBrightnessBoostChanged();
// 更新用户活动时间
userActivityNoUpdateLocked(now,
PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);
}
}
}
只有当mDirty设置了DIRTY_SCREEN_BRIGHTNESS_BOOST标记时,才会执行这个方法。这个标记位就是通过boostScreenBrightness()设置,这个功能在Google原生逻辑中有一个使用场景:
// frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
mDoublePressOnPowerBehavior = mContext.getResources().getInteger(
com.android.internal.R.integer.config_doublePressOnPowerBehavior);
将config_doublePressOnPowerBehavior配置为2后,双击power键会通过上述方法将增强亮度到最大。
2.4 updateWakeLockSummaryLocked()更新WakeLock统计值
从这个方法开始到,直到updateWakefulnessLocked()结束,将会在一个for循环中执行。
该方法用来更新mWakeLockSummary属性,它是用来记录所有WakeLock锁状态的状态值,代表了所有的WakeLock,在请求Display状时作为判断条件确定具体的请求状态。系统规定了系统休眠状态对WakeLock锁的使用影响:如当系统休眠后,常亮锁(PowerManager.SCREEN_BRIGHT等)将会被忽略;系统唤醒后,Doze锁(PowerManager.DOZE_WAKE_LOCK等)将会被忽略:
// frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
private void updateWakeLockSummaryLocked(int dirty) {
if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_WAKEFULNESS)) != 0) {
mWakeLockSummary = 0; // 置为0
// 将每个ProfilePowerState的mWakeLockSummary也进行重置
final int numProfiles = mProfilePowerState.size();
for (int i = 0; i < numProfiles; i++) {
mProfilePowerState.valueAt(i).mWakeLockSummary = 0;
}
// 遍历mWakeLocks列表
final int numWakeLocks = mWakeLocks.size();
for (int i = 0; i < numWakeLocks; i++) {
final WakeLock wakeLock = mWakeLocks.get(i);
// 获取每个WakeLock对应的Flag标记
final int wakeLockFlags = getWakeLockSummaryFlags(wakeLock);
// 标记在mWakeLockSummary上
mWakeLockSummary |= wakeLockFlags;
// 对每个ProfilePowerState#mWakeLockSummary也进行标记
for (int j = 0; j < numProfiles; j++) {
final ProfilePowerState profile = mProfilePowerState.valueAt(j);
if (wakeLockAffectsUser(wakeLock, profile.mUserId)) {
profile.mWakeLockSummary |= wakeLockFlags;
}
}
}
// 根据系统状态对mWakeLockSummary进行调整
mWakeLockSummary = adjustWakeLockSummaryLocked(mWakeLockSummary);
// 对每个ProfilePowerState#mWakeLockSummary也进行调整
for (int i = 0; i < numProfiles; i++) {
final ProfilePowerState profile = mProfilePowerState.valueAt(i);
profile.mWakeLockSummary = adjustWakeLockSummaryLocked(profile.mWakeLockSummary);
}
}
}
只有当mDirty设置了DIRTY_WAKE_LOCKS和DIRTY_WAKEFULNESS标记位时,才会执行该方法。DIRTY_WAKE_LOCKS在申请WakeLock锁时设置,DIRTY_WAKEFULNESS在系统唤醒状态发生变化时设置。
进入该方法后,首先从保存了WakeLock的List中进行遍历,并根据WakeLock类型给mWakeLockSummary设置标记,这些标记位如下:
// frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
private static final int WAKE_LOCK_CPU = 1 << 0; // 表示需要CPU保持唤醒状态
private static final int WAKE_LOCK_SCREEN_BRIGHT = 1 << 1; // 表示持有FULL_WAKE_LOCK锁,需要屏幕常亮
private static final int WAKE_LOCK_SCREEN_DIM = 1 << 2; // 表示持有SCREEN_DIM_WAKE_LOCK锁,需要保持dim不灭屏
private static final int WAKE_LOCK_BUTTON_BRIGHT = 1 << 3; // 表示持有FULL_WAKE_LOCK锁,需要按键灯常亮
private static final int WAKE_LOCK_PROXIMITY_SCREEN_OFF = 1 << 4; // 表示持有Psensor WakeLock锁
private static final int WAKE_LOCK_STAY_AWAKE = 1 << 5; // 表示保持屏幕常亮
private static final int WAKE_LOCK_DOZE = 1 << 6; // 表示持有DOZE_WAKE_LOCK锁
private static final int WAKE_LOCK_DRAW = 1 << 7; // 表示持有DRAW_WAKE_LOCK锁
然后将根据系统状态进行调整:
// frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
private int adjustWakeLockSummaryLocked(int wakeLockSummary) {
// 唤醒状态不处于Doze状态时,忽略掉PowerManager.DOZE_WAKE_LOCK和PowerManager.DRAW_WAKE_LOCK两类型锁
if (mWakefulness != WAKEFULNESS_DOZING) {
wakeLockSummary &= ~(WAKE_LOCK_DOZE | WAKE_LOCK_DRAW);
}
// 唤醒状态处于Asleep状态或者Doze状态时,忽略掉屏幕常亮锁、PSensor锁
if (mWakefulness == WAKEFULNESS_ASLEEP
|| (wakeLockSummary & WAKE_LOCK_DOZE) != 0) {
wakeLockSummary &= ~(WAKE_LOCK_SCREEN_BRIGHT | WAKE_LOCK_SCREEN_DIM
| WAKE_LOCK_BUTTON_BRIGHT);
if (mWakefulness == WAKEFULNESS_ASLEEP) {
wakeLockSummary &= ~WAKE_LOCK_PROXIMITY_SCREEN_OFF;
}
}
if ((wakeLockSummary & (WAKE_LOCK_SCREEN_BRIGHT | WAKE_LOCK_SCREEN_DIM)) != 0) {
// 唤醒状态处于Awake状态,WAKE_LOCK_STAY_AWAKE只用于awake状态时
if (mWakefulness == WAKEFULNESS_AWAKE) {
wakeLockSummary |= WAKE_LOCK_CPU | WAKE_LOCK_STAY_AWAKE;
} else if (mWakefulness == WAKEFULNESS_DREAMING) { // 唤醒状态处于Dreamland,需要CPU保持激活
wakeLockSummary |= WAKE_LOCK_CPU;
}
}
// 有DRAW_WAKE_LOCK锁时,需要CPU保持唤醒
if ((wakeLockSummary & WAKE_LOCK_DRAW) != 0) {
wakeLockSummary |= WAKE_LOCK_CPU;
}
return wakeLockSummary;
}
在平时分析处理不灭屏相关Bug时,通过该值可确定当前系统持有哪些类型的锁。
最终得到mWakeLockSummary,在自动灭屏流程中将使用起到重要作用,当自动灭屏时,如果mWakeLockSummary设置有WAKE_LOCK_STAY_AWAKE标记位,那么将不会灭屏。
此外,上面方法中出现了对ProfilePowerState对象的处理,它用来对不同user进行不同参数的设置,在多用户模式下,可以支持不同的状态,这部分略去。
2.5 updateUserActivitySummaryLocked()更新用户活动状态
这个方法用来更新全局变量mUserActivitySummary,它表示用户活动状态,有三个值:
// frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
private static final int USER_ACTIVITY_SCREEN_BRIGHT = 1 << 0; // 表示亮屏状态下的交互
private static final int USER_ACTIVITY_SCREEN_DIM = 1 << 1; // 表示Dim状态下的交互
private static final int USER_ACTIVITY_SCREEN_DREAM = 1 << 2; // 表示Dreamland状态下的交互
何时开始自动灭屏,就是这个方法中实现的。当设备和用户有交互时,都会根据当前时间和自动灭屏时间、Dim时长、当前唤醒状态计算下次休眠的时间,完成自动灭屏的操作。由亮屏进入Dim的时长、Dim到灭屏的时长、亮屏到屏保的时长,都是在这里计算的,这个方法的详细分析见灭屏流程。
2.6 updateAttentiveStateLocked()更新细微模式状态
这个方法用来更新细微模式状态,这是Android R上新添加的一个功能,其目的就是解决用户长时间没有操作但一直持有亮屏锁导致系统不灭屏这个场景的,算是一个省电优化项,看下是如何实现的:
// frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
private void updateAttentiveStateLocked(long now, int dirty) {
// 触发细微模式的时间阈值
long attentiveTimeout = getAttentiveTimeoutLocked();
// 自动休眠时间
long goToSleepTime = mLastUserActivityTime + attentiveTimeout;
// 细微模式提示Dialog弹出时间
long showWarningTime = goToSleepTime - mAttentiveWarningDurationConfig;
// 是否已经弹出提升对话框
boolean warningDismissed = maybeHideInattentiveSleepWarningLocked(now, showWarningTime);
if (attentiveTimeout >= 0 && (warningDismissed
|| (dirty & (DIRTY_ATTENTIVE | DIRTY_STAY_ON | DIRTY_SCREEN_BRIGHTNESS_BOOST
| DIRTY_PROXIMITY_POSITIVE | DIRTY_WAKEFULNESS | DIRTY_BOOT_COMPLETED
| DIRTY_SETTINGS)) != 0)) {
......
// 是否需要弹出警告
if (isBeingKeptFromShowingInattentiveSleepWarningLocked()) {
return;
}
long nextTimeout = -1;
if (now < showWarningTime) {
nextTimeout = showWarningTime;
} else if (now < goToSleepTime) {
// 弹出警告给用户
mInattentiveSleepWarningOverlayController.show();
nextTimeout = goToSleepTime;
} else {
}
// 下一次进入时将会灭屏
if (nextTimeout >= 0) {
scheduleAttentiveTimeout(nextTimeout);
}
}
}
这里提供了两个配置值:
mAttentiveWarningDurationConfig表示触发细微模式前,弹出警告的时长,到达该时间时,会弹出对话框提示用户是否还要亮屏;
mAttentiveTimeoutConfig表示触发细微模式的时间阈值,到达该时长后,会进行自动灭屏;这种情况下,即使没有达到用户设置的自动休眠时间,也会进行自动灭屏。
2.7 updateWakefulnessLocked()是否需要更新唤醒状态
这个方法也和自动灭屏流程有关。如果满足自动灭屏条件,会更新系统唤醒状态。
这三个方法放在for(;;)循环中执行,是因为它们共同决定了设备的唤醒状态,前两个方法是汇总状态,后一个方法是根据前两个方法汇总的值而进行判断是否要改变当前的设备唤醒状态,汇总状态会受mWakefulness的影响,因此会进行循环处理。
同时,也仅仅会在超时灭屏进入睡眠或屏保时,for循环会执行两次,其他情况下,只会执行一次。这个方法的详细分析见灭屏流程。
2.8 updateProfilesLocked()
以上几个方法针对全局状态进行更新,这个方法则根据ProfilePowerState中保存的状态,更新不同用户是否进入锁定状态,由于使用场景不是很高,这里暂且略过。
2.9 updateDisplayPowerStateLocked()
该方法用于请求并更新Display状态,在这个方法中,会确定多个影响Display状态的属性,并将这些值封装到DisplayPowerRequest对象中,向DisplayMangerService发起请求,最终由DMS完成Display亮度、状态的更新:
// frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
private boolean updateDisplayPowerStateLocked(int dirty) {
final boolean oldDisplayReady = mDisplayReady;
if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_USER_ACTIVITY | DIRTY_WAKEFULNESS
| DIRTY_ACTUAL_DISPLAY_POWER_STATE_UPDATED | DIRTY_BOOT_COMPLETED
| DIRTY_SETTINGS | DIRTY_SCREEN_BRIGHTNESS_BOOST | DIRTY_VR_MODE_CHANGED |
DIRTY_QUIESCENT)) != 0) {
// 根据系统唤醒状态获取请求的'策略': off, doze, dim or bright.
mDisplayPowerRequest.policy = getDesiredScreenPolicyLocked();
final boolean autoBrightness; // 自动亮度是否开启
final int screenBrightnessOverride; // 是否有覆盖亮度
......
// WindowManager覆盖的亮度值,如播放视频时调节亮度
mDisplayPowerRequest.screenBrightnessOverride = screenBrightnessOverride;
// 是否使用自动亮度
mDisplayPowerRequest.useAutoBrightness = autoBrightness;
// 是否存在PSensor Wakelock锁
mDisplayPowerRequest.useProximitySensor = shouldUseProximitySensorLocked();
// 是否有增强亮度
mDisplayPowerRequest.boostScreenBrightness = shouldBoostScreenBrightness();
// 设置某些设置亮度时用到的battery信息
updatePowerRequestFromBatterySaverPolicy(mDisplayPowerRequest);
// 唤醒状态为Doze时,确定display的状态
if (mDisplayPowerRequest.policy == DisplayPowerRequest.POLICY_DOZE) {
mDisplayPowerRequest.dozeScreenState = mDozeScreenStateOverrideFromDreamManager;
if ((mWakeLockSummary & WAKE_LOCK_DRAW) != 0
&& !mDrawWakeLockOverrideFromSidekick) {
if (mDisplayPowerRequest.dozeScreenState == Display.STATE_DOZE_SUSPEND) {
mDisplayPowerRequest.dozeScreenState = Display.STATE_DOZE;
}
if (mDisplayPowerRequest.dozeScreenState == Display.STATE_ON_SUSPEND) {
mDisplayPowerRequest.dozeScreenState = Display.STATE_ON;
}
}
// Doze时的屏幕亮度
mDisplayPowerRequest.dozeScreenBrightness =
mDozeScreenBrightnessOverrideFromDreamManager;
} else {
mDisplayPowerRequest.dozeScreenState = Display.STATE_UNKNOWN;
mDisplayPowerRequest.dozeScreenBrightness = PowerManager.BRIGHTNESS_DEFAULT;
}
// 发起请求,返回值表示DisplayPowerController中是否完成这次请求
mDisplayReady = mDisplayManagerInternal.requestPowerState(mDisplayPowerRequest,
mRequestWaitForNegativeProximity);
// 释放PSensor WakeLock锁时的一个标记
mRequestWaitForNegativeProximity = false;
if ((dirty & DIRTY_QUIESCENT) != 0) {
sQuiescent = false; // 这个值主要是单独控制背光,默认为false
}
}
return mDisplayReady && !oldDisplayReady;
}
在向DisplayManagerService发起请求时,会将所有的信息封装到DisplayPowerRequest对象中,其中,policy属性值有五类:
POLICY_OFF:请求屏幕进入灭屏状态;
POLICY_DOZE:请求屏幕进入Doze状态;
POLICY_DIM:请求屏幕进入Dim状态,
POLICY_BRIGHT:请求屏幕处于正常亮屏状态;
POLICY_VR:VR模式相关。
在请求过程中,通过getDesiredScreenPolicyLocked()方法,根据当前唤醒状态和WakeLock统计状态来决定要请求的Display状态:
// frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
int getDesiredScreenPolicyLocked() {
// 当前唤醒状态为Asleep,则Display状态会设置为OFF
if (mWakefulness == WAKEFULNESS_ASLEEP || sQuiescent) {
return DisplayPowerRequest.POLICY_OFF;
}
// 当前唤醒状态为Doze,则Display状态会设置为Doze指定的状态
if (mWakefulness == WAKEFULNESS_DOZING) {
if ((mWakeLockSummary & WAKE_LOCK_DOZE) != 0) {
return DisplayPowerRequest.POLICY_DOZE;
}
if (mDozeAfterScreenOff) {
// 表示跳过Doze的状态,直接设置成OFF
return DisplayPowerRequest.POLICY_OFF;
}
}
......
// 如果存在亮屏锁、用户活动状态为亮屏、进行增强亮度,则Display状态将设置为ON
if ((mWakeLockSummary & WAKE_LOCK_SCREEN_BRIGHT) != 0
|| (mUserActivitySummary & USER_ACTIVITY_SCREEN_BRIGHT) != 0
|| !mBootCompleted
|| mScreenBrightnessBoostInProgress) {
return DisplayPowerRequest.POLICY_BRIGHT;
}
// 不满足以上条件,默认设置DIM
return DisplayPowerRequest.POLICY_DIM;
}
当DisplayPowerController中完成请求后,将返回true给mDisplayReady,表示这次请求完成了。后面的流程在亮屏流程中总结。
2.10 updateDreamLocked()
该方法用来更新设备Dreamland状态,比如是否进入Dream、Doze或者开始休眠:
// frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
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) { //mDisplayReady为ture后,才会进一步执行
//通过Handler异步发送一个消息
scheduleSandmanLocked();
}
}
}
可以看到,对于Dreamland相关状态的更新,依赖于mDisplayReady变量,它表示Display是否准备就绪,因此只有在准备就绪的情况下才会进一步调用该方法的方法体。最后会PMS主线程中,调用handleSandman()方法执行Dreamland的操作:
// frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
private void handleSandman() {
......
synchronized (mLock) {
// 如果召唤了"睡眠精灵",且Display状态已经准备完毕
if (mSandmanSummoned && mDisplayReady) {
// 判断是否可以进入Dreamland
startDreaming = canDreamLocked() || canDozeLocked();
mSandmanSummoned = false;
} else {
startDreaming = false;
}
}
final boolean isDreaming;
if (mDreamManager != null) {
if (startDreaming) {
mDreamManager.stopDream(false /*immediate*/);
// 开始进入Dreamland
mDreamManager.startDream(wakefulness == WAKEFULNESS_DOZING);
}
isDreaming = mDreamManager.isDreaming();
} else {
isDreaming = false;
}
synchronized (mLock) {
......
// Determine whether the dream should continue.
if (wakefulness == WAKEFULNESS_DREAMING) {
if (isDreaming && canDreamLocked()) {
if (mDreamsBatteryLevelDrainCutoffConfig >= 0
&& mBatteryLevel < mBatteryLevelWhenDreamStarted
- mDreamsBatteryLevelDrainCutoffConfig
&& !isBeingKeptAwakeLocked()) {
// 退出Dreamland
} else {
return; // continue dreaming
}
}
// 退出Dreamland,进入休眠状态
if (isItBedTimeYetLocked()) {
goToSleepNoUpdateLocked(SystemClock.uptimeMillis(),
PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, 0, Process.SYSTEM_UID);
updatePowerStateLocked();
} else {
// 唤醒设备
wakeUpNoUpdateLocked(SystemClock.uptimeMillis(),
PowerManager.WAKE_REASON_UNKNOWN,
"android.server.power:DREAM_FINISHED", Process.SYSTEM_UID,
mContext.getOpPackageName(), Process.SYSTEM_UID);
updatePowerStateLocked();
}
} else if (wakefulness == WAKEFULNESS_DOZING) {
if (isDreaming) {
return; // continue dozing
}
// 进行灭屏流程
reallyGoToSleepNoUpdateLocked(SystemClock.uptimeMillis(), Process.SYSTEM_UID);
// 更新PMS状态
updatePowerStateLocked();
}
}
// Stop dream.
if (isDreaming) {
mDreamManager.stopDream(false /*immediate*/);
}
}
在以上方法中,将会调用DreamManager处理具体的Dreamland逻辑,这部分流程的分析,在DreamManagerService模块分析时会进行详细分析。
2.11 finishWakefulnessChangeIfNeededLocked()
该方法主要做唤醒状态发生变化后,后半部分更新工作:
private void finishWakefulnessChangeIfNeededLocked() {
if (mWakefulnessChanging && mDisplayReady) {
// 如果唤醒状态处于Doze状态,不进行任何处理
if (mWakefulness == WAKEFULNESS_DOZING
&& (mWakeLockSummary & WAKE_LOCK_DOZE) == 0) {
return; // wait until dream has enabled dozing
}
if (mWakefulness == WAKEFULNESS_AWAKE) {
// 如果亮屏流程超过200ms,输出亮屏所用时间
if (latencyMs >= SCREEN_ON_LATENCY_WARNING_MS) {
Slog.w(TAG, "Screen on took " + latencyMs + " ms");
}
}
// 表示唤醒状态更新完成
mWakefulnessChanging = false;
//通过Notifier进行唤醒状态改变后的处理
mNotifier.onWakefulnessChangeFinished();
}
}
只有当屏幕状态改变后,才会执行该方法。进入该方法后,将通过Notifier#onWakefulnessChangeFinished()方法发送亮屏、灭屏广播等。
该方法中的logScreenOn()方法将打印出整个亮屏流程的耗时,在平时处理问题时很有帮助。
2.12 updateSuspendBlockerLocked()
这个方法用来更新SuspendBlocker锁状态。Suspend锁机制是Android框架中锁机的一种锁,它代表了框架层以上所有的WakeLock。Wakelock锁是APP或其他组件向PMS模块申请,而Suspend锁是PMS模块中对WakeLock锁的最终表现,或者说上层应用或system_server其他组件申请了wakelock锁后,在PMS中最终都会表现为Suspend锁,通过Suspend锁向Hal层写入节点,Kernal层会读取节点,从而唤醒或者休眠CPU。
该方法的详细分析在后续的WakeLock机制中进行汇总。
到此为止,对PMS中所有核心方法进行了简单的分析,有些方法仅仅说明了下作用,会在后面具体业务中进行详细分析。