应用宝的NotificationManagerService_post_com.tencent.android.qqdownloader持锁现象
摘要:
现象:
应用宝的NotificationManagerService_post_com.tencent.android.qqdownloader在Doze下也可以频繁持锁,很猛啊。
解决方案:
直接拦截持锁就行,也可以冻结,限制服务或主动释放锁,反正解决起来很容易。就是没想明白,为了保活目的这么拼干啥。因为在AOSP代码也就是一个if else就屏蔽掉了哈,甚至都不需要查杀或冻结操作。
分析
相关逻辑系统内部调用链
1.应用调用 notify() 后,系统服务的执行流程如下:
2.应用进程 → Binder 调用 → NotificationManagerService.notify()
3.NotificationManagerService 内部处理通知:
4.最终释放 WakeLock 的逻辑由 PostNotificationTracker 在通知处理完成后触发。
其实消息通知触发也没关系,但是太频繁就过分了,会带来频繁的持锁,导致功耗变大哈。
日志
从日志看:感觉是定时周期1分钟,就行不断地持锁。这样做的目的是啥,我只知道对手机待机功耗是不友好的哈。
Line 2118: 05-26 17:34:43.610025 1623 1663 D PowerManagerService: acquireWakeLockInternal: lock=110346082, flags=0x1, tag="NotificationManagerService:post:com.tencent.android.qqdownloader", ws=WorkSource{10225 com.tencent.android.qqdownloader}, uid=1000, pid=1623
Line 2773: 05-26 17:35:45.094949 1623 3364 D PowerManagerService: acquireWakeLockInternal: lock=206925355, flags=0x1, tag="NotificationManagerService:post:com.tencent.android.qqdownloader", ws=WorkSource{10225 com.tencent.android.qqdownloader}, uid=1000, pid=1623
Line 3403: 05-26 17:36:46.593976 1623 3364 D PowerManagerService: acquireWakeLockInternal: lock=200498714, flags=0x1, tag="NotificationManagerService:post:com.tencent.android.qqdownloader", ws=WorkSource{10225 com.tencent.android.qqdownloader}, uid=1000, pid=1623
Line 4148: 05-26 17:37:48.095304 1623 3450 D PowerManagerService: acquireWakeLockInternal: lock=91113790, flags=0x1, tag="NotificationManagerService:post:com.tencent.android.qqdownloader", ws=WorkSource{10225 com.tencent.android.qqdownloader}, uid=1000, pid=1623
Line 4949: 05-26 17:38:49.596294 1623 3364 D PowerManagerService: acquireWakeLockInternal: lock=101527642, flags=0x1, tag="NotificationManagerService:post:com.tencent.android.qqdownloader", ws=WorkSource{10225 com.tencent.android.qqdownloader}, uid=1000, pid=1623
Line 5400: 05-26 17:39:49.768274 1623 3451 D PowerManagerService: acquireWakeLockInternal: lock=190337700, flags=0x1, tag="NotificationManagerService:post:com.tencent.android.qqdownloader", ws=WorkSource{10225 com.tencent.android.qqdownloader}, uid=1000, pid=1623
源码
日志对应的源码:
private PostNotificationTracker acquireWakeLockForPost(String pkg, int uid) {
// The package probably doesn't have WAKE_LOCK permission and should not require it.
return Binder.withCleanCallingIdentity(() -> {
WakeLock wakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
"NotificationManagerService:post:" + pkg); // 日志打印位置
wakeLock.setWorkSource(new WorkSource(uid, pkg));
wakeLock.acquire(POST_WAKE_LOCK_TIMEOUT.toMillis());
return mPostNotificationTrackerFactory.newTracker(wakeLock);
});
}
相关逻辑系统内部调用链
1.应用调用 notify() 后,系统服务的执行流程如下:
2.应用进程 → Binder 调用 → NotificationManagerService.notify()
3.NotificationManagerService 内部处理通知:
4.最终释放 WakeLock 的逻辑由 PostNotificationTracker 在通知处理完成后触发。
// 应用发送通知的典型代码
NotificationManager nm = getSystemService(NotificationManager.class);
Notification notification = new Notification.Builder(this, "channel_id")
.setContentTitle("Test")
.setContentText("Trigger WakeLock")
.setSmallIcon(R.drawable.ic_notification)
.build();
nm.notify(1, notification); // 触发内部 WakeLock 逻辑
void enqueueNotificationInternal(...) {
// 检查权限、频道状态等
...
// 获取 WakeLock(关键步骤)
PostNotificationTracker tracker = acquireWakeLockForPost(pkg, uid);
// 投递通知到系统UI
mHandler.post(new EnqueueNotificationRunnable(tracker));
}
最终释放 WakeLock 的逻辑由 PostNotificationTracker 在通知处理完成后触发。
解决方案:
直接拦截持锁就行,也可以冻结,限制服务或主动释放锁,反正解决起来很容易。就是没想明白,为了保活目的这么拼干啥。因为在AOSP代码也就是一个if else就屏蔽掉了哈,甚至都不需要查杀或冻结操作。