Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 89bda313 authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge "Sort running task by focus and visibility"

parents b754daea 76df3f45
Loading
Loading
Loading
Loading
+73 −38
Original line number Diff line number Diff line
@@ -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<Task> 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<Task> mTmpSortedSet = new TreeSet<>(LAST_ACTIVE_TIME_COMPARATOR);
    // Tasks are sorted in order {focusedVisibleTasks, visibleTasks, invisibleTasks}.
    private final ArrayList<Task> mTmpSortedTasks = new ArrayList<>();
    // mTmpVisibleTasks, mTmpInvisibleTasks and mTmpFocusedTasks are sorted from top
    // to bottom.
    private final ArrayList<Task> mTmpVisibleTasks = new ArrayList<>();
    private final ArrayList<Task> mTmpInvisibleTasks = new ArrayList<>();
    private final ArrayList<Task> 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<Task> iter = mTmpSortedSet.iterator();
        while (iter.hasNext()) {
            if (maxNum == 0) {
                break;
        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));
        }

            final Task task = iter.next();
            list.add(createRunningTaskInfo(task));
            maxNum--;
        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;
+34 −66
Original line number Diff line number Diff line
@@ -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<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
    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<Task> 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();

        final Task visibleTaskTop = tasks.get(numTasks - 2);
        doReturn(true).when(visibleTaskTop).isVisible();

        // Ensure that the last active time of visible tasks were updated while the focused one had
        // the largest last active time.
        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<RunningTaskInfo> 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"))