这类问题一般比较难分析,符合以下情况的才有可能分析出来:
- 能够复现并调试
- 有问题时的堆栈以及对应的event log
TaskFragment#shouldSleepActivities 方法导致递归
There is a recursion among check for sleep and complete pause during sleeping
关键log
01-08 08:45:42.946 1941 5167 I wm_finish_activity: [0,240239432,5187,com.ss.android.auto/com.ss.android.account.v2.view.AccountLoginActivity,app-request]
01-08 08:45:42.949 1941 5167 I wm_pause_activity: [0,240239432,com.ss.android.auto/com.ss.android.account.v2.view.AccountLoginActivity,userLeaving=false,finish]
01-08 08:45:42.977 30679 30679 I wm_on_top_resumed_lost_called: [240239432,com.ss.android.account.v2.view.AccountLoginActivity,topStateChangedWhenResumed]
01-08 08:45:42.980 1941 2054 I WindowManager: Display0 turned off...
// 问题发生在息屏场景
01-08 08:45:43.001 1941 2054 I wm_sleep_token: [0,1,ScreenOff]
// 问题AccountLoginActivity已经完成onPaused回调
01-08 08:45:43.007 30679 30679 I wm_on_paused_called: [0,240239432,com.ss.android.account.v2.view.AccountLoginActivity,performPause,1]
01-08 08:45:43.011 1941 22598 I wm_set_resumed_activity: [0,com.ss.android.auto/.ugc.video.activity.UgcNewDetailActivity,resumeTopActivity - onActivityStateChanged]
01-08 08:45:43.016 1941 22598 I wm_resume_activity: [0,212386622,5187,com.ss.android.auto/.ugc.video.activity.UgcNewDetailActivity]
01-08 08:45:43.021 1941 22598 I wm_add_to_stopping: [0,240239432,com.ss.android.auto/com.ss.android.account.v2.view.AccountLoginActivity,completeFinishing]
// 重复打印很多次,表明递归
01-08 08:45:43.022 1941 2009 I am_wtf : [0,1941,system_server,-1,ActivityTaskManager,Going to pause when pause is already pending for ActivityRecord
{e51c348 u0 com.ss.android.auto/com.ss.android.account.v2.view.AccountLoginActivity} t5187 f}} state=FINISHING]
01-08 08:45:43.045 1941 2009 I am_wtf : [0,1941,system_server,-1,ActivityTaskManager,Going to pause when pause is already pending for ActivityRecord
{e51c348 u0 com.ss.android.auto/com.ss.android.account.v2.view.AccountLoginActivity}
t5187 f}} state=PAUSED]
......
关键堆栈
一直在重复TaskFragment.startPausing -> TaskFragment.sleepIfPossible 的堆栈
at com.android.server.wm.TaskFragment.startPausing(TaskFragment.java:1690)
at com.android.server.wm.TaskFragment.sleepIfPossible(TaskFragment.java:856)
at com.android.server.wm.Task.lambda$goToSleepIfPossible$20(Task.java:5425)
at com.android.server.wm.Task$$ExternalSyntheticLambda9.accept(unavailable:6)
at com.android.server.wm.TaskFragment.forAllLeafTaskFragments(TaskFragment.java:1952)
at com.android.server.wm.Task.lambda$forAllLeafTasksAndLeafTaskFragments$14(Task.java:3699)
at com.android.server.wm.Task$$ExternalSyntheticLambda31.accept(unavailable:6)
at com.android.server.wm.Task.forAllLeafTasks(Task.java:3644)
at com.android.server.wm.Task.forAllLeafTasksAndLeafTaskFragments(Task.java:3687)
at com.android.server.wm.Task.goToSleepIfPossible(Task.java:5424)
at com.android.server.wm.Task.checkReadyForSleep(Task.java:5406)
at com.android.server.wm.ActivityRecord.addToStopping(ActivityRecord.java:6974)
at com.android.server.wm.ActivityRecord.completeFinishing(ActivityRecord.java:4091)
at com.android.server.wm.TaskFragment.completePause(TaskFragment.java:1854)
at com.android.server.wm.TaskFragment.startPausing(TaskFragment.java:1696)
...
...
at com.android.server.wm.TaskFragment.startPausing(TaskFragment.java:1696)
at com.android.server.wm.TaskFragment.sleepIfPossible(TaskFragment.java:856)
at com.android.server.wm.Task.lambda$goToSleepIfPossible$20(Task.java:5425)
at com.android.server.wm.Task$$ExternalSyntheticLambda9.accept(unavailable:6)
at com.android.server.wm.TaskFragment.forAllLeafTaskFragments(TaskFragment.java:1952)
at com.android.server.wm.Task.lambda$forAllLeafTasksAndLeafTaskFragments$14(Task.java:3699)
at com.android.server.wm.Task$$ExternalSyntheticLambda31.accept(unavailable:6)
at com.android.server.wm.Task.forAllLeafTasks(Task.java:3644)
at com.android.server.wm.Task.forAllLeafTasksAndLeafTaskFragments(Task.java:3687)
at com.android.server.wm.Task.goToSleepIfPossible(Task.java:5424)
at com.android.server.wm.Task.checkReadyForSleep(Task.java:5406)
at com.android.server.wm.ActivityRecord.addToStopping(ActivityRecord.java:6974)
at com.android.server.wm.ActivityRecord.completeFinishing(ActivityRecord.java:4091)
at com.android.server.wm.TaskFragment.completePause(TaskFragment.java:1854)
at com.android.server.wm.TaskFragment.startPausing(TaskFragment.java:1696)
at com.android.server.wm.TaskFragment.sleepIfPossible(TaskFragment.java:856)
at com.android.server.wm.Task.lambda$goToSleepIfPossible$20(Task.java:5425)
at com.android.server.wm.Task$$ExternalSyntheticLambda9.accept(unavailable:6)
at com.android.server.wm.TaskFragment.forAllLeafTaskFragments(TaskFragment.java:1952)
at com.android.server.wm.Task.lambda$forAllLeafTasksAndLeafTaskFragments$14(Task.java:3699)
at com.android.server.wm.Task$$ExternalSyntheticLambda31.accept(unavailable:6)
at com.android.server.wm.Task.forAllLeafTasks(Task.java:3644)
at com.android.server.wm.Task.forAllLeafTasksAndLeafTaskFragments(Task.java:3687)
at com.android.server.wm.Task.goToSleepIfPossible(Task.java:5424)
at com.android.server.wm.Task.checkReadyForSleep(Task.java:5406)
at com.android.server.wm.ActivityRecord.completeResumeLocked(ActivityRecord.java:6766)
at com.android.server.wm.TaskFragment.resumeTopActivity(TaskFragment.java:1609)
at com.android.server.wm.Task.resumeTopActivityInnerLocked(Task.java:5665)
at com.android.server.wm.Task.resumeTopActivityUncheckedLocked(Task.java:5593)
at com.android.server.wm.Task.resumeTopActivityUncheckedLocked(Task.java:5648)
at com.android.server.wm.ActivityRecord.makeActiveIfNeeded(ActivityRecord.java:6551)
at com.android.server.wm.RootWindowContainer.lambda$resumeFocusedTasksTopActivities$18(RootWindowContainer.java:2468)
at com.android.server.wm.RootWindowContainer$$ExternalSyntheticLambda17.accept(unavailable:15)
at com.android.server.wm.Task.forAllRootTasks(Task.java:3656)
at com.android.server.wm.WindowContainer.forAllRootTasks(WindowContainer.java:2127)
at com.android.server.wm.WindowContainer.forAllRootTasks(WindowContainer.java:2127)
at com.android.server.wm.WindowContainer.forAllRootTasks(WindowContainer.java:2127)
... repeated 2 times
at com.android.server.wm.WindowContainer.forAllRootTasks(WindowContainer.java:2120)
at com.android.server.wm.RootWindowContainer.resumeFocusedTasksTopActivities(RootWindowContainer.java:2446)
at com.android.server.wm.RootWindowContainer.resumeFocusedTasksTopActivities(RootWindowContainer.java:2424)
at com.android.server.wm.RootWindowContainer.resumeFocusedTasksTopActivities(RootWindowContainer.java:2419)
at com.android.server.wm.ActivityRecord.addToFinishingAndWaitForIdle(ActivityRecord.java:4178)
at com.android.server.wm.ActivityRecord.completeFinishing(ActivityRecord.java:4095)
at com.android.server.wm.TaskFragment.completePause(TaskFragment.java:1854)
at com.android.server.wm.ActivityRecord.activityPaused(ActivityRecord.java:6784)
at com.android.server.wm.ActivityClientController.activityPaused(ActivityClientController.java:191)
- locked <0x093a97ff> (a com.android.server.wm.WindowManagerGlobalLock)
at android.app.IActivityClientController$Stub.onTransact(IActivityClientController.java:582)
at com.android.server.wm.ActivityClientController.onTransact(ActivityClientController.java:131)
at android.os.Binder.execTransactInternal(Binder.java:1285)
at android.os.Binder.execTransact(Binder.java:1249)
关键代码
首先查看关键log中重复log打印的地方在TaskFragment#startPausing()
at com.android.server.wm.Task.goToSleepIfPossible(Task.java:5424)
// 这里的shouldSleepActivities返回true,才会走到上面的goToSleepIfPossible
at com.android.server.wm.Task.checkReadyForSleep(Task.java:5406)
at com.android.server.wm.ActivityRecord.addToStopping(ActivityRecord.java:6974)
at com.android.server.wm.ActivityRecord.completeFinishing(ActivityRecord.java:4091)
at com.android.server.wm.TaskFragment.completePause(TaskFragment.java:1854)
// shouldSleepActivities 返回false,才会走到上面的completePause
at com.android.server.wm.TaskFragment.startPausing(TaskFragment.java:1696)
- 因为上面有走到TaskFragment#completePause,所以这里的shouldSleepActivities会返回false
// TaskFragment.java
boolean startPausing(boolean userLeaving, boolean uiSleeping, ActivityRecord resuming,
String reason) {
......
if (mPausingActivity != null) {
Slog.wtf(TAG, "Going to pause when pause is already pending for " + mPausingActivity
+ " state=" + mPausingActivity.getState());
// 按理谷歌这里已经做了规避递归,此处看log应该还没亮屏,不会走到completePause陷入递归
if (!shouldSleepActivities()) {
// Avoid recursion among check for sleep and complete pause during sleeping.
// Because activity will be paused immediately after resume, just let pause
// be completed by the order of activity paused from clients.
completePause(false, resuming);
}
}
- 因为Task.checkReadyForSleep后面有走到Task.goToSleepIfPossible,所以这里的shouldSleepActivities会返回true
// Task.java
void checkReadyForSleep() {
if (shouldSleepActivities() && goToSleepIfPossible(false /* shuttingDown */)) {
mTaskSupervisor.checkReadyForSleepLocked(true /* allowDelay */);
}
}
- 但是对同一个Task而言,一个方法基本不会在一个堆栈里既返回false,又返回true。从shouldSleepActivities本身查看下。
查看代码可以得出,Task继承自TaskFragment,但是TaskFragment的shouldSleepActivities永远返回false,而Task中的shouldSleepActivities在此息屏场景下应该是返回true的。
//Task.java
boolean shouldSleepActivities() {
final DisplayContent display = mDisplayContent;
final boolean isKeyguardGoingAway = (mDisplayContent != null)
? mDisplayContent.isKeyguardGoingAway()
: mRootWindowContainer.getDefaultDisplay().isKeyguardGoingAway();
// Do not sleep activities in this root task if we're marked as focused and the keyguard
// is in the process of going away.
if (isKeyguardGoingAway && isFocusedRootTaskOnDisplay()
// Avoid resuming activities on secondary displays since we don't want bubble
// activities to be resumed while bubble is still collapsed.
// TODO(b/113840485): Having keyguard going away state for secondary displays.
&& display.isDefaultDisplay) {
return false;
}
return display != null ? display.isSleeping() : mAtmService.isSleepingLocked();
}
// TaskFragment.java
boolean shouldSleepActivities() {
return false;
}
总结 & 解决
合理推测:
默认情况下Task是TaskFragment子类,Task重写了shouldSleepActivities方法的话就应该执行Task中的方法。但是在平行窗口模式下,Task中包含TaskFragment对象,且jira中也可看到只在pad/Fold等有平行窗口的机型上上报。
本地验证 :
本地测试发现,平行窗口右侧页面最上方是诸如登录页面的半透明theme样式的页面finish且屏幕此时息屏(本地android studio 赋值DisplayContent的mSleeping为true)可复现该问题。
问题解决:
给谷歌提patch:
https://android-review.googlesource.com/c/platform/frameworks/base/+/2383980
TaskFragment#getVisibility 未处理小窗模式导致递归
复现步骤
- 给微信设置应用锁,以小窗方式打开微信
- 息屏后亮屏,应用锁会启动在微信Task最上面
结果:锁屏解锁后屏幕无反应,过一阵才恢复正常进入桌面
关键log
其次按照上面复现步骤确定ConfirmAccessControl本来的状态应该为resume
.....
// ConfirmAccessControl 一直重复打印resume、pause相关生命周期
// 注意这里每次的线程id不同,说明不是同一个线程堆栈出现递归
07-14 17:40:50.029 1000 2339 2381 I wm_resume_activity: [0,201462615,23,com.miui.securitycenter/com.miui.applicationlock.ConfirmAccessControl]
07-14 17:40:50.032 1000 2339 2381 I wm_pause_activity: [0,201462615,com.miui.securitycenter/com.miui.applicationlock.ConfirmAccessControl,userLeaving=false,pauseBackTasks]
07-14 17:40:50.182 1000 9884 9884 I wm_on_resume_called: [0,201462615,com.miui.applicationlock.ConfirmAccessControl,RESUME_ACTIVITY,62]
07-14 17:40:50.188 1000 9884 9884 I wm_on_paused_called: [0,201462615,com.miui.applicationlock.ConfirmAccessControl,performPause,2]
07-14 17:40:50.195 1000 2339 5156 I wm_resume_activity: [0,201462615,23,com.miui.securitycenter/com.miui.applicationlock.ConfirmAccessControl]
07-14 17:40:50.197 1000 2339 5156 I wm_pause_activity: [0,201462615,com.miui.securitycenter/com.miui.applicationlock.ConfirmAccessControl,userLeaving=false,pauseBackTasks]
07-14 17:40:50.312 1000 9884 9884 I wm_on_resume_called: [0,201462615,com.miui.applicationlock.ConfirmAccessControl,RESUME_ACTIVITY,50]
07-14 17:40:50.317 1000 9884 9884 I wm_on_paused_called: [0,201462615,com.miui.applicationlock.ConfirmAccessControl,performPause,4]
.......
关键堆栈
从关键log可以看出一直在重复执行pause流程,所以在PauseActivityItem 打断点,发现system server的binder线程一直重复执行如下堆栈,且针对的也确实都是ConfirmAccessControl页面。
形成了ActivityClientController.activityPaused -> ClientLifecycleManager.scheduleTransaction,system server <-> app之间的递归。
Breakpoint reached
// system server binder app去执行pause流程
at com.android.server.wm.ClientLifecycleManager.scheduleTransaction(ClientLifecycleManager.java:69)
at com.android.server.wm.TaskFragment.schedulePauseActivity(TaskFragment.java:1847)
at com.android.server.wm.TaskFragment.startPausing(TaskFragment.java:1789)
at com.android.server.wm.TaskFragment.startPausing(TaskFragment.java:1686)
at com.android.server.wm.TaskDisplayArea.lambda$pauseBackTasks$8$com-android-server-wm-TaskDisplayArea(TaskDisplayArea.java:1400)
at com.android.server.wm.TaskDisplayArea$$ExternalSyntheticLambda5.accept(D8$$SyntheticClass:-1)
at com.android.server.wm.Task.forAllLeafTasks(Task.java:3673)
at com.android.server.wm.WindowContainer.forAllLeafTasks(WindowContainer.java:2116)
at com.android.server.wm.TaskDisplayArea.pauseBackTasks(TaskDisplayArea.java:1392)
// 第二次出现resume逻辑,且此次resume的是应用锁下面LauncherUI页面,不合理
at com.android.server.wm.TaskFragment.resumeTopActivity(TaskFragment.java:1325)
at com.android.server.wm.Task.lambda$resumeTopActivityInnerLocked$24(Task.java:5787)
at com.android.server.wm.Task$$ExternalSyntheticLambda33.accept(D8$$SyntheticClass:-1)
at com.android.server.wm.TaskFragment.forAllLeafTaskFragments(TaskFragment.java:1976)
at com.android.server.wm.TaskFragment.forAllLeafTaskFragments(TaskFragment.java:1964)
at com.android.server.wm.Task.resumeTopActivityInnerLocked(Task.java:5780)
at com.android.server.wm.Task.resumeTopActivityUncheckedLocked(Task.java:5702)
at com.android.server.wm.Task.resumeTopActivityUncheckedLocked(Task.java:5762)
at com.android.server.wm.ActivityRecord.makeActiveIfNeeded(ActivityRecord.java:6801)
at com.android.server.wm.EnsureActivitiesVisibleHelper.setActivityVisibilityState(EnsureActivitiesVisibleHelper.java:226)
at com.android.server.wm.EnsureActivitiesVisibleHelper.process(EnsureActivitiesVisibleHelper.java:144)
at com.android.server.wm.TaskFragment.updateActivityVisibilities(TaskFragment.java:1181)
at com.android.server.wm.Task.lambda$ensureActivitiesVisible$23(Task.java:5598)
at com.android.server.wm.Task$$ExternalSyntheticLambda23.accept(D8$$SyntheticClass:-1)
at com.android.server.wm.Task.forAllLeafTasks(Task.java:3673)
at com.android.server.wm.Task.ensureActivitiesVisible(Task.java:5597)
at com.android.server.wm.TaskDisplayArea.lambda$ensureActivitiesVisible$11(TaskDisplayArea.java:1997)
at com.android.server.wm.TaskDisplayArea$$ExternalSyntheticLambda2.accept(D8$$SyntheticClass:-1)
at com.android.server.wm.Task.forAllRootTasks(Task.java:3685)
at com.android.server.wm.WindowContainer.forAllRootTasks(WindowContainer.java:2151)
at com.android.server.wm.WindowContainer.forAllRootTasks(WindowContainer.java:2144)
at com.android.server.wm.TaskDisplayArea.ensureActivitiesVisible(TaskDisplayArea.java:1996)
// 第一次准备resume ConfirmAccessControl页面,正确流程
at com.android.server.wm.TaskFragment.resumeTopActivity(TaskFragment.java:1221)
at com.android.server.wm.Task.resumeTopActivityInnerLocked(Task.java:5779)
at com.android.server.wm.Task.resumeTopActivityUncheckedLocked(Task.java:5702)
at com.android.server.wm.Task.resumeTopActivityUncheckedLocked(Task.java:5727)
at com.android.server.wm.RootWindowContainer.resumeFocusedTasksTopActivities(RootWindowContainer.java:2531)
at com.android.server.wm.RootWindowContainer.resumeFocusedTasksTopActivities(RootWindowContainer.java:2517)
at com.android.server.wm.TaskFragment.completePause(TaskFragment.java:1917)
at com.android.server.wm.ActivityRecord.activityPaused(ActivityRecord.java:7035)
// app进程binder 系统pause已完成
at com.android.server.wm.ActivityClientController.activityPaused(ActivityClientController.java:195)
at android.app.IActivityClientController$Stub.onTransact(IActivityClientController.java:592)
at com.android.server.wm.ActivityClientController.onTransact(ActivityClientController.java:133)
at android.os.Binder.execTransactInternal(Binder.java:1285)
at android.os.Binder.execTransact(Binder.java:1249)
PauseActivityItem{finished=false,userLeaving=false,configChanges=0,dontReport=false,autoEnteringPip=false}
关键代码
不合理的TaskFragment结构
从上面堆栈可以看出第二次resume 应用锁下面的LauncherUI页面不太合理,查看微信Task发现如下不合理的结构:
所以推测上面的第一次resume是正常resume RootTask的直接child ConfirmAccessControl页面,第二次resume 异常resume了Task的child的TaskFragment中的LauncherUI页面。
从上面堆栈可以看出第二次resume的触发点是下面resumeTopActivityInnerLocked方法的forAllLeafTaskFragments的callback中。
而forAllLeafTaskFragments 主要是遍历Task下面的TaskFragment的并执行resumeTopActivity操作,一般callback执行场景应该是在平行窗口的场景中,确保左右两个TaskFragment中的top页面能正常resume。
@GuardedBy("mService")
private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options,
boolean deferPause) {
if (!mAtmService.isBooting() && !mAtmService.isBooted()) {
// Not ready yet!
return false;
}
final ActivityRecord topActivity = topRunningActivity(true /* focusableOnly */);
if (topActivity == null) {
// There are no activities left in this task, let's look somewhere else.
return resumeNextFocusableActivityWhenRootTaskIsEmpty(prev, options);
}
final boolean[] resumed = new boolean[1];
// 第一次resume,正常页面
final TaskFragment topFragment = topActivity.getTaskFragment();
resumed[0] = topFragment.resumeTopActivity(prev, options, deferPause);
forAllLeafTaskFragments(f -> {
if (topFragment == f) {
return;
}
if (!f.canBeResumed(null /* starting */)) {
return;
}
// 第二次resume到TaskFragment中的页面
resumed[0] |= f.resumeTopActivity(prev, options, deferPause);
}, true);
return resumed[0];
}
查看该Task中的TaskFragment创建是否合理:
// 微信进程创建TaskFragment堆栈如下
createTaskFragment:482, WindowContainerTransaction (android.window)
createTaskFragment:251, JetpackTaskFragmentOrganizer (androidx.window.extensions.embedding)
createTaskFragment:434, SplitPresenter (androidx.window.extensions.embedding)
createTaskFragment:241, JetpackTaskFragmentOrganizer (androidx.window.extensions.embedding)
createTaskFragmentAndReparentActivity:262, JetpackTaskFragmentOrganizer (androidx.window.extensions.embedding)
expandActivity:199, JetpackTaskFragmentOrganizer (androidx.window.extensions.embedding)
expandActivity:682, SplitController (androidx.window.extensions.embedding)
resolveActivityToContainer:560, SplitController (androidx.window.extensions.embedding)
onActivityCreated:494, SplitController (androidx.window.extensions.embedding)
onActivityPostCreated:1796, SplitController$LifecycleCallbacks (androidx.window.extensions.embedding)
dispatchActivityPostCreated:395, Application (android.app)
dispatchActivityPostCreated:1486, Activity (android.app)
performCreate:8590, Activity (android.app)
本地调试微信的WelcomeActivity满足了mSplitRules规则,走了expandActivity的逻辑,导致创建了TaskFragment,但这些规则是微信自己适配谷歌平行窗口的,所以不能从创建TaskFragment来规避该问题。
// SplitController.java
@VisibleForTesting
boolean resolveActivityToContainer(@NonNull Activity activity, boolean isOnReparent) {
// 1. Whether the new launched activity should always expand.
if (shouldExpand(activity, null /* intent */)) {
expandActivity(activity);
return true;
}
全屏场景正常
本地测试全屏场景下按以上步骤不能复现问题,经调试发现不满足canBeResumed,直接return,走不到下面的resumeTopActivity流程,形成不了递归。
// Task.java
@GuardedBy("mService")
private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options,
boolean deferPause) {
.......
final boolean[] resumed = new boolean[1];
final TaskFragment topFragment = topActivity.getTaskFragment();
resumed[0] = topFragment.resumeTopActivity(prev, options, deferPause);
forAllLeafTaskFragments(f -> {
if (topFragment == f) {
return;
}
// 全屏情况下,不满足canBeResumed,直接return,走不到下面的resumeTopActivity流程,形成不了递归
if (!f.canBeResumed(null /* starting */)) {
return;
}
resumed[0] |= f.resumeTopActivity(prev, options, deferPause);
}, true);
return resumed[0];
}
所以来看看canBeResumed方法
// TaskFragment.java
boolean canBeResumed(@Nullable ActivityRecord starting) {
// No need to resume activity in TaskFragment that is not visible.
return isTopActivityFocusable()
&& getVisibility(starting) == TASK_FRAGMENT_VISIBILITY_VISIBLE;
}
- 全屏时getVisibility(starting)获取到的是TASK_FRAGMENT_VISIBILITY_INVISIBLE,所以此处canBeResumed返回false
- 而小窗下获取到的是TASK_FRAGMENT_VISIBILITY_VISIBLE,所以会导致这个问题
// TaskFragment.java
int getVisibility(ActivityRecord starting) {
........
// 这里的other即ConfirmAccessControl页面
final int otherWindowingMode = other.getWindowingMode();
if (otherWindowingMode == WINDOWING_MODE_FULLSCREEN) {
if (isTranslucent(other, starting)) {
// Can be visible behind a translucent fullscreen TaskFragment.
gotTranslucentFullscreen = true;
continue;
}
// 全屏直接返回,不递归
return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
// 小窗的mode为WINDOWING_MODE_FREEFORM,也不满足下面的条件
} else if (otherWindowingMode == WINDOWING_MODE_MULTI_WINDOW
&& other.matchParentBounds()) {
if (isTranslucent(other, starting)) {
// Can be visible behind a translucent TaskFragment.
gotTranslucentFullscreen = true;
continue;
}
// Multi-window TaskFragment that matches parent bounds would occlude other children
return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
}
.....
// 小窗最后走的这里的流程,返回TASK_FRAGMENT_VISIBILITY_VISIBLE
// Lastly - check if there is a translucent fullscreen TaskFragment on top.
return gotTranslucentFullscreen
? TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT
: TASK_FRAGMENT_VISIBILITY_VISIBLE;
}
总结 & 解决
合理总结:
上面的getVisibility方法中,小窗应该也跟multi window mode一样处理,毕竟它覆盖住了同一层级底下的TaskFragment,所以该TaskFragment的getVisibility应该也返回TASK_FRAGMENT_VISIBILITY_INVISIBLE。
给谷歌提patch:
谷歌merge
https://android-review.googlesource.com/c/platform/frameworks/base/+/2674876
WPC#updateTopResumingActivityInProcessIfNeeded方法未处理Stopping列表
关键log
// 看event log是startPausing过程中陷入递归
08-19 00:27:07.266 1000 11789 17981 I wm_create_activity: [0,93149810,1422,com.taobao.idlefish/.maincontainer.activity.MainActivity,android.intent.action.MAIN,NULL,NULL,270598144]
08-19 00:27:08.036 1000 11789 17981 I wm_restart_activity: [0,93149810,1422,com.taobao.idlefish/.maincontainer.activity.MainActivity]
08-19 00:27:08.978 1000 11789 21836 I wm_finish_activity: [0,197701613,1422,com.lbe.security.miui/com.android.packageinstaller.permission.ui.GrantPermissionsActivity,app-request]
08-19 00:27:09.000 10284 16745 16745 I wm_on_create_called: [0,93149810,com.taobao.idlefish.maincontainer.activity.MainActivity,performCreate,395]
// 陷入递归的页面为GrantPermissionsActivity
08-19 00:27:09.008 1000 11789 21836 I wm_pause_activity: [0,197701613,com.lbe.security.miui/com.android.packageinstaller.permission.ui.GrantPermissionsActivity,userLeaving=false,finish]
08-19 00:27:09.035 10190 13482 13482 I wm_on_paused_called: [0,197701613,com.android.packageinstaller.permission.ui.GrantPermissionsActivity,performPause,0]
// 重复打印的log
08-19 00:27:09.056 1000 11789 11844 I am_wtf : [0,11789,system_server,-1,ActivityTaskManager,Going to pause when pause is already pending for ActivityRecord\\\{bc8afed u0 com.lbe.security.miui/com.android.packageinstaller.permission.ui.GrantPermissionsActivity t1422 f}} state=FINISHING]
.......
08-19 00:28:03.749 1000 11789 11844 I am_wtf : [0,11789,system_server,-1,ActivityTaskManager,Going to pause when pause is already pending for ActivityRecord\\{bc8afed u0 com.lbe.security.miui/com.android.packageinstaller.permission.ui.GrantPermissionsActivity t1422 f}} state=FINISHING]
关键堆栈
看下面堆栈可以看出TaskFragment.startPausing- >WindowProcessController.updateTopResumingActivityInProcessIfNeeded之间的递归。
at com.android.server.wm.TaskFragment.startPausing(TaskFragment.java:1773)
........
// 第二次循环堆栈
at com.android.server.wm.TaskFragment.startPausing(TaskFragment.java:1779)
at com.android.server.wm.WindowProcessController.updateTopResumingActivityInProcessIfNeeded(WindowProcessController.java:925)
at com.android.server.wm.ActivityRecord.canResumeByCompat(ActivityRecord.java:11696)
at com.android.server.wm.ActivityRecord.shouldBeResumed(ActivityRecord.java:7019)
at com.android.server.wm.ActivityRecord.shouldResumeActivity(ActivityRecord.java:7007)
at com.android.server.wm.ActivityRecord.makeActiveIfNeeded(ActivityRecord.java:6940)
at com.android.server.wm.RootWindowContainer.lambda$resumeFocusedTasksTopActivities$17(RootWindowContainer.java:2654)
at com.android.server.wm.RootWindowContainer$$ExternalSyntheticLambda14.accept(unavailable:13)
at com.android.server.wm.Task.forAllRootTasks(Task.java:3677)
at com.android.server.wm.WindowContainer.forAllRootTasks(WindowContainer.java:2231)
at com.android.server.wm.WindowContainer.forAllRootTasks(WindowContainer.java:2231)
at com.android.server.wm.WindowContainer.forAllRootTasks(WindowContainer.java:2231)
... repeated 2 times
at com.android.server.wm.WindowContainer.forAllRootTasks(WindowContainer.java:2224)
at com.android.server.wm.RootWindowContainer.resumeFocusedTasksTopActivities(RootWindowContainer.java:2628)
at com.android.server.wm.RootWindowContainer.resumeFocusedTasksTopActivities(RootWindowContainer.java:2606)
at com.android.server.wm.RootWindowContainer.resumeFocusedTasksTopActivities(RootWindowContainer.java:2601)
at com.android.server.wm.ActivityRecord.addToFinishingAndWaitForIdle(ActivityRecord.java:4405)
at com.android.server.wm.ActivityRecord.completeFinishing(ActivityRecord.java:4322)
at com.android.server.wm.TaskFragment.completePause(TaskFragment.java:1951)
// 第一次循环堆栈
at com.android.server.wm.TaskFragment.startPausing(TaskFragment.java:1779)
at com.android.server.wm.WindowProcessController.updateTopResumingActivityInProcessIfNeeded(WindowProcessController.java:925)
at com.android.server.wm.ActivityRecord.canResumeByCompat(ActivityRecord.java:11696)
at com.android.server.wm.ActivityRecord.shouldBeResumed(ActivityRecord.java:7019)
at com.android.server.wm.ActivityRecord.shouldResumeActivity(ActivityRecord.java:7007)
at com.android.server.wm.ActivityRecord.makeActiveIfNeeded(ActivityRecord.java:6940)
at com.android.server.wm.RootWindowContainer.lambda$resumeFocusedTasksTopActivities$17(RootWindowContainer.java:2654)
at com.android.server.wm.RootWindowContainer$$ExternalSyntheticLambda14.accept(unavailable:13)
at com.android.server.wm.Task.forAllRootTasks(Task.java:3677)
at com.android.server.wm.WindowContainer.forAllRootTasks(WindowContainer.java:2231)
at com.android.server.wm.WindowContainer.forAllRootTasks(WindowContainer.java:2231)
at com.android.server.wm.WindowContainer.forAllRootTasks(WindowContainer.java:2231)
... repeated 2 times
at com.android.server.wm.WindowContainer.forAllRootTasks(WindowContainer.java:2224)
at com.android.server.wm.RootWindowContainer.resumeFocusedTasksTopActivities(RootWindowContainer.java:2628)
at com.android.server.wm.RootWindowContainer.resumeFocusedTasksTopActivities(RootWindowContainer.java:2606)
at com.android.server.wm.RootWindowContainer.resumeFocusedTasksTopActivities(RootWindowContainer.java:2601)
at com.android.server.wm.ActivityRecord.addToFinishingAndWaitForIdle(ActivityRecord.java:4405)
at com.android.server.wm.ActivityRecord.completeFinishing(ActivityRecord.java:4322)
at com.android.server.wm.TaskFragment.completePause(TaskFragment.java:1951)
at com.android.server.wm.ActivityRecord.activityPaused(ActivityRecord.java:7188)
at com.android.server.wm.ActivityClientController.activityPaused(ActivityClientController.java:243)
- locked <0x08c0fce1> (a com.android.server.wm.WindowManagerGlobalLock)
at android.app.IActivityClientController$Stub.onTransact(IActivityClientController.java:673)
at com.android.server.wm.ActivityClientController.onTransact(ActivityClientController.java:171)
at android.os.Binder.execTransactInternal(Binder.java:1346)
at android.os.Binder.execTransact(Binder.java:1282)
关键代码
log打印的地方:
// TaskFragment.java
boolean startPausing(boolean userLeaving, boolean uiSleeping, ActivityRecord resuming,
String reason) {
......
if (mPausingActivity != null) {
Slog.wtf(TAG, "Going to pause when pause is already pending for " + mPausingActivity
+ " state=" + mPausingActivity.getState());
if (!shouldSleepActivities()) {
// Avoid recursion among check for sleep and complete pause during sleeping.
// Because activity will be paused immediately after resume, just let pause
// be completed by the order of activity paused from clients.
completePause(false, resuming);
}
}
主要看下updateTopResumingActivityInProcessIfNeeded 的代码逻辑
- 上面陷入pause循环的activity所在Task对应的app的targetSdkVersion 小于29
上面的event log可以看出发生递归的GrantPermissionsActivity 处于task#1422,对应的app为com.taobao.idlefish
// 1. 该app的targetSdk确实小于28
Package [com.taobao.idlefish] (4d601a0):
appId=10284
pkg=Package{55da159 com.taobao.idlefish}
.......
versionCode=340 minSdk=21 targetSdk=28
- mPreQTopResumedActivity 和 pause循环的activity处于同一个Task/TaskFragment
因为InitActivity为闲鱼的第一个actiivty,所以这里的topDisplay为null,赋值InitActivity到mPreQTopResumedActivity - mPreQTopResumedActivity 不为null且处于RESUMED状态
// topRunningActivity为MainActivity,但是这里这个生命周期有点奇怪,无法推断出具体的场景
08-19 00:27:07.266 1000 11789 17981 I wm_create_activity: [0,93149810,1422,com.taobao.idlefish/.maincontainer.activity.MainActivity,android.intent.action.MAIN,NULL,NULL,270598144]
08-19 00:27:08.036 1000 11789 17981 I wm_restart_activity: [0,93149810,1422,com.taobao.idlefish/.maincontainer.activity.MainActivity]
// 因为InitActivity还没执行pause,所以它的状态仍然是RESUMED
08-19 00:27:08.041 1000 11789 17981 I wm_add_to_stopping: [0,3457984,com.taobao.idlefish/com.taobao.fleamarket.home.activity.InitActivity,makeInvisible]
// WindowProcessController#updateTopResumingActivityInProcessIfNeeded
boolean updateTopResumingActivityInProcessIfNeeded(@NonNull ActivityRecord activity) {
// 对于大部分app来说,基本在这里直接返回,只有targetSdk小于Q的app才有可能发生这个问题
if (mInfo.targetSdkVersion >= Q || mPreQTopResumedActivity == activity) {
return true;
}
if (!activity.isAttached()) {
// No need to update if the activity hasn't attach to any display.
return false;
}
boolean canUpdate = false;
// 因为堆栈走到了下面的taskFrag.startPausing,所以mPreQTopResumedActivity != null
// 但是对于Task中的第一个Activity而言,此处的mPreQTopResumedActivity为null,topDisplay为null
final DisplayContent topDisplay =
(mPreQTopResumedActivity != null && mPreQTopResumedActivity.isAttached())
? mPreQTopResumedActivity.mDisplayContent
: null;
// Update the topmost activity if current top activity is
// - not on any display OR
// - no longer visible OR
// - not focusable (in PiP mode for instance)
// 第一个Activity会走如下if语句
if (topDisplay == null
|| !mPreQTopResumedActivity.isVisibleRequested()
|| !mPreQTopResumedActivity.isFocusable()) {
canUpdate = true;
}
final DisplayContent display = activity.mDisplayContent;
// Update the topmost activity if the current top activity wasn't on top of the other one.
if (!canUpdate && topDisplay.compareTo(display) < 0) {
canUpdate = true;
}
// Update the topmost activity if the activity has higher z-order than the current
// top-resumed activity.
if (!canUpdate) {
final ActivityRecord ar = topDisplay.getActivity(r -> r == activity,
true /* traverseTopToBottom */, mPreQTopResumedActivity);
if (ar != null && ar != mPreQTopResumedActivity) {
canUpdate = true;
}
}
// 第一个Activity直接赋值到mPreQTopResumedActivity中
if (canUpdate) {
// Make sure the previous top activity in the process no longer be resumed.
if (mPreQTopResumedActivity != null && mPreQTopResumedActivity.isState(RESUMED)) {
final TaskFragment taskFrag = mPreQTopResumedActivity.getTaskFragment();
if (taskFrag != null) {
boolean userLeaving = taskFrag.shouldBeVisible(null);
taskFrag.startPausing(userLeaving, false /* uiSleeping */,
activity, "top-resumed-changed");
}
}
mPreQTopResumedActivity = activity;
}
return canUpdate;
}
总结 & 解决
本地正常情况基本都是先pause在执行addToStoppping流程,所以InitActivity基本应该都是Paused状态而不是Resumed状态。
或许可以通过如下修改来规避这个问题,但是具体场景不可预知,无法验证,而且看这个场景发生此次递归的概率极小。暂不提change,先观察复现。
// WindowProcessController#updateTopResumingActivityInProcessIfNeeded
if (canUpdate) {
// Make sure the previous top activity in the process no longer be resumed.
- if (mPreQTopResumedActivity != null && mPreQTopResumedActivity.isState(RESUMED)) {
+ if (mPreQTopResumedActivity != null && mPreQTopResumedActivity.isState(RESUMED)
+ && !mAtm.mTaskSupervisor.mStoppingActivities.contains(mPreQTopResumedActivity)) {
final TaskFragment taskFrag = mPreQTopResumedActivity.getTaskFragment();
if (taskFrag != null) {
boolean userLeaving = taskFrag.shouldBeVisible(null);
taskFrag.startPausing(userLeaving, false /* uiSleeping */,
activity, "top-resumed-changed");
@@ -926,7 +927,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
activity, "top-resumed-changed");
}
}