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 <libowen1@xiaomi.corp-partner.google.com>
Merged-In: I7f7d5e72fbf73988b58c23f8a11dff05dbc1b3f1
Change-Id: I7f7d5e72fbf73988b58c23f8a11dff05dbc1b3f1
This commit is contained in:
Bowen Li
2022-10-20 13:06:19 +08:00
parent 79605eb994
commit 76df3f4574
2 changed files with 108 additions and 105 deletions

View File

@@ -20,16 +20,15 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import android.app.ActivityManager.RunningTaskInfo; import android.app.ActivityManager.RunningTaskInfo;
import android.os.SystemClock;
import android.os.UserHandle; import android.os.UserHandle;
import android.util.ArraySet; import android.util.ArraySet;
import com.android.internal.util.function.pooled.PooledConsumer; import com.android.internal.util.function.pooled.PooledConsumer;
import com.android.internal.util.function.pooled.PooledLambda; import com.android.internal.util.function.pooled.PooledLambda;
import java.util.Comparator; import java.util.ArrayList;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.TreeSet;
/** /**
* Class for resolving the set of running tasks in the system. * 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_CROSS_USERS = 1 << 2;
static final int FLAG_KEEP_INTENT_EXTRA = 1 << 3; static final int FLAG_KEEP_INTENT_EXTRA = 1 << 3;
// Comparator to sort by last active time (descending) // Tasks are sorted in order {focusedVisibleTasks, visibleTasks, invisibleTasks}.
private static final Comparator<Task> LAST_ACTIVE_TIME_COMPARATOR = private final ArrayList<Task> mTmpSortedTasks = new ArrayList<>();
(o1, o2) -> { // mTmpVisibleTasks, mTmpInvisibleTasks and mTmpFocusedTasks are sorted from top
return o1.lastActiveTime == o2.lastActiveTime // to bottom.
? Integer.signum(o2.mTaskId - o1.mTaskId) : private final ArrayList<Task> mTmpVisibleTasks = new ArrayList<>();
Long.signum(o2.lastActiveTime - o1.lastActiveTime); private final ArrayList<Task> mTmpInvisibleTasks = new ArrayList<>();
}; private final ArrayList<Task> mTmpFocusedTasks = new ArrayList<>();
private final TreeSet<Task> mTmpSortedSet = new TreeSet<>(LAST_ACTIVE_TIME_COMPARATOR);
private int mCallingUid; private int mCallingUid;
private int mUserId; private int mUserId;
@@ -67,8 +64,6 @@ class RunningTasks {
return; return;
} }
// Gather all of the tasks across all of the tasks, and add them to the sorted set
mTmpSortedSet.clear();
mCallingUid = callingUid; mCallingUid = callingUid;
mUserId = UserHandle.getUserId(callingUid); mUserId = UserHandle.getUserId(callingUid);
mCrossUser = (flags & FLAG_CROSS_USERS) == FLAG_CROSS_USERS; mCrossUser = (flags & FLAG_CROSS_USERS) == FLAG_CROSS_USERS;
@@ -79,22 +74,67 @@ class RunningTasks {
mRecentTasks = root.mService.getRecentTasks(); mRecentTasks = root.mService.getRecentTasks();
mKeepIntentExtra = (flags & FLAG_KEEP_INTENT_EXTRA) == FLAG_KEEP_INTENT_EXTRA; mKeepIntentExtra = (flags & FLAG_KEEP_INTENT_EXTRA) == FLAG_KEEP_INTENT_EXTRA;
final PooledConsumer c = PooledLambda.obtainConsumer(RunningTasks::processTask, this, if (root instanceof RootWindowContainer) {
PooledLambda.__(Task.class)); ((RootWindowContainer) root).forAllDisplays(dc -> {
root.forAllLeafTasks(c, false); final Task focusedTask = dc.mFocusedApp != null ? dc.mFocusedApp.getTask() : null;
c.recycle(); 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 // Take the first {@param maxNum} tasks and create running task infos for them
final Iterator<Task> iter = mTmpSortedSet.iterator(); final int size = Math.min(maxNum, mTmpSortedTasks.size());
while (iter.hasNext()) { final long now = SystemClock.elapsedRealtime();
if (maxNum == 0) { for (int i = 0; i < size; i++) {
break; 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
final Task task = iter.next(); // the item order.
list.add(createRunningTaskInfo(task)); final long visibleActiveTime = i < visibleTaskCount ? now + size - i : -1;
maxNum--; 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) { private void processTask(Task task) {
@@ -121,25 +161,20 @@ class RunningTasks {
// home & recent tasks // home & recent tasks
return; return;
} }
if (task.isVisible()) { if (task.isVisible()) {
// For the visible task, update the last active time so that it can be used to determine mTmpVisibleTasks.add(task);
// the order of the tasks (it may not be set for newly created tasks) } else {
task.touchActiveTime(); mTmpInvisibleTasks.add(task);
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();
}
} }
mTmpSortedSet.add(task);
} }
/** Constructs a {@link RunningTaskInfo} from a given {@param 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(); final RunningTaskInfo rti = new RunningTaskInfo();
task.fillTaskInfo(rti, !mKeepIntentExtra); task.fillTaskInfo(rti, !mKeepIntentExtra);
if (visibleActiveTime > 0) {
rti.lastActiveTime = visibleActiveTime;
}
// Fill in some deprecated values // Fill in some deprecated values
rti.id = rti.taskId; rti.id = rti.taskId;
return rti; return rti;

View File

@@ -60,55 +60,6 @@ public class RunningTasksTest extends WindowTestsBase {
mRunningTasks = new RunningTasks(); mRunningTasks = new RunningTasks();
} }
@Test
public void testCollectTasksByLastActiveTime() {
// Create a number of stacks with tasks (of incrementing active time)
final ArrayList<DisplayContent> 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<Task> 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<RunningTaskInfo> 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 @Test
public void testTaskInfo_expectNoExtrasByDefault() { public void testTaskInfo_expectNoExtrasByDefault() {
final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1000, 2500).build(); final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1000, 2500).build();
@@ -120,7 +71,7 @@ public class RunningTasksTest extends WindowTestsBase {
.build(); .build();
final Bundle data = new Bundle(); final Bundle data = new Bundle();
data.putInt("key", 100); data.putInt("key", 100);
createTask(stack, ".Task" + i, i, i, data); createTask(stack, ".Task" + i, i, data);
} }
final int numFetchTasks = 5; final int numFetchTasks = 5;
@@ -145,7 +96,7 @@ public class RunningTasksTest extends WindowTestsBase {
.build(); .build();
final Bundle data = new Bundle(); final Bundle data = new Bundle();
data.putInt("key", 100); data.putInt("key", 100);
createTask(stack, ".Task" + i, i, i, data); createTask(stack, ".Task" + i, i, data);
} }
final int numFetchTasks = 5; final int numFetchTasks = 5;
@@ -162,46 +113,63 @@ public class RunningTasksTest extends WindowTestsBase {
} }
@Test @Test
public void testUpdateLastActiveTimeOfVisibleTasks() { public void testGetTasksSortByFocusAndVisibility() {
final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1000, 2500).build(); 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 int numTasks = 10;
final ArrayList<Task> tasks = new ArrayList<>(); final ArrayList<Task> tasks = new ArrayList<>();
for (int i = 0; i < numTasks; i++) { 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(); doReturn(false).when(task).isVisible();
tasks.add(task); tasks.add(task);
} }
final Task visibleTask = tasks.get(0); final Task focusedTask = tasks.get(numTasks - 1);
doReturn(true).when(visibleTask).isVisible();
final Task focusedTask = tasks.get(1);
doReturn(true).when(focusedTask).isVisible(); 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 final Task visibleTaskTop = tasks.get(numTasks - 2);
// the largest last active time. 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 int numFetchTasks = 5;
final ArrayList<RunningTaskInfo> fetchTasks = new ArrayList<>(); final ArrayList<RunningTaskInfo> fetchTasks = new ArrayList<>();
mRunningTasks.getTasks(numFetchTasks, fetchTasks, mRunningTasks.getTasks(numFetchTasks, fetchTasks,
FLAG_ALLOWED | FLAG_CROSS_USERS | FLAG_KEEP_INTENT_EXTRA, mRootWindowContainer, FLAG_ALLOWED | FLAG_CROSS_USERS | FLAG_KEEP_INTENT_EXTRA, mRootWindowContainer,
-1 /* callingUid */, PROFILE_IDS); -1 /* callingUid */, PROFILE_IDS);
assertThat(fetchTasks).hasSize(numFetchTasks); assertThat(fetchTasks).hasSize(numFetchTasks);
assertEquals(fetchTasks.get(0).id, focusedTask.mTaskId); for (int i = 0; i < numFetchTasks; i++) {
assertEquals(fetchTasks.get(1).id, visibleTask.mTaskId); 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, private Task createTask(Task stack, String className, int taskId, Bundle extras) {
int lastActiveTime, Bundle extras) {
final Task task = new TaskBuilder(mAtm.mTaskSupervisor) final Task task = new TaskBuilder(mAtm.mTaskSupervisor)
.setComponent(new ComponentName(mContext.getPackageName(), className)) .setComponent(new ComponentName(mContext.getPackageName(), className))
.setTaskId(taskId) .setTaskId(taskId)
.setParentTaskFragment(stack) .setParentTaskFragment(stack)
.build(); .build();
task.lastActiveTime = lastActiveTime;
final ActivityRecord activity = new ActivityBuilder(mAtm) final ActivityRecord activity = new ActivityBuilder(mAtm)
.setTask(task) .setTask(task)
.setComponent(new ComponentName(mContext.getPackageName(), ".TaskActivity")) .setComponent(new ComponentName(mContext.getPackageName(), ".TaskActivity"))