From 8d5de97b83130686c83fed2f13dacea1bd7419eb Mon Sep 17 00:00:00 2001 From: Riddle Hsu Date: Thu, 24 Dec 2020 17:57:57 +0800 Subject: [PATCH] Fix result code of launching existing activity in split Since all secondary leaf tasks will be reparented to the organizer created task, when launching an existing task in background in split-screen mode, the root task mTargetStack is always the organizer created task which is always the top of display area. Then mMovedToFront is always set to false. This may cause missing some launch metrics in split-screen because ActivityMetricsLogger won't count the number for result code START_DELIVERED_TO_TOP. The test testSplitScreenTaskToFront didn't catch the problem because it still creates tasks in the old way (multiple split secondary root tasks under display area) which doesn't exist in current design. Fixes: 176277089 Test: ActivityStarterTests#testSplitScreenDeliverToTop ActivityStarterTests#testSplitScreenTaskToFront Change-Id: I6bdb1faf8f7dbfb1e8a6f75279b87ffc9072fb31 --- .../android/server/wm/ActivityStarter.java | 12 ++-- .../server/wm/ActivityStarterTests.java | 55 ++++++++++--------- .../android/server/wm/WindowTestsBase.java | 3 + 3 files changed, 38 insertions(+), 32 deletions(-) diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 1b0bec5f34026..e002e6248982c 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -2604,11 +2604,6 @@ class ActivityStarter { final Task launchStack = getLaunchStack(mStartActivity, mLaunchFlags, intentTask, mOptions); if (launchStack == null || launchStack == mTargetStack) { - // Do not set mMovedToFront to true below for split-screen-top stack, or - // START_TASK_TO_FRONT will be returned and trigger unexpected animations when a - // new intent has delivered. - final boolean isSplitScreenTopStack = mTargetStack.isTopSplitScreenStack(); - // TODO(b/151572268): Figure out a better way to move tasks in above 2-levels // tasks hierarchies. if (mTargetStack != intentTask @@ -2617,6 +2612,11 @@ class ActivityStarter { false /* includingParents */); intentTask = intentTask.getParent().asTask(); } + // If the task is in multi-windowing mode, the activity may already be on + // the top (visible to user but not the global top), then the result code + // should be START_DELIVERED_TO_TOP instead of START_TASK_TO_FRONT. + final boolean wasTopOfVisibleRootTask = intentActivity.mVisibleRequested + && intentActivity == mTargetStack.topRunningActivity(); // We only want to move to the front, if we aren't going to launch on a // different stack. If we launch on a different stack, we will put the // task on top there. @@ -2625,7 +2625,7 @@ class ActivityStarter { mTargetStack.moveTaskToFront(intentTask, mNoAnimation, mOptions, mStartActivity.appTimeTracker, DEFER_RESUME, "bringingFoundTaskToFront"); - mMovedToFront = !isSplitScreenTopStack; + mMovedToFront = !wasTopOfVisibleRootTask; } else { intentTask.reparent(launchStack, ON_TOP, REPARENT_MOVE_ROOT_TASK_TO_FRONT, ANIMATE, DEFER_RESUME, "reparentToTargetStack"); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java index cde866b19df83..e8045e06d9fff 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java @@ -33,7 +33,6 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; -import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; import static android.content.Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; import static android.content.Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED; @@ -84,6 +83,7 @@ import android.os.Process; import android.os.RemoteException; import android.platform.test.annotations.Presubmit; import android.service.voice.IVoiceInteractionSession; +import android.util.Pair; import android.view.Gravity; import androidx.test.filters.SmallTest; @@ -434,17 +434,12 @@ public class ActivityStarterTests extends WindowTestsBase { final ActivityStarter starter = prepareStarter( FLAG_ACTIVITY_RESET_TASK_IF_NEEDED | FLAG_ACTIVITY_SINGLE_TOP, false /* mockGetLaunchStack */); - final ActivityRecord splitPrimaryFocusActivity = - new ActivityBuilder(mAtm).setCreateTask(true).build(); - final ActivityRecord splitSecondReusableActivity = - new ActivityBuilder(mAtm).setCreateTask(true).build(); - splitPrimaryFocusActivity.getRootTask() - .setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); - splitSecondReusableActivity.getRootTask() - .setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY); + final Pair activities = createActivitiesInSplit(); + final ActivityRecord splitPrimaryFocusActivity = activities.first; + final ActivityRecord splitSecondReusableActivity = activities.second; // Set focus back to primary. - splitPrimaryFocusActivity.getRootTask().moveToFront("testSplitScreenDeliverToTop"); + splitPrimaryFocusActivity.moveFocusableActivityToTop("testSplitScreenDeliverToTop"); // Start activity and delivered new intent. starter.getIntent().setComponent(splitSecondReusableActivity.mActivityComponent); @@ -463,24 +458,15 @@ public class ActivityStarterTests extends WindowTestsBase { public void testSplitScreenTaskToFront() { final ActivityStarter starter = prepareStarter( FLAG_ACTIVITY_RESET_TASK_IF_NEEDED | FLAG_ACTIVITY_SINGLE_TOP, false); - final ActivityRecord splitSecondReusableActivity = - new ActivityBuilder(mAtm).setCreateTask(true).build(); - final ActivityRecord splitSecondTopActivity = - new ActivityBuilder(mAtm).setCreateTask(true).build(); - final ActivityRecord splitPrimaryFocusActivity = - new ActivityBuilder(mAtm).setCreateTask(true).build(); - splitPrimaryFocusActivity.getRootTask() - .setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); - splitSecondReusableActivity.getRootTask() - .setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY); - splitSecondTopActivity.getRootTask() - .setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY); - - // Make it on top of split-screen-secondary. - splitSecondTopActivity.getRootTask().moveToFront("testSplitScreenTaskToFront"); + final Pair activities = createActivitiesInSplit(); + final ActivityRecord splitPrimaryFocusActivity = activities.first; + final ActivityRecord splitSecondReusableActivity = activities.second; + final ActivityRecord splitSecondTopActivity = new ActivityBuilder(mAtm).setCreateTask(true) + .setParentTask(splitSecondReusableActivity.getRootTask()).build(); + assertTrue(splitSecondTopActivity.inSplitScreenSecondaryWindowingMode()); // Let primary stack has focus. - splitPrimaryFocusActivity.getRootTask().moveToFront("testSplitScreenTaskToFront"); + splitPrimaryFocusActivity.moveFocusableActivityToTop("testSplitScreenTaskToFront"); // Start activity and delivered new intent. starter.getIntent().setComponent(splitSecondReusableActivity.mActivityComponent); @@ -491,6 +477,23 @@ public class ActivityStarterTests extends WindowTestsBase { assertEquals(START_TASK_TO_FRONT, result); } + /** Returns 2 activities. The first is in primary and the second is in secondary. */ + private Pair createActivitiesInSplit() { + final TestSplitOrganizer splitOrg = new TestSplitOrganizer(mAtm); + // The fullscreen windowing mode activity will be moved to split-secondary by + // TestSplitOrganizer when a split-primary task appears. + final ActivityRecord splitSecondActivity = + new ActivityBuilder(mAtm).setCreateTask(true).build(); + final ActivityRecord splitPrimaryActivity = new TaskBuilder(mSupervisor) + .setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY).setCreateActivity(true) + .build().getTopMostActivity(); + splitPrimaryActivity.mVisibleRequested = splitSecondActivity.mVisibleRequested = true; + + assertEquals(splitOrg.mPrimary, splitPrimaryActivity.getRootTask()); + assertEquals(splitOrg.mSecondary, splitSecondActivity.getRootTask()); + return Pair.create(splitPrimaryActivity, splitSecondActivity); + } + /** * Tests activity is cleaned up properly in a task mode violation. */ diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java index 0d31d3874785b..56c8bd12ac067 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java @@ -1045,6 +1045,9 @@ class WindowTestsBase extends SystemServiceTestsBase { spyOn(task); task.mUserId = mUserId; Task rootTask = task.getRootTask(); + if (task != rootTask && !Mockito.mockingDetails(rootTask).isSpy()) { + spyOn(rootTask); + } doNothing().when(rootTask).startActivityLocked( any(), any(), anyBoolean(), anyBoolean(), any());