From 27f81888dd5f253e6735d4cd5b5c2996690291e6 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Thu, 19 Apr 2018 14:45:03 -0700 Subject: [PATCH] Fix issue with recent task being replaced by task with another windowing mode - ag/3762067 is not sufficient to fix the regression from stack id to activity type and windowing mode. When finding a task in the recents list to remove and replace with a new task, we need to treat tasks with different windowing modes as separate tasks. Bug: 78291664 Test: Launch play movies, enter PIP and ensure that the main activity task still shows in overview Change-Id: Id1fddfe580bd6bfab8e618930cec82e1e4363ce0 --- .../com/android/server/am/RecentTasks.java | 30 ++++++++- .../android/server/am/RecentTasksTest.java | 62 +++++++++++++++++++ 2 files changed, 91 insertions(+), 1 deletion(-) diff --git a/services/core/java/com/android/server/am/RecentTasks.java b/services/core/java/com/android/server/am/RecentTasks.java index a20452bb50746..644e365f1e4bc 100644 --- a/services/core/java/com/android/server/am/RecentTasks.java +++ b/services/core/java/com/android/server/am/RecentTasks.java @@ -22,8 +22,10 @@ import static android.app.ActivityManager.RECENT_WITH_EXCLUDED; import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; 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_UNDEFINED; import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK; import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; @@ -1255,7 +1257,8 @@ class RecentTasks { for (int i = 0; i < recentsCount; i++) { final TaskRecord tr = mTasks.get(i); if (task != tr) { - if (!task.hasCompatibleActivityType(tr) || task.userId != tr.userId) { + if (!hasCompatibleActivityTypeAndWindowingMode(task, tr) + || task.userId != tr.userId) { continue; } final Intent trIntent = tr.intent; @@ -1547,4 +1550,29 @@ class RecentTasks { return rti; } + + /** + * @return Whether the activity types and windowing modes of the two tasks are considered + * compatible. This is necessary because we currently don't persist the activity type + * or the windowing mode with the task, so they can be undefined when restored. + */ + private boolean hasCompatibleActivityTypeAndWindowingMode(TaskRecord t1, TaskRecord t2) { + final int activityType = t1.getActivityType(); + final int windowingMode = t1.getWindowingMode(); + final boolean isUndefinedType = activityType == ACTIVITY_TYPE_UNDEFINED; + final boolean isUndefinedMode = windowingMode == WINDOWING_MODE_UNDEFINED; + final int otherActivityType = t2.getActivityType(); + final int otherWindowingMode = t2.getWindowingMode(); + final boolean isOtherUndefinedType = otherActivityType == ACTIVITY_TYPE_UNDEFINED; + final boolean isOtherUndefinedMode = otherWindowingMode == WINDOWING_MODE_UNDEFINED; + + // An activity type and windowing mode is compatible if they are the exact same type/mode, + // or if one of the type/modes is undefined + final boolean isCompatibleType = activityType == otherActivityType + || isUndefinedType || isOtherUndefinedType; + final boolean isCompatibleMode = windowingMode == otherWindowingMode + || isUndefinedMode || isOtherUndefinedMode; + + return isCompatibleType && isCompatibleMode; + } } diff --git a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java index 592f7b16470f5..b73ac8934cb06 100644 --- a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java +++ b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java @@ -21,6 +21,7 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS; import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK; @@ -303,6 +304,8 @@ public class RecentTasksTest extends ActivityTestsBase { @Test public void testAddTaskCompatibleActivityType_expectRemove() throws Exception { + // Test with undefined activity type since the type is not persisted by the task persister + // and we want to ensure that a new task will match a restored task Configuration config1 = new Configuration(); config1.windowConfiguration.setActivityType(ACTIVITY_TYPE_UNDEFINED); TaskRecord task1 = createTaskBuilder(".Task1") @@ -354,6 +357,65 @@ public class RecentTasksTest extends ActivityTestsBase { assertTrue(mCallbacksRecorder.removed.isEmpty()); } + @Test + public void testAddTaskCompatibleWindowingMode_expectRemove() throws Exception { + Configuration config1 = new Configuration(); + config1.windowConfiguration.setWindowingMode(WINDOWING_MODE_UNDEFINED); + TaskRecord task1 = createTaskBuilder(".Task1") + .setFlags(FLAG_ACTIVITY_NEW_TASK) + .setStack(mStack) + .build(); + task1.onConfigurationChanged(config1); + assertTrue(task1.getWindowingMode() == WINDOWING_MODE_UNDEFINED); + mRecentTasks.add(task1); + mCallbacksRecorder.clear(); + + Configuration config2 = new Configuration(); + config2.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN); + TaskRecord task2 = createTaskBuilder(".Task1") + .setFlags(FLAG_ACTIVITY_NEW_TASK) + .setStack(mStack) + .build(); + task2.onConfigurationChanged(config2); + assertTrue(task2.getWindowingMode() == WINDOWING_MODE_FULLSCREEN); + mRecentTasks.add(task2); + + assertTrue(mCallbacksRecorder.added.size() == 1); + assertTrue(mCallbacksRecorder.added.contains(task2)); + assertTrue(mCallbacksRecorder.trimmed.isEmpty()); + assertTrue(mCallbacksRecorder.removed.size() == 1); + assertTrue(mCallbacksRecorder.removed.contains(task1)); + } + + @Test + public void testAddTaskIncompatibleWindowingMode_expectNoRemove() throws Exception { + Configuration config1 = new Configuration(); + config1.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN); + TaskRecord task1 = createTaskBuilder(".Task1") + .setFlags(FLAG_ACTIVITY_NEW_TASK) + .setStack(mStack) + .build(); + task1.onConfigurationChanged(config1); + assertTrue(task1.getWindowingMode() == WINDOWING_MODE_FULLSCREEN); + mRecentTasks.add(task1); + + Configuration config2 = new Configuration(); + config2.windowConfiguration.setWindowingMode(WINDOWING_MODE_PINNED); + TaskRecord task2 = createTaskBuilder(".Task1") + .setFlags(FLAG_ACTIVITY_NEW_TASK) + .setStack(mStack) + .build(); + task2.onConfigurationChanged(config2); + assertTrue(task2.getWindowingMode() == WINDOWING_MODE_PINNED); + mRecentTasks.add(task2); + + assertTrue(mCallbacksRecorder.added.size() == 2); + assertTrue(mCallbacksRecorder.added.contains(task1)); + assertTrue(mCallbacksRecorder.added.contains(task2)); + assertTrue(mCallbacksRecorder.trimmed.isEmpty()); + assertTrue(mCallbacksRecorder.removed.isEmpty()); + } + @Test public void testUsersTasks() throws Exception { mRecentTasks.setOnlyTestVisibleRange();