1. WakeLock(休眠锁)
WakeLock用于保持设备的唤醒状态,有些情况下,即时用户不操作App,我们也需要保持屏幕处于唤醒状态,以保证用户体验,比如视频类APP和计步类APP,视频类APP需要屏幕一直保持常量,计步类APP要求熄屏后程序依然保持运行状态。
2. WakeLock的多种类型:
PARTIAL_WAKE_LOCK:保持CPU正常运转,但屏幕和键盘灯都可能是关闭的。
SCREEN_DIM_WAKE_LOCK:保持CPU正常运转,允许屏幕点亮但可能屏幕被置灰,键盘灯可能是关闭的。
SCREEN_BRIGHT_WAKE_LOCK:保持CPU正常运转,允许屏幕高亮显示,键盘灯可能是关闭的。
FULL_WAKE_LOCK:保持CPU正常运转,保持屏幕高亮显示,键盘灯也保持亮度。
ACQUIRE_CAUSES_WAKEUP:强制屏幕和键盘灯亮起,这种锁针对一些必须通知用户的操作。
ON_AFTER_RELEASE:当WakeLock被释放后,继续保持屏幕和键盘灯亮起一段时间。
1. 方法一
使用adb shell dumpsys >dumpsys.txt
或者在bugreport.zip
或者使用命令: adb shell dumpsys power > power_dump.txt搜索关键字:Wake Locks: 查看对应的持有对象。
复现场景:
[settings]settings->display->screen timeout-设置15秒,15秒后手机不灭屏.(低概率)
(1) 查看dump power的信息:
Wake Locks: size=1 FULL_WAKE_LOCK 'TinnoFactory' ACQUIRE_CAUSES_WAKEUP ACQ=-55m1s400ms (uid=1000 pid=16340)
(2) 发现有持有'TinnoFactory' TAG的 wake lock (FULL_WAKE_LOCK)导致不能灭屏。
(3) 查看代码:
private static final String TAG = "TinnoFactory";
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i(TAG, "onCreate"); PowerManager pm =(PowerManager) getSystemService(POWER_SERVICE);
wakeLock= pm.newWakeLock(PowerManager.FULL_WAKE_LOCK| PowerManager.ACQUIRE_CAUSES_WAKEUP, TAG);
wakeLock.acquire();@Override
public void onDestroy() {
Log.d(TAG, "onDestroy");
// Log.i(TAG, "onDestroy, reset previousMobileDataState");
// setMobileDataState(mPreviousMobileDataState);
// Log.i(TAG, "setMobileData, getMobileDataState = " + getMobileDataState());
if (wakeLock != null) {wakeLock.release();}
super.onDestroy();
}
(4) 这个应用退出来就可以恢复。
复现场景:
将timeout 时间设置为15秒。在打电话界面停留超过15秒,手机不灭屏。
(1) 查看dump power的信息:
SCREEN_BRIGHT_WAKE_LOCK 'WindowManager' ON_AFTER_RELEASE ACQ=-1m56s271ms (uid=1000 pid=1344 ws=WorkSource{10172})
(2) 发现有持有'WindowManager' TAG,是来自uid: 10172, 通过日志发现10172是dialer应用。
(3) 分析'WindowManager' TAG,怎么持有wake lock。
使用倒序的方法找:
1) TAG 定义:
TAG_WM 定义:
frameworks/base/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java
public class WindowManagerDebugConfig {
...
static final String TAG_WM = "WindowManager";
..
2) 调用newWakeLock SCREEN_BRIGHT_WAKE_LOCK的地方:
frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
private WindowManagerService(Context context, InputManagerService inputManager,
boolean showBootMsgs, boolean onlyCore, WindowManagerPolicy policy,
ActivityTaskManagerService atm, DisplayWindowSettingsProvider
displayWindowSettingsProvider, SuppliertransactionFactory,
SuppliersurfaceFactory,
FunctionsurfaceControlFactory) {
...
mHoldingScreenWakeLock = mPowerManager.newWakeLock(
PowerManager.SCREEN_BRIGHT_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE, TAG_WM);...
}
3)谁在使用mHoldingScreenWakeLock.
只要newHoldScreen不为null,若调用setHoldScreenLocked,则就会调用acquire函数.
void setHoldScreenLocked(final Session newHoldScreen) {
final boolean hold = newHoldScreen != null;
...
if (hold != state) {
if (hold) {
mHoldingScreenWakeLock.acquire();
mPolicy.keepScreenOnStartedLw();
}..
4) 接下来寻找是调用setHoldScreenLocked 函数
发现只有一处,是在RootWindowContainer.java 中。
frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.java
// "Something has changed! Let's make it correct now."
// TODO: Super long method that should be broken down...
void performSurfacePlacementNoTrace() {
...
mWmService.setHoldScreenLocked(mHoldScreen);..
5) 查看是谁给mHoldScreen赋值。
也是只有一处,当设置flag是FLAG_KEEP_SCREEN_ON。
android/frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.java
/**
* @param w WindowState this method is applied to.
* @param obscured True if there is a window on top of this obscuring the display.
* @param syswin System window?
* @return True when the display contains content to show the user. When false, the display
* manager may choose to mirror or blank the display.
*/
boolean handleNotObscuredLocked(WindowState w, boolean obscured, boolean syswin) {
if (w.mHasSurface && canBeSeen) {
if ((attrFlags & FLAG_KEEP_SCREEN_ON) != 0) {
mHoldScreen = w.mSession;
mHoldScreenWindow = w;
}
(4) 在dialer 中查看是谁使用了FLAG_KEEP_SCREEN_ON 。
果真有有相应的代码,进行debug调试既可.
vendor/mediatek/proprietary/packages/apps/Dialer/java/com/android/incallui/InCallPresenter.java
private void applyScreenTimeout() {
if (screenTimeoutEnabled) {
window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
} else {
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
}