Android Fk: PowerManagerService重點整理
主要內容:
1.PowerManagerService的架構
2.Wakelock的知識
3.電源管理相關的知識
4.相關debug
5. 具體場景釋疑
本文涉及到的圖片由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,必須滿足–mCount0才會釋放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中實現;
3. 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記錄電量資訊的主要結構: