From a5cf6804fc4b78265e84d69e139f9b9f893a89c0 Mon Sep 17 00:00:00 2001 From: Jorim Jaggi Date: Fri, 26 Apr 2019 19:43:11 +0200 Subject: [PATCH] Do not execute app transition too early We execute an app transition if something was moved to the front, but that activity was already resumed because when moving a task to the front, we rely on the activityResumed callback to update visibility and execute the transition usually, which doesn't happen in that case. However, we should only do that for the top task/stack as otherwise we may execute an app transition too early in case an activity behind the top one is still visible and resumed, like it can happen when Launcher finishes the recents animation and quickly launches the next one. Test: Quick switch 100 times, no flashing Bug: 129729087 Change-Id: Id76643e01c8e1c4b3ffd0d5a2fa38a35336e1d9e --- .../server/wm/RootActivityContainer.java | 5 +- .../server/wm/RootActivityContainerTests.java | 47 +++++++++++++++++++ 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/services/core/java/com/android/server/wm/RootActivityContainer.java b/services/core/java/com/android/server/wm/RootActivityContainer.java index 46b5f3ab38f9b..977906052ec00 100644 --- a/services/core/java/com/android/server/wm/RootActivityContainer.java +++ b/services/core/java/com/android/server/wm/RootActivityContainer.java @@ -1150,8 +1150,9 @@ class RootActivityContainer extends ConfigurationContainer resumedOnDisplay |= result; continue; } - if (topRunningActivity.isState(RESUMED)) { - // Kick off any lingering app transitions form the MoveTaskToFront operation. + if (display.isTopStack(stack) && topRunningActivity.isState(RESUMED)) { + // Kick off any lingering app transitions form the MoveTaskToFront operation, + // but only consider the top task and stack on that display. stack.executeAppTransition(targetOptions); } else { resumedOnDisplay |= topRunningActivity.makeActiveIfNeeded(target); diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java index 94def2bb78465..f58559f124381 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java @@ -62,6 +62,7 @@ import android.util.Pair; import androidx.test.filters.MediumTest; import com.android.internal.app.ResolverActivity; +import com.android.server.wm.ActivityStack.ActivityState; import org.junit.Before; import org.junit.Test; @@ -394,6 +395,52 @@ public class RootActivityContainerTests extends ActivityTestsBase { eq(activity), eq(null /* targetOptions */)); } + /** + * Verify that a lingering transition is being executed in case the activity to be resumed is + * already resumed + */ + @Test + public void testResumeActivityLingeringTransition() { + // Create a stack at top. + final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay(); + final ActivityStack targetStack = spy(display.createStack(WINDOWING_MODE_FULLSCREEN, + ACTIVITY_TYPE_STANDARD, false /* onTop */)); + final TaskRecord task = new TaskBuilder(mSupervisor).setStack(targetStack).build(); + final ActivityRecord activity = new ActivityBuilder(mService).setTask(task).build(); + activity.setState(ActivityState.RESUMED, "test"); + + // Assume the stack is at the topmost position + assertTrue(targetStack.isTopStackOnDisplay()); + + // Use the stack as target to resume. + mRootActivityContainer.resumeFocusedStacksTopActivities(); + + // Verify the lingering app transition is being executed because it's already resumed + verify(targetStack, times(1)).executeAppTransition(any()); + } + + @Test + public void testResumeActivityLingeringTransition_notExecuted() { + // Create a stack at bottom. + final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay(); + final ActivityStack targetStack = spy(display.createStack(WINDOWING_MODE_FULLSCREEN, + ACTIVITY_TYPE_STANDARD, false /* onTop */)); + final TaskRecord task = new TaskBuilder(mSupervisor).setStack(targetStack).build(); + final ActivityRecord activity = new ActivityBuilder(mService).setTask(task).build(); + activity.setState(ActivityState.RESUMED, "test"); + display.positionChildAtBottom(targetStack); + + // Assume the stack is at the topmost position + assertFalse(targetStack.isTopStackOnDisplay()); + doReturn(targetStack).when(mRootActivityContainer).getTopDisplayFocusedStack(); + + // Use the stack as target to resume. + mRootActivityContainer.resumeFocusedStacksTopActivities(); + + // Verify the lingering app transition is being executed because it's already resumed + verify(targetStack, never()).executeAppTransition(any()); + } + /** * Tests that home activities can be started on the displays that supports system decorations. */