Android Fk: PowerManagerService重点整理
主要内容:
1.PowerManagerService的架构
2.Wakelock的知识
3.电源管理相关的知识
4.相关debug
- 具体场景释疑
本文涉及到的图片由draw.io绘制,绘制的原xml文件
时序图由plantuml绘制,uml文件
几个用于学习该模块的资料
均分享在百度云中
https://pan.baidu.com/s/1lDxJ_Z-tmjqCB9hi_JFG8g
1. PowerManagerService的架构
1.1 PowerManagerService家族整体架构
1.2 PowerManagerService的binder架构
1.3 PowerManagerService开机初始化
1.4 PowerManagerService中的重要接口
Wakeup(): 强制系统从睡眠状态唤醒,此接口对应用是不开放的,应用想唤醒系统必须通过设置亮屏标志
gotoSleep(): 强制系统进入到睡眠状态,此接口也是应用不开放。
userActivity(): 向 PowerManagerService 报告影响系统休眠的用户活动,重计算灭屏时间,背光亮度等,例如触屏,划屏等用户活动;
Wakelock: wakelock 是 PowerManager 的一个内部类,提供了相关的接口来操作 wakelock 锁, 比如
newWakeLock()方法来创建 wakelock 锁,acquire()和 release()方法来申请和释放锁。
isDeviceIdleMode(): 返回设备当前是否处于idle状态
setBacklightBrightness() : 设置屏幕背光值
isScreenOn(): 返回屏幕当前是否处于亮屏(可交互状态),不推荐使用该接口,后面谷歌可能会删掉该
接口推荐使用isInteractive();
reboot(): 重启手机( Requires the {@link android.Manifest.permission#REBOOT} permission.)
shutdown():关机( Requires the {@link android.Manifest.permission#REBOOT} permission.)
2 Wakelock相关知识
2.1 wakelock的种类
PowerManager.WakeLock 有加锁和解锁两种状态,加锁的方式有两种:
• 永久锁:,这样的锁除非显式的放开,否则是不会解锁的,所以这种锁用起来要非常的小心(默认)。
xref: /v8-n-mido-dev/frameworks/base/core/java/android/os/PowerManager.java
1204 /**
1205 * Acquires the wake lock with a timeout.
1206 * <p>
1207 * Ensures that the device is on at the level requested when
1208 * the wake lock was created. The lock will be released after the given timeout
1209 * expires.
1210 * </p>
1211 *
1212 * @param timeout The timeout after which to release the wake lock, in milliseconds.
1213 */
1214 public void acquire(long timeout) {
1215 synchronized (mToken) {
1216 acquireLocked();
1217 mHandler.postDelayed(mReleaser, timeout);
1218 }
1219 }
• 超时锁:这种锁会在锁住后一段时间解锁。
xref: /v8-n-mido-dev/frameworks/base/core/java/android/os/PowerManager.java
1204 /**
1205 * Acquires the wake lock with a timeout.
1206 * <p>
1207 * Ensures that the device is on at the level requested when
1208 * the wake lock was created. The lock will be released after the given timeout
1209 * expires.
1210 * </p>
1211 *
1212 * @param timeout The timeout after which to release the wake lock, in milliseconds.
1213 */
1214 public void acquire(long timeout) {
1215 synchronized (mToken) {
1216 acquireLocked();
1217 mHandler.postDelayed(mReleaser, timeout);
1218 }
1219 }
1220
以锁的类型来划分也是可分为两种:
• 计数锁:应用调用一次 acquire 申请必定会对应一个 release 来释放;mRefCounted 为true,那么必须满足条件
mCount++==0才会去执行acquire,必须满足--mCount==0才会释放WakeLock,这也就意味着,计数锁只会真正执行
第一次申请acquireWakeLock,以及最后一次释放,releaseWakeLock;再次申请,以及再次释放,只是对申请次数
以及释放次数的统计。所以每一次acquire 都必须一一对应一个release 操作
• 非计数锁:非计数锁应用调用多次acquire, 调用一次 release 就可释放前面 acquire 的锁。
setReferenceCounted(boolean)设置计数锁和非计数锁
xref: /v8-n-mido-dev/frameworks/base/core/java/android/os/PowerManager.java
acquire:
1221 private void acquireLocked() {
1222 if (!mRefCounted || mCount++ == 0) {
1223 // Do this even if the wake lock is already thought to be held (mHeld == true)
1224 // because non-reference counted wake locks are not always properly released.
1225 // For example, the keyguard's wake lock might be forcibly released by the
1226 // power manager without the keyguard knowing. A subsequent call to acquire
1227 // should immediately acquire the wake lock once again despite never having
1228 // been explicitly released by the keyguard.
1229 mHandler.removeCallbacks(mReleaser);
1230 Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, mTraceName, 0);
1231 try {
1232 mService.acquireWakeLock(mToken, mFlags, mTag, mPackageName, mWorkSource,
1233 mHistoryTag);
1234 } catch (RemoteException e) {
1235 throw e.rethrowFromSystemServer();
1236 }
1237 mHeld = true;
1238 }
1239 }
realese:
1265 public void release(int flags) {
1266 synchronized (mToken) {
1267 if (!mRefCounted || --mCount == 0) {
1268 mHandler.removeCallbacks(mReleaser);
1269 if (mHeld) {
1270 Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, mTraceName, 0);
1271 try {
1272 mService.releaseWakeLock(mToken, flags);
1273 } catch (RemoteException e) {
1274 throw e.rethrowFromSystemServer();
1275 }
1276 mHeld = false;
1277 }
1278 }
1279 if (mCount < 0) {
1280 throw new RuntimeException("WakeLock under-locked " + mTag);
1281 }
1282 }
1283 }
2.2 wakelock的flag
2.3 wakelock的持有释放流程
详细流程:
2.4 updatePowerStateLocked()做了什么
2.5 屏幕亮屏流程
(待整理)
3. 电源管理
3.1 电源管理的整体框架
3.2 Framework注册natvie层电源信息监听器
3.3 电源信息更新
4. 电源debug
1. adb shell dumpsys battery
AC powered :false 表示是否连接电源供电,false无供电
USB powered :true 表示是否USB使用供电,true供电
status :5 表示电池充电状态 5表示电量是满的
health :2 表示电池健康状况 2表示良好
present: true 表示手机上是否有电池 ,true表示有电池
level :100 表示当前剩余电量信息 100表示100%
scale:100 表示电池电量最大值
voltage:4332 表示当前电池电压 单位mv
temperature: 314 表示当前电池温度 314表示31.4度
technology:Li-ion 表示电池使用技术
adb shell dumpsys power //
adb shell dumpsys battery unplug //相当于不插电
adb shell dumpsys deviceidle step //让状态转换Doze模式
adb shell dumpsys battery reset //重置电源状态
adb shell dumpsys battery set level 10 //将电池电量改为10
adb shell dumpsys batterystatus
2. 电量信息测试方法(adb shell dumpsys batterystats)
1.首先需下载historian.py脚本,下载地址:https://github.com/google/battery-historian
2.下载后解压,battery-historian-master\battery-historian-master\scripts目录下,historian.py脚本在该目录下
3.在此目录下执行操作
4.执行步骤
1)首先要初始化batterystats数据
adb shell dumpsys batterystats --enable full-wake-history
shell dumpsys batterystats --reset
2)上面的操作执行完毕后,拔掉手机,操作你的App,操作完成后,重新连接手机,执行下面的命令,收集Battery数据:
adb shell dumpsys batterystats > batterystats.txt
3)得到这些数据后,这个时候使用我们的battery-historian来生成我们可见HTML报告:
python historian.py batterystats.txt > batterystats.html
4)用google浏览器打开此文件即可
打开结果:
参数意义:
5. 疑问解答
1.UserActivity最常调用的地方,按音量键是否调用?
如果我们在Settings中设置sleep时间为15s,那么15秒内如果没有任何操作,屏幕就会熄灭(当然,没有WakeLock未被释放是前提)。
如果在这个时间内用户有操作:touch屏幕或者按下菜单键、返回键等,那么这时就会调用PowerManagerService的UserActivity方法,
重新计算亮屏至灭屏的timeout时间。
屏幕及按键事件的调用主要在InputDispatcher中通过JNI调用PMS的userActivityFromNative()方法,在KeyGuard中也有调用userActivity()方法的地方,
也是为了更新屏幕是否需要更新屏幕灭屏的timeout时间,由keyGuard自己的逻辑调用。
2. 打电话亮灭屏时锁是怎么申请的(靠近远离时对于是申请还是释放)?
PROXIMITY_SCREEN_OFF_WAKE_LOCK的使用方法
在新建call连接时incallui里 会新建一个ProximitySensor的对象,在ProximitySensor的
构造函数中持有一个PROXIMITY_SCREEN_OFF_WAKE_LOCK
类型的wakelock,mProximityWakeLock;
在判断需要距离传感器工作的场景下执行mProximityWakeLock.acquire()
不需要距离传感器工作的情况下执行mProximityWakeLock.release()
即申请该wakelock会将距离传感器唤醒;释放会关闭距离传感器。屏幕的亮灭由传感器模块控制
具体的场景:
a. 打通电话,InCallUI全屏显示即申请该wakelock,打开距离传感器,(屏幕亮灭由传感器控制,如果是传感器导致的灭屏
该锁不会释放,系统也不会进入睡眠状态;)
b. 这时如果按home键,IncallUI退至后台此时释放该wakelock,关闭距离传感器;
c. 打通电话,InCallUI全屏显示即申请该wakelock,打开距离传感器,如果不做操作即没有userActivity屏幕自动灭屏,将释放该wakelock,关闭传感器;只能由power键唤醒屏幕;
d. power键影响:如果已申请该wakelock,距离传感器已开,通过power键使灭屏会释放该wakelock,距离传感器关闭,再点击power键点亮屏幕后会再次申请该wakelock,唤醒距离传感器;
如上d场景有歧义,这里详细描述下:
d. power键影响:如果已申请该wakelock,距离传感器已开,通过power键使灭屏会释放该wakelock,距离传感器关闭,再点击power键点亮屏幕后会再次申请该wakelock,唤醒距离传感器;
1.通过power键使灭屏会释放该wakelock 有歧义: 通过power键灭屏,InCallUI退至后台,释放距离传感器的wakelock是在InCallUI退至后台主动释放的, 不是power键事件去释放的;
2.释放该wakelock,距离传感器关闭,有歧义: 如果释放时有这个RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY flag,会推迟释放该wakelock,直到物体离开传感器才会去释放wakelock,然后关闭距离传感器;
flag定义:
frameworks/base/core/java/android/os/PowerManager.java
260 /**
261 * Flag for {@link WakeLock#release WakeLock.release(int)}: Defer releasing a
262 * {@link #PROXIMITY_SCREEN_OFF_WAKE_LOCK} wake lock until the proximity sensor
263 * indicates that an object is not in close proximity.
264 */
265 public static final int RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY = 1;
InCallUI中使用:
packages/apps/InCallUI/src/com/android/incallui/ProximitySensor.java
86 private void turnOffProximitySensor(boolean screenOnImmediately) {
87 if (mProximityWakeLock != null) {
88 if (mProximityWakeLock.isHeld()) {
89 Log.i(this, "Releasing proximity wake lock");
90 // Because of ultrasonic sensor not work well sometimes, turn on screen immediately
91 int flags = screenOnImmediately || Utils.isUltrasonicSensorDevice()
92 ? 0 : PowerManager.RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY;
93 mProximityWakeLock.release(flags);
94 } else {
95 Log.i(this, "Proximity wake lock already released");
96 }
97 }
98 }
3.ACQUIRE_CAUSES_WAKEUP不能和PARTIAL_WAKE_LOCK一起使用的原因?
PARTIAL_WAKE_LOCK只持有CPU锁,如后台下载,听音乐,虽然屏幕灭了但是CPU不休眠,
三种ScreenLock:
PowerManagerService.java
907 @SuppressWarnings("deprecation")
908 private static boolean isScreenLock(final WakeLock wakeLock) {
909 switch (wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) {
910 case PowerManager.FULL_WAKE_LOCK:
911 case PowerManager.SCREEN_BRIGHT_WAKE_LOCK:
912 case PowerManager.SCREEN_DIM_WAKE_LOCK:
913 return true;
914 }
915 return false;
916 }
以上三种是屏幕相关的wakelock,如果flag中含有ACQUIRE_CAUSES_WAKEUP可以将屏幕wakeup,但是其和PARTIAL_WAKE_LOCK同用会没有持有屏幕锁导致屏幕不亮,或者亮屏下持有该锁也无法保持屏幕一直亮;
因此ACQUIRE_CAUSES_WAKEUP应和ScreenLock类型的flag一起使用才能达到想要的效果(灭屏时申请,点亮对应屏幕或键盘,屏幕为操作超时后保持屏幕或键盘对应亮度);
4.申请锁后再释放锁,这时是什么状态(原来的状态么)?
ACQUIRE_CAUSES_WAKEUP与三种ScreenLock配合使用效果:
亮屏状态申请:
FULL_WAKE_LOCK:申请后 屏幕超时该灭屏的时候可以保持屏幕正常亮度,键盘灯保持亮起状态,之后释放该wakelock,屏幕和键盘灯立刻灭;
SCREEN_BRIGHT_WAKE_LOCK:申请后 屏幕超时该灭屏的时候可以保持屏幕正常亮度,键盘不亮,之后释放该wakelock,屏幕立刻灭;
SCREEN_DIM_WAKE_LOCK:申请后 屏幕超时该灭屏的时候可以保持屏幕处于dim亮度,键盘灯不亮,之后释放该wakelock,屏幕立刻灭;
灭屏状态申请:屏幕正常亮起,无用户操作超时后同上;
屏幕或键盘灯未到超时时间释放,将继续保持未超时状态,走超时后该灭屏灭键盘等行为;
5.wakelock 申请和释放kenerl是怎么处理write进来的数据的?
kernel对wake_lock的sysfs接口文件的处理在kernel\power\wakelock.c中定义
这部分需要大量学习暂未完成,日后有需要时再深入学习,先梳理大概流程:
1.上层接口通过JNI调用hal层的power.c将wakelock信息写入/sys/power/wake_lock sysfs文件,这部分在hardware/libhardware_legacy/power/power.c中实现;
2.之后将调用kernel中PM core的wakelock模块的pm_wake_lock方法,处理和管理wakelock,这部分在kernel/kernel/power/wakelock.c中实现;
- PM core的wakeup模块向device driver上报一个wakeup event,用来阻止系统suspend,这部分在drivers/base/power/wakeup.c中实现;
4.device driver将设备的wakeup信息,以sysfs的形式提供到用户空间供查询配置,这部分在在drivers/base/power/sysfs.c中实现。
6.Battery的数据保存在哪里,什么时机保存?
BatteryStats的数据保存在/data/system/batterystats.bin中,在对应模块需要更新电量统计时调用BatteryStatsImpl接口口来向该文件写入,分门别类的统计每个部分的耗电情况,如说的healthd更新电池电量至BatteryService的回调中就在必走的processValuesLocked方法中调用了BatteryStatService的记录电池信息的方法:
424 private void processValuesLocked(boolean force) {
439 ...
457
458 // Let the battery stats keep track of the current level.
459 try {
460 mBatteryStats.setBatteryState(mBatteryProps.batteryStatus, mBatteryProps.batteryHealth,
461 mPlugType, mBatteryProps.batteryLevel, mBatteryProps.batteryTemperature,
462 mBatteryProps.batteryVoltage, mBatteryProps.batteryChargeCounter);
463 } catch (RemoteException e) {
464 // Should never happen.
465 }
466 ...
626 }
BatteryStats记录电量信息的主要结构: