Allow to collect moving back task in open transition
Simulation:
Task A starts(NEW_TASK) Task B, finishes
Task B starts(NEW_TASK) Task C
Task B calls moveTaskToBack in onPause
Task C (declare as translucent and sleep 200ms in
onCreate to delay transition)
starts(NEW_TASK) Task B -> attempt to move task B to front
finishes
Because the initial open transition may be done until the last
step, then moveTaskToBack for B will run after the last step.
Then expect B should be on top but the result is on home screen
because A and C are finished, and B is moved to back.
This change is kinda a workaround to mitigate the case of
deferred move-to-back. Because it is regular to have closing
targets in an open transition, the moving task can run with
the collecting transition directly.
Fix: 325645759
Test: atest TransitionTests#testDeferredMoveTaskToBack
(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:7079ba9aad0012ae53cb689dc23740b99ed27d15)
Merged-In: I46d8e26110ebefea74e5dd06c85c003b12693416
Change-Id: I46d8e26110ebefea74e5dd06c85c003b12693416
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 1353ff0..2212724 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -5770,7 +5770,7 @@
// If we have a watcher, preflight the move before committing to it. First check
// for *other* available tasks, but if none are available, then try again allowing the
// current task to be selected.
- if (isTopRootTaskInDisplayArea() && mAtmService.mController != null) {
+ if (mAtmService.mController != null && isTopRootTaskInDisplayArea()) {
ActivityRecord next = topRunningActivity(null, task.mTaskId);
if (next == null) {
next = topRunningActivity(null, INVALID_TASK_ID);
@@ -5814,6 +5814,15 @@
+ tr.mTaskId);
if (mTransitionController.isShellTransitionsEnabled()) {
+ // TODO(b/277838915): Consider to make it concurrent to eliminate the special case.
+ final Transition collecting = mTransitionController.getCollectingTransition();
+ if (collecting != null && collecting.mType == TRANSIT_OPEN) {
+ // It can be a CLOSING participate of an OPEN transition. This avoids the deferred
+ // transition from moving task to back after the task was moved to front.
+ collecting.collect(tr);
+ moveTaskToBackInner(tr, collecting);
+ return true;
+ }
final Transition transition = new Transition(TRANSIT_TO_BACK, 0 /* flags */,
mTransitionController, mWmService.mSyncEngine);
// Guarantee that this gets its own transition by queueing on SyncEngine
@@ -5842,7 +5851,7 @@
return true;
}
- private boolean moveTaskToBackInner(@NonNull Task task, @Nullable Transition transition) {
+ private void moveTaskToBackInner(@NonNull Task task, @Nullable Transition transition) {
final Transition.ReadyCondition movedToBack =
new Transition.ReadyCondition("moved-to-back", task);
if (transition != null) {
@@ -5857,7 +5866,7 @@
if (inPinnedWindowingMode()) {
mTaskSupervisor.removeRootTask(this);
- return true;
+ return;
}
mRootWindowContainer.ensureVisibilityAndConfig(null /* starting */,
@@ -5880,7 +5889,6 @@
} else {
mRootWindowContainer.resumeFocusedTasksTopActivities();
}
- return true;
}
boolean willActivityBeVisible(IBinder token) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index ce890f6..9657eb2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -64,8 +64,10 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -2367,9 +2369,7 @@
assertTrue(transitA.isCollecting());
// finish collecting A
- transitA.start();
- transitA.setAllReady();
- mSyncEngine.tryFinishForTest(transitA.getSyncId());
+ tryFinishTransitionSyncSet(transitA);
waitUntilHandlersIdle();
assertTrue(transitA.isPlaying());
@@ -2475,6 +2475,36 @@
}
@Test
+ public void testDeferredMoveTaskToBack() {
+ final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
+ final Task task = activity.getTask();
+ registerTestTransitionPlayer();
+ final TransitionController controller = mWm.mRoot.mTransitionController;
+ mSyncEngine = createTestBLASTSyncEngine();
+ controller.setSyncEngine(mSyncEngine);
+ final Transition transition = createTestTransition(TRANSIT_CHANGE, controller);
+ controller.moveToCollecting(transition);
+ task.moveTaskToBack(task);
+ // Actual action will be deferred by current transition.
+ verify(task, never()).moveToBack(any(), any());
+
+ tryFinishTransitionSyncSet(transition);
+ waitUntilHandlersIdle();
+ // Continue to move task to back after the transition is done.
+ verify(task).moveToBack(any(), any());
+ final Transition moveBackTransition = controller.getCollectingTransition();
+ assertNotNull(moveBackTransition);
+ moveBackTransition.abort();
+
+ // The move-to-back can be collected in to a collecting OPEN transition.
+ clearInvocations(task);
+ final Transition transition2 = createTestTransition(TRANSIT_OPEN, controller);
+ controller.moveToCollecting(transition2);
+ task.moveTaskToBack(task);
+ verify(task).moveToBack(any(), any());
+ }
+
+ @Test
public void testNoSyncFlagIfOneTrack() {
final TransitionController controller = mAtm.getTransitionController();
final TestTransitionPlayer player = registerTestTransitionPlayer();
@@ -2491,17 +2521,11 @@
controller.startCollectOrQueue(transitC, (deferred) -> {});
// Verify that, as-long as there is <= 1 track, we won't get a SYNC flag
- transitA.start();
- transitA.setAllReady();
- mSyncEngine.tryFinishForTest(transitA.getSyncId());
+ tryFinishTransitionSyncSet(transitA);
assertTrue((player.mLastReady.getFlags() & FLAG_SYNC) == 0);
- transitB.start();
- transitB.setAllReady();
- mSyncEngine.tryFinishForTest(transitB.getSyncId());
+ tryFinishTransitionSyncSet(transitB);
assertTrue((player.mLastReady.getFlags() & FLAG_SYNC) == 0);
- transitC.start();
- transitC.setAllReady();
- mSyncEngine.tryFinishForTest(transitC.getSyncId());
+ tryFinishTransitionSyncSet(transitC);
assertTrue((player.mLastReady.getFlags() & FLAG_SYNC) == 0);
}
@@ -2610,6 +2634,12 @@
assertEquals("reason1", condition1.mAlternate);
}
+ private void tryFinishTransitionSyncSet(Transition transition) {
+ transition.setAllReady();
+ transition.start();
+ mSyncEngine.tryFinishForTest(transition.getSyncId());
+ }
+
private static void makeTaskOrganized(Task... tasks) {
final ITaskOrganizer organizer = mock(ITaskOrganizer.class);
for (Task t : tasks) {