From 76df3f45740e10f78aac3d406ff9b81cef6249a8 Mon Sep 17 00:00:00 2001 From: Bowen Li Date: Thu, 20 Oct 2022 13:06:19 +0800 Subject: [PATCH] Sort running task by focus and visibility Currently the order of visible tasks we get is opposite to that in the system. This CL is to ensure that the visible focused task is on top, visible tasks below, then invisible tasks. And keep the order within the acquired focus, visible and invisible tasks groups consistent with the system instead of sort by lastActiveTime. If there are multiple DisplayContents, getTasks method will return: [{VisibleFocusTask for DisplayTop, VisibleFocusTask for DisplayBottom}, {VisbleTask0...n1 for DisplayTop, VisbleTask0...n2 for DisplayBottom}, {InvisbleTask0...n3 for DisplayTop, InvisbleTask0...n4 for DisplayBottom}] in the same result List. Bug: 255255158 Signed-off-by: Bowen Li Merged-In: I7f7d5e72fbf73988b58c23f8a11dff05dbc1b3f1 Change-Id: I7f7d5e72fbf73988b58c23f8a11dff05dbc1b3f1 --- .../com/android/server/wm/RunningTasks.java | 113 ++++++++++++------ .../android/server/wm/RunningTasksTest.java | 100 ++++++---------- 2 files changed, 108 insertions(+), 105 deletions(-) diff --git a/services/core/java/com/android/server/wm/RunningTasks.java b/services/core/java/com/android/server/wm/RunningTasks.java index 1ec191ed7c058..e14bbbd78ded8 100644 --- a/services/core/java/com/android/server/wm/RunningTasks.java +++ b/services/core/java/com/android/server/wm/RunningTasks.java @@ -20,16 +20,15 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; import android.app.ActivityManager.RunningTaskInfo; +import android.os.SystemClock; import android.os.UserHandle; import android.util.ArraySet; import com.android.internal.util.function.pooled.PooledConsumer; import com.android.internal.util.function.pooled.PooledLambda; -import java.util.Comparator; -import java.util.Iterator; +import java.util.ArrayList; import java.util.List; -import java.util.TreeSet; /** * Class for resolving the set of running tasks in the system. @@ -41,15 +40,13 @@ class RunningTasks { static final int FLAG_CROSS_USERS = 1 << 2; static final int FLAG_KEEP_INTENT_EXTRA = 1 << 3; - // Comparator to sort by last active time (descending) - private static final Comparator LAST_ACTIVE_TIME_COMPARATOR = - (o1, o2) -> { - return o1.lastActiveTime == o2.lastActiveTime - ? Integer.signum(o2.mTaskId - o1.mTaskId) : - Long.signum(o2.lastActiveTime - o1.lastActiveTime); - }; - - private final TreeSet mTmpSortedSet = new TreeSet<>(LAST_ACTIVE_TIME_COMPARATOR); + // Tasks are sorted in order {focusedVisibleTasks, visibleTasks, invisibleTasks}. + private final ArrayList mTmpSortedTasks = new ArrayList<>(); + // mTmpVisibleTasks, mTmpInvisibleTasks and mTmpFocusedTasks are sorted from top + // to bottom. + private final ArrayList mTmpVisibleTasks = new ArrayList<>(); + private final ArrayList mTmpInvisibleTasks = new ArrayList<>(); + private final ArrayList mTmpFocusedTasks = new ArrayList<>(); private int mCallingUid; private int mUserId; @@ -67,8 +64,6 @@ class RunningTasks { return; } - // Gather all of the tasks across all of the tasks, and add them to the sorted set - mTmpSortedSet.clear(); mCallingUid = callingUid; mUserId = UserHandle.getUserId(callingUid); mCrossUser = (flags & FLAG_CROSS_USERS) == FLAG_CROSS_USERS; @@ -79,22 +74,67 @@ class RunningTasks { mRecentTasks = root.mService.getRecentTasks(); mKeepIntentExtra = (flags & FLAG_KEEP_INTENT_EXTRA) == FLAG_KEEP_INTENT_EXTRA; - final PooledConsumer c = PooledLambda.obtainConsumer(RunningTasks::processTask, this, - PooledLambda.__(Task.class)); - root.forAllLeafTasks(c, false); - c.recycle(); + if (root instanceof RootWindowContainer) { + ((RootWindowContainer) root).forAllDisplays(dc -> { + final Task focusedTask = dc.mFocusedApp != null ? dc.mFocusedApp.getTask() : null; + if (focusedTask != null) { + mTmpFocusedTasks.add(focusedTask); + } + processTaskInWindowContainer(dc); + }); + } else { + final DisplayContent dc = root.getDisplayContent(); + final Task focusedTask = dc != null + ? (dc.mFocusedApp != null ? dc.mFocusedApp.getTask() : null) + : null; + // May not be include focusedTask if root is DisplayArea. + final boolean rootContainsFocusedTask = focusedTask != null + && focusedTask.isDescendantOf(root); + if (rootContainsFocusedTask) { + mTmpFocusedTasks.add(focusedTask); + } + processTaskInWindowContainer(root); + } + + final int visibleTaskCount = mTmpVisibleTasks.size(); + for (int i = 0; i < mTmpFocusedTasks.size(); i++) { + final Task focusedTask = mTmpFocusedTasks.get(i); + final boolean containsFocusedTask = mTmpVisibleTasks.remove(focusedTask); + if (containsFocusedTask) { + // Put the visible focused task at the first position. + mTmpSortedTasks.add(focusedTask); + } + } + if (!mTmpVisibleTasks.isEmpty()) { + mTmpSortedTasks.addAll(mTmpVisibleTasks); + } + if (!mTmpInvisibleTasks.isEmpty()) { + mTmpSortedTasks.addAll(mTmpInvisibleTasks); + } // Take the first {@param maxNum} tasks and create running task infos for them - final Iterator iter = mTmpSortedSet.iterator(); - while (iter.hasNext()) { - if (maxNum == 0) { - break; - } - - final Task task = iter.next(); - list.add(createRunningTaskInfo(task)); - maxNum--; + final int size = Math.min(maxNum, mTmpSortedTasks.size()); + final long now = SystemClock.elapsedRealtime(); + for (int i = 0; i < size; i++) { + final Task task = mTmpSortedTasks.get(i); + // Override the last active to current time for the visible tasks because the visible + // tasks can be considered to be currently active, the values are descending as + // the item order. + final long visibleActiveTime = i < visibleTaskCount ? now + size - i : -1; + list.add(createRunningTaskInfo(task, visibleActiveTime)); } + + mTmpFocusedTasks.clear(); + mTmpVisibleTasks.clear(); + mTmpInvisibleTasks.clear(); + mTmpSortedTasks.clear(); + } + + private void processTaskInWindowContainer(WindowContainer wc) { + final PooledConsumer c = PooledLambda.obtainConsumer(RunningTasks::processTask, this, + PooledLambda.__(Task.class)); + wc.forAllLeafTasks(c, true); + c.recycle(); } private void processTask(Task task) { @@ -121,25 +161,20 @@ class RunningTasks { // home & recent tasks return; } - if (task.isVisible()) { - // For the visible task, update the last active time so that it can be used to determine - // the order of the tasks (it may not be set for newly created tasks) - task.touchActiveTime(); - if (!task.isFocused()) { - // TreeSet doesn't allow the same value and make sure this task is lower than the - // focused one. - task.lastActiveTime -= mTmpSortedSet.size(); - } + mTmpVisibleTasks.add(task); + } else { + mTmpInvisibleTasks.add(task); } - - mTmpSortedSet.add(task); } /** Constructs a {@link RunningTaskInfo} from a given {@param task}. */ - private RunningTaskInfo createRunningTaskInfo(Task task) { + private RunningTaskInfo createRunningTaskInfo(Task task, long visibleActiveTime) { final RunningTaskInfo rti = new RunningTaskInfo(); task.fillTaskInfo(rti, !mKeepIntentExtra); + if (visibleActiveTime > 0) { + rti.lastActiveTime = visibleActiveTime; + } // Fill in some deprecated values rti.id = rti.taskId; return rti; diff --git a/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java index 33b236669ec77..13fc61cbf2faf 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java @@ -60,55 +60,6 @@ public class RunningTasksTest extends WindowTestsBase { mRunningTasks = new RunningTasks(); } - @Test - public void testCollectTasksByLastActiveTime() { - // Create a number of stacks with tasks (of incrementing active time) - final ArrayList displays = new ArrayList<>(); - final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1000, 2500).build(); - displays.add(display); - - final int numStacks = 2; - for (int stackIndex = 0; stackIndex < numStacks; stackIndex++) { - final Task stack = new TaskBuilder(mSupervisor) - .setDisplay(display) - .setOnTop(false) - .build(); - } - - final int numTasks = 10; - int activeTime = 0; - final List rootTasks = new ArrayList<>(); - display.getDefaultTaskDisplayArea().forAllRootTasks(task -> { - rootTasks.add(task); - }, false /* traverseTopToBottom */); - for (int i = 0; i < numTasks; i++) { - final Task task = - createTask(rootTasks.get(i % numStacks), ".Task" + i, i, activeTime++, null); - doReturn(false).when(task).isVisible(); - } - - // Ensure that the latest tasks were returned in order of decreasing last active time, - // collected from all tasks across all the stacks - final int numFetchTasks = 5; - ArrayList tasks = new ArrayList<>(); - mRunningTasks.getTasks(5, tasks, FLAG_ALLOWED | FLAG_CROSS_USERS, mRootWindowContainer, - -1 /* callingUid */, PROFILE_IDS); - assertThat(tasks).hasSize(numFetchTasks); - for (int i = 0; i < numFetchTasks; i++) { - assertEquals(numTasks - i - 1, tasks.get(i).id); - } - - // Ensure that requesting more than the total number of tasks only returns the subset - // and does not crash - tasks.clear(); - mRunningTasks.getTasks(100, tasks, FLAG_ALLOWED | FLAG_CROSS_USERS, - mRootWindowContainer, -1 /* callingUid */, PROFILE_IDS); - assertThat(tasks).hasSize(numTasks); - for (int i = 0; i < numTasks; i++) { - assertEquals(numTasks - i - 1, tasks.get(i).id); - } - } - @Test public void testTaskInfo_expectNoExtrasByDefault() { final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1000, 2500).build(); @@ -120,7 +71,7 @@ public class RunningTasksTest extends WindowTestsBase { .build(); final Bundle data = new Bundle(); data.putInt("key", 100); - createTask(stack, ".Task" + i, i, i, data); + createTask(stack, ".Task" + i, i, data); } final int numFetchTasks = 5; @@ -145,7 +96,7 @@ public class RunningTasksTest extends WindowTestsBase { .build(); final Bundle data = new Bundle(); data.putInt("key", 100); - createTask(stack, ".Task" + i, i, i, data); + createTask(stack, ".Task" + i, i, data); } final int numFetchTasks = 5; @@ -162,46 +113,63 @@ public class RunningTasksTest extends WindowTestsBase { } @Test - public void testUpdateLastActiveTimeOfVisibleTasks() { + public void testGetTasksSortByFocusAndVisibility() { final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1000, 2500).build(); + final Task stack = new TaskBuilder(mSupervisor) + .setDisplay(display) + .setOnTop(true) + .build(); + final int numTasks = 10; final ArrayList tasks = new ArrayList<>(); for (int i = 0; i < numTasks; i++) { - final Task task = createTask(null, ".Task" + i, i, i, null); + final Task task = createTask(stack, ".Task" + i, i, null); doReturn(false).when(task).isVisible(); tasks.add(task); } - final Task visibleTask = tasks.get(0); - doReturn(true).when(visibleTask).isVisible(); - - final Task focusedTask = tasks.get(1); + final Task focusedTask = tasks.get(numTasks - 1); doReturn(true).when(focusedTask).isVisible(); - doReturn(true).when(focusedTask).isFocused(); + display.mFocusedApp = focusedTask.getTopNonFinishingActivity(); - // Ensure that the last active time of visible tasks were updated while the focused one had - // the largest last active time. + final Task visibleTaskTop = tasks.get(numTasks - 2); + doReturn(true).when(visibleTaskTop).isVisible(); + + final Task visibleTaskBottom = tasks.get(numTasks - 3); + doReturn(true).when(visibleTaskBottom).isVisible(); + + // Ensure that the focused Task is on top, visible tasks below, then invisible tasks. final int numFetchTasks = 5; final ArrayList fetchTasks = new ArrayList<>(); mRunningTasks.getTasks(numFetchTasks, fetchTasks, FLAG_ALLOWED | FLAG_CROSS_USERS | FLAG_KEEP_INTENT_EXTRA, mRootWindowContainer, -1 /* callingUid */, PROFILE_IDS); assertThat(fetchTasks).hasSize(numFetchTasks); - assertEquals(fetchTasks.get(0).id, focusedTask.mTaskId); - assertEquals(fetchTasks.get(1).id, visibleTask.mTaskId); + for (int i = 0; i < numFetchTasks; i++) { + assertEquals(numTasks - i - 1, fetchTasks.get(i).id); + } + + // Ensure that requesting more than the total number of tasks only returns the subset + // and does not crash + fetchTasks.clear(); + mRunningTasks.getTasks(100, fetchTasks, + FLAG_ALLOWED | FLAG_CROSS_USERS | FLAG_KEEP_INTENT_EXTRA, mRootWindowContainer, + -1 /* callingUid */, PROFILE_IDS); + assertThat(fetchTasks).hasSize(numTasks); + for (int i = 0; i < numTasks; i++) { + assertEquals(numTasks - i - 1, fetchTasks.get(i).id); + } } /** - * Create a task with a single activity in it, with the given last active time. + * Create a task with a single activity in it. */ - private Task createTask(Task stack, String className, int taskId, - int lastActiveTime, Bundle extras) { + private Task createTask(Task stack, String className, int taskId, Bundle extras) { final Task task = new TaskBuilder(mAtm.mTaskSupervisor) .setComponent(new ComponentName(mContext.getPackageName(), className)) .setTaskId(taskId) .setParentTaskFragment(stack) .build(); - task.lastActiveTime = lastActiveTime; final ActivityRecord activity = new ActivityBuilder(mAtm) .setTask(task) .setComponent(new ComponentName(mContext.getPackageName(), ".TaskActivity"))