Update visibility when visible activity crashed
Previously, if a top fullscreen app is crashed when a display
transition is collecting, the transition will be ready and sent
directly from finishTopCrashedActivityLocked -> finishIfPossible
-> continueWindowLayout -> BLASTSyncEngine.SyncGroup#tryFinish.
That causes the transition to contain a change to hide display
area because nothing is visible in it.
Now finishTopCrashedActivities has similar steps as handleAppDied.
If there is perceptible activity in the crashed process, the
next top activity will be requested to visible at the same time.
Then the transition can recognize that the display area is visible.
Note that finishTopCrashedActivityLocked calls finishIfPossible,
so it may already adjust focusable task. But because the crashing
activity is pausing (pause->resume from switch display and then
resume -> pausing when finishing for crash), the next activity
won't resume until the pausing is gone. Then it requires
ensureActivitiesVisible to make the current top activity (its
task was moved to front when adjusting focusable task) visible.
Bug: 300790517
Test: RootWindowContainerTests#testRemovingRootTaskOnAppCrash
Change-Id: I935121ed53638720ec3c8ef872f7c78e90edd714
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 237bc92..62f22f0 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -6420,19 +6420,8 @@
if (!restarting && hasVisibleActivities) {
deferWindowLayout();
try {
- final Task topTask = mRootWindowContainer.getTopDisplayFocusedRootTask();
- if (topTask != null
- && topTask.topRunningActivity(true /* focusableOnly */) == null) {
- topTask.adjustFocusToNextFocusableTask("handleAppDied");
- }
- if (!mRootWindowContainer.resumeFocusedTasksTopActivities()) {
- // If there was nothing to resume, and we are not already restarting
- // this process, but there is a visible activity that is hosted by the
- // process...then make sure all visible activities are running, taking
- // care of restarting this process.
- mRootWindowContainer.ensureActivitiesVisible(null, 0,
- !PRESERVE_WINDOWS);
- }
+ mRootWindowContainer.ensureVisibilityOnVisibleActivityDiedOrCrashed(
+ "handleAppDied");
} finally {
continueWindowLayout();
}
@@ -6873,7 +6862,18 @@
@Override
public int finishTopCrashedActivities(WindowProcessController crashedApp, String reason) {
synchronized (mGlobalLock) {
- return mRootWindowContainer.finishTopCrashedActivities(crashedApp, reason);
+ deferWindowLayout();
+ try {
+ final Task finishedTask = mRootWindowContainer.finishTopCrashedActivities(
+ crashedApp, reason);
+ if (finishedTask != null) {
+ mRootWindowContainer.ensureVisibilityOnVisibleActivityDiedOrCrashed(reason);
+ return finishedTask.mTaskId;
+ }
+ return INVALID_TASK_ID;
+ } finally {
+ continueWindowLayout();
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 2a33918..f1586cd 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2297,19 +2297,36 @@
*
* @param app The app that crashed.
* @param reason Reason to perform this action.
- * @return The task id that was finished in this root task, or INVALID_TASK_ID if none was
- * finished.
+ * @return The finished task which was on top or visible, otherwise {@code null} if the crashed
+ * app doesn't have activity in visible task.
*/
- int finishTopCrashedActivities(WindowProcessController app, String reason) {
+ @Nullable
+ Task finishTopCrashedActivities(WindowProcessController app, String reason) {
Task focusedRootTask = getTopDisplayFocusedRootTask();
final Task[] finishedTask = new Task[1];
forAllRootTasks(rootTask -> {
+ final boolean recordTopOrVisible = finishedTask[0] == null
+ && (focusedRootTask == rootTask || rootTask.isVisibleRequested());
final Task t = rootTask.finishTopCrashedActivityLocked(app, reason);
- if (rootTask == focusedRootTask || finishedTask[0] == null) {
+ if (recordTopOrVisible) {
finishedTask[0] = t;
}
});
- return finishedTask[0] != null ? finishedTask[0].mTaskId : INVALID_TASK_ID;
+ return finishedTask[0];
+ }
+
+ void ensureVisibilityOnVisibleActivityDiedOrCrashed(String reason) {
+ final Task topTask = getTopDisplayFocusedRootTask();
+ if (topTask != null && topTask.topRunningActivity(true /* focusableOnly */) == null) {
+ // Move the next focusable task to front.
+ topTask.adjustFocusToNextFocusableTask(reason);
+ }
+ if (!resumeFocusedTasksTopActivities()) {
+ // It may be nothing to resume because there are pausing activities or all the top
+ // activities are resumed. Then it still needs to make sure all visible activities are
+ // running in case the tasks were reordered or there are non-top visible activities.
+ ensureActivitiesVisible(null /* starting */, 0 /* configChanges */, !PRESERVE_WINDOWS);
+ }
}
boolean resumeFocusedTasksTopActivities() {
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index 3bc6450..c241033 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -549,10 +549,12 @@
// Let's pretend that the app has crashed.
firstActivity.app.setThread(null);
- mRootWindowContainer.finishTopCrashedActivities(firstActivity.app, "test");
+ final Task finishedTask = mRootWindowContainer.finishTopCrashedActivities(
+ firstActivity.app, "test");
// Verify that the root task was removed.
assertEquals(originalRootTaskCount, defaultTaskDisplayArea.getRootTaskCount());
+ assertEquals(rootTask, finishedTask);
}
/**