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

Commit 9cf5a93c authored by Winson Chung's avatar Winson Chung
Browse files

4a/ Track visible running tasks via transitions observer

- When enable_shell_top_task_tracking is enabled, use the existing
  task transition observer (until we have the generalized task repo)
  to track the set of visible tasks for dispatching to Launcher.
  In addition, update the paths to compose GroupedTaskInfos instead
  of raw RunningTaskInfos so that we can later coalesce tasks that
  should be bundled together for presentation in Launcher (or whose
  joint states are required by launcher).

Bug: 346588978
Flag: EXEMPT adding new flag enable_shell_top_task_tracking
Test: Build SystemUI & Launcher
Test: atest WMShellUnitTests
Change-Id: I12bb44ff8ccc07709cb3a2aa44e853fe0cc7ea45
parent 859bdacb
Loading
Loading
Loading
Loading
+18 −0
Original line number Diff line number Diff line
@@ -167,6 +167,16 @@ public class GroupedTaskInfo implements Parcelable {
        return null;
    }

    /**
     * @return The task info for the task in this group with the given {@code taskId}.
     */
    @Nullable
    public TaskInfo getTaskById(int taskId) {
        return mTasks.stream()
                .filter(task -> task.taskId == taskId)
                .findFirst().orElse(null);
    }

    /**
     * Get all {@link RecentTaskInfo}s grouped together.
     */
@@ -175,6 +185,14 @@ public class GroupedTaskInfo implements Parcelable {
        return mTasks;
    }

    /**
     * @return Whether this grouped task contains a task with the given {@code taskId}.
     */
    public boolean containsTask(int taskId) {
        return mTasks.stream()
                .anyMatch((task -> task.taskId == taskId));
    }

    /**
     * Return {@link SplitBounds} if this is a split screen entry or {@code null}
     */
+6 −3
Original line number Diff line number Diff line
@@ -1024,10 +1024,13 @@ public abstract class WMShellBaseModule {
    @WMSingleton
    @Provides
    static TaskStackTransitionObserver provideTaskStackTransitionObserver(
            Lazy<Transitions> transitions,
            ShellInit shellInit
            ShellInit shellInit,
            Lazy<ShellTaskOrganizer> shellTaskOrganizer,
            ShellCommandHandler shellCommandHandler,
            Lazy<Transitions> transitions
    ) {
        return new TaskStackTransitionObserver(transitions, shellInit);
        return new TaskStackTransitionObserver(shellInit, shellTaskOrganizer, shellCommandHandler,
                transitions);
    }

    //
+2 −0
Original line number Diff line number Diff line
@@ -46,6 +46,8 @@ public enum ShellProtoLogGroup implements IProtoLogGroup {
            "ShellBackPreview"),
    WM_SHELL_RECENT_TASKS(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
            Consts.TAG_WM_SHELL),
    WM_SHELL_TASK_OBSERVER(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true,
            Consts.TAG_WM_SHELL),
    // TODO(b/282232877): turn logToLogcat to false.
    WM_SHELL_PICTURE_IN_PICTURE(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true,
            Consts.TAG_WM_SHELL),
+9 −3
Original line number Diff line number Diff line
@@ -45,9 +45,15 @@ oneway interface IRecentTasksListener {
     */
    void onRunningTaskChanged(in RunningTaskInfo taskInfo);

    /** A task has moved to front. */
    void onTaskMovedToFront(in GroupedTaskInfo[] visibleTasks);
    /** A task has moved to front. Only used if enableShellTopTaskTracking() is disabled. */
    void onTaskMovedToFront(in GroupedTaskInfo taskToFront);

    /** A task info has changed. */
    /** A task info has changed. Only used if enableShellTopTaskTracking() is disabled. */
    void onTaskInfoChanged(in RunningTaskInfo taskInfo);

    /**
     * If enableShellTopTaskTracking() is enabled, this reports the set of all visible tasks.
     * Otherwise, this reports only the new top most visible task.
     */
    void onVisibleTasksChanged(in GroupedTaskInfo[] visibleTasks);
}
 No newline at end of file
+122 −65
Original line number Diff line number Diff line
@@ -17,14 +17,18 @@
package com.android.wm.shell.recents;

import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.content.pm.PackageManager.FEATURE_PC;

import static com.android.wm.shell.Flags.enableShellTopTaskTracking;
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_OBSERVER;
import static com.android.wm.shell.shared.ShellSharedConstants.KEY_EXTRA_SHELL_RECENT_TASKS;

import android.Manifest;
import android.annotation.RequiresPermission;
import android.app.ActivityManager;
import android.app.ActivityManager.RecentTaskInfo;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityTaskManager;
import android.app.IApplicationThread;
import android.app.KeyguardManager;
@@ -65,7 +69,6 @@ import com.android.wm.shell.shared.split.SplitBounds;
import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;

import java.io.PrintWriter;
import java.util.ArrayList;
@@ -110,6 +113,11 @@ public class RecentTasksController implements TaskStackListenerCallback,
     */
    private final Map<Integer, SplitBounds> mTaskSplitBoundsMap = new HashMap<>();

    /**
     * Cached list of the visible tasks, sorted from top most to bottom most.
     */
    private final List<RunningTaskInfo> mVisibleTasks = new ArrayList<>();

    /**
     * Creates {@link RecentTasksController}, returns {@code null} if the feature is not
     * supported.
@@ -170,10 +178,8 @@ public class RecentTasksController implements TaskStackListenerCallback,
        mShellCommandHandler.addDumpCallback(this::dump, this);
        mTaskStackListener.addListener(this);
        mDesktopRepository.ifPresent(it -> it.addActiveTaskListener(this));
        if (Transitions.ENABLE_SHELL_TRANSITIONS) {
        mTaskStackTransitionObserver.addTaskStackTransitionObserverListener(this,
                mMainExecutor);
        }
        mContext.getSystemService(KeyguardManager.class).addKeyguardLockedStateListener(
                mMainExecutor, isKeyguardLocked -> notifyRecentTasksChanged());
    }
@@ -249,8 +255,11 @@ public class RecentTasksController implements TaskStackListenerCallback,

    @Override
    public void onTaskStackChanged() {
        if (!enableShellTopTaskTracking()) {
            // Skip notifying recent tasks changed whenever task stack changes
            notifyRecentTasksChanged();
        }
    }

    @Override
    public void onRecentTaskListUpdated() {
@@ -263,15 +272,18 @@ public class RecentTasksController implements TaskStackListenerCallback,
        notifyRecentTasksChanged();
    }

    public void onTaskAdded(ActivityManager.RunningTaskInfo taskInfo) {
    public void onTaskAdded(RunningTaskInfo taskInfo) {
        notifyRunningTaskAppeared(taskInfo);
    }

    public void onTaskRemoved(ActivityManager.RunningTaskInfo taskInfo) {
    public void onTaskRemoved(RunningTaskInfo taskInfo) {
        // Remove any split pairs associated with this task
        removeSplitPair(taskInfo.taskId);
        notifyRecentTasksChanged();
        notifyRunningTaskVanished(taskInfo);
        if (!enableShellTopTaskTracking()) {
            // Only notify recent tasks changed if we aren't already notifying the visible tasks
            notifyRecentTasksChanged();
        }
    }

    /**
@@ -279,7 +291,7 @@ public class RecentTasksController implements TaskStackListenerCallback,
     *
     * This currently includes windowing mode and visibility.
     */
    public void onTaskRunningInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
    public void onTaskRunningInfoChanged(RunningTaskInfo taskInfo) {
        notifyRecentTasksChanged();
        notifyRunningTaskChanged(taskInfo);
    }
@@ -289,15 +301,22 @@ public class RecentTasksController implements TaskStackListenerCallback,
        notifyRecentTasksChanged();
    }

    @Override
    public void onTaskMovedToFrontThroughTransition(RunningTaskInfo runningTaskInfo) {
        notifyTaskMovedToFront(runningTaskInfo);
    }

    @Override
    public void onTaskChangedThroughTransition(@NonNull ActivityManager.RunningTaskInfo taskInfo) {
        notifyTaskInfoChanged(taskInfo);
    }

    @Override
    public void onTaskMovedToFrontThroughTransition(
            ActivityManager.RunningTaskInfo runningTaskInfo) {
        notifyTaskMovedToFront(runningTaskInfo);
    public void onVisibleTasksChanged(@NonNull List<? extends RunningTaskInfo> visibleTasks) {
        mVisibleTasks.clear();
        mVisibleTasks.addAll(visibleTasks);
        // Notify with all the info and not just the running task info
        notifyVisibleTasksChanged(visibleTasks);
    }

    @VisibleForTesting
@@ -316,7 +335,7 @@ public class RecentTasksController implements TaskStackListenerCallback,
    /**
     * Notify the running task listener that a task appeared on desktop environment.
     */
    private void notifyRunningTaskAppeared(ActivityManager.RunningTaskInfo taskInfo) {
    private void notifyRunningTaskAppeared(RunningTaskInfo taskInfo) {
        if (mListener == null
                || !shouldEnableRunningTasksForDesktopMode()
                || taskInfo.realActivity == null) {
@@ -329,10 +348,26 @@ public class RecentTasksController implements TaskStackListenerCallback,
        }
    }

    /**
     * Notify the running task listener that a task was changed on desktop environment.
     */
    private void notifyRunningTaskChanged(RunningTaskInfo taskInfo) {
        if (mListener == null
                || !shouldEnableRunningTasksForDesktopMode()
                || taskInfo.realActivity == null) {
            return;
        }
        try {
            mListener.onRunningTaskChanged(taskInfo);
        } catch (RemoteException e) {
            Slog.w(TAG, "Failed call onRunningTaskChanged", e);
        }
    }

    /**
     * Notify the running task listener that a task was removed on desktop environment.
     */
    private void notifyRunningTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
    private void notifyRunningTaskVanished(RunningTaskInfo taskInfo) {
        if (mListener == null
                || !shouldEnableRunningTasksForDesktopMode()
                || taskInfo.realActivity == null) {
@@ -346,25 +381,30 @@ public class RecentTasksController implements TaskStackListenerCallback,
    }

    /**
     * Notify the running task listener that a task was changed on desktop environment.
     * Notify the recents task listener that a task moved to front via a transition.
     */
    private void notifyRunningTaskChanged(ActivityManager.RunningTaskInfo taskInfo) {
    private void notifyTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo) {
        if (mListener == null
                || !shouldEnableRunningTasksForDesktopMode()
                || taskInfo.realActivity == null) {
                || !DesktopModeFlags.ENABLE_TASK_STACK_OBSERVER_IN_SHELL.isTrue()
                || taskInfo.realActivity == null
                || enableShellTopTaskTracking()) {
            return;
        }
        try {
            mListener.onRunningTaskChanged(taskInfo);
            mListener.onTaskMovedToFront(GroupedTaskInfo.forFullscreenTasks(taskInfo));
        } catch (RemoteException e) {
            Slog.w(TAG, "Failed call onRunningTaskChanged", e);
            Slog.w(TAG, "Failed call onTaskMovedToFront", e);
        }
    }

    /**
     * Notify the recents task listener that a task changed via a transition.
     */
    private void notifyTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
        if (mListener == null
                || !DesktopModeFlags.ENABLE_TASK_STACK_OBSERVER_IN_SHELL.isTrue()
                || taskInfo.realActivity == null) {
                || taskInfo.realActivity == null
                || enableShellTopTaskTracking()) {
            return;
        }
        try {
@@ -374,17 +414,21 @@ public class RecentTasksController implements TaskStackListenerCallback,
        }
    }

    private void notifyTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo) {
    /**
     * Notifies that the test of visible tasks have changed.
     */
    private void notifyVisibleTasksChanged(@NonNull List<? extends RunningTaskInfo> visibleTasks) {
        if (mListener == null
                || !DesktopModeFlags.ENABLE_TASK_STACK_OBSERVER_IN_SHELL.isTrue()
                || taskInfo.realActivity == null) {
                || !enableShellTopTaskTracking()) {
            return;
        }
        try {
            GroupedTaskInfo runningTask = GroupedTaskInfo.forFullscreenTasks(taskInfo);
            mListener.onTaskMovedToFront(new GroupedTaskInfo[]{ runningTask });
            // Compute the visible recent tasks in order, and move the task to the top
            mListener.onVisibleTasksChanged(generateList(visibleTasks)
                    .toArray(new GroupedTaskInfo[0]));
        } catch (RemoteException e) {
            Slog.w(TAG, "Failed call onTaskMovedToFront", e);
            Slog.w(TAG, "Failed call onVisibleTasksChanged", e);
        }
    }

@@ -397,6 +441,11 @@ public class RecentTasksController implements TaskStackListenerCallback,
    @VisibleForTesting
    void registerRecentTasksListener(IRecentTasksListener listener) {
        mListener = listener;
        if (enableShellTopTaskTracking()) {
            ProtoLog.v(WM_SHELL_TASK_OBSERVER, "registerRecentTasksListener");
            // Post a notification for the current set of visible tasks
            mMainExecutor.executeDelayed(() -> notifyVisibleTasksChanged(mVisibleTasks), 0);
        }
    }

    @VisibleForTesting
@@ -411,14 +460,18 @@ public class RecentTasksController implements TaskStackListenerCallback,

    @VisibleForTesting
    ArrayList<GroupedTaskInfo> getRecentTasks(int maxNum, int flags, int userId) {
        // Note: the returned task list is from the most-recent to least-recent order
        final List<RecentTaskInfo> rawList = mActivityTaskManager.getRecentTasks(
                maxNum, flags, userId);
        // Note: the returned task list is ordered from the most-recent to least-recent order
        return generateList(mActivityTaskManager.getRecentTasks(maxNum, flags, userId));
    }

    /**
     * Generates a list of GroupedTaskInfos for the given list of tasks.
     */
    private <T extends TaskInfo> ArrayList<GroupedTaskInfo> generateList(@NonNull List<T> tasks) {
        // Make a mapping of task id -> task info
        final SparseArray<TaskInfo> rawMapping = new SparseArray<>();
        for (int i = 0; i < rawList.size(); i++) {
            final TaskInfo taskInfo = rawList.get(i);
        for (int i = 0; i < tasks.size(); i++) {
            final TaskInfo taskInfo = tasks.get(i);
            rawMapping.put(taskInfo.taskId, taskInfo);
        }

@@ -427,10 +480,10 @@ public class RecentTasksController implements TaskStackListenerCallback,

        int mostRecentFreeformTaskIndex = Integer.MAX_VALUE;

        ArrayList<GroupedTaskInfo> groupedTasks = new ArrayList<>();
        // Pull out the pairs as we iterate back in the list
        ArrayList<GroupedTaskInfo> recentTasks = new ArrayList<>();
        for (int i = 0; i < rawList.size(); i++) {
            final RecentTaskInfo taskInfo = rawList.get(i);
        for (int i = 0; i < tasks.size(); i++) {
            final TaskInfo taskInfo = tasks.get(i);
            if (!rawMapping.contains(taskInfo.taskId)) {
                // If it's not in the mapping, then it was already paired with another task
                continue;
@@ -441,7 +494,7 @@ public class RecentTasksController implements TaskStackListenerCallback,
                    && mDesktopRepository.get().isActiveTask(taskInfo.taskId)) {
                // Freeform tasks will be added as a separate entry
                if (mostRecentFreeformTaskIndex == Integer.MAX_VALUE) {
                    mostRecentFreeformTaskIndex = recentTasks.size();
                    mostRecentFreeformTaskIndex = groupedTasks.size();
                }
                // If task has their app bounds set to null which happens after reboot, set the
                // app bounds to persisted lastFullscreenBounds. Also set the position in parent
@@ -461,36 +514,34 @@ public class RecentTasksController implements TaskStackListenerCallback,
            }

            final int pairedTaskId = mSplitTasks.get(taskInfo.taskId, INVALID_TASK_ID);
            if (pairedTaskId != INVALID_TASK_ID && rawMapping.contains(
                    pairedTaskId)) {
            if (pairedTaskId != INVALID_TASK_ID && rawMapping.contains(pairedTaskId)) {
                final TaskInfo pairedTaskInfo = rawMapping.get(pairedTaskId);
                rawMapping.remove(pairedTaskId);
                recentTasks.add(GroupedTaskInfo.forSplitTasks(taskInfo, pairedTaskInfo,
                groupedTasks.add(GroupedTaskInfo.forSplitTasks(taskInfo, pairedTaskInfo,
                        mTaskSplitBoundsMap.get(pairedTaskId)));
            } else {
                recentTasks.add(GroupedTaskInfo.forFullscreenTasks(taskInfo));
                // TODO(346588978): Consolidate multiple visible fullscreen tasks into the same
                //  grouped task
                groupedTasks.add(GroupedTaskInfo.forFullscreenTasks(taskInfo));
            }
        }

        // Add a special entry for freeform tasks
        if (!freeformTasks.isEmpty()) {
            recentTasks.add(mostRecentFreeformTaskIndex,
            groupedTasks.add(mostRecentFreeformTaskIndex,
                    GroupedTaskInfo.forFreeformTasks(
                            freeformTasks,
                            minimizedFreeformTasks));
        }

        return recentTasks;
        if (enableShellTopTaskTracking()) {
            // We don't current send pinned tasks as a part of recent or running tasks, so remove
            // them from the list here
            groupedTasks.removeIf(
                    gti -> gti.getTaskInfo1().getWindowingMode() == WINDOWING_MODE_PINNED);
        }

    /**
     * Returns the top running leaf task.
     */
    @Nullable
    public ActivityManager.RunningTaskInfo getTopRunningTask() {
        List<ActivityManager.RunningTaskInfo> tasks = mActivityTaskManager.getTasks(1,
                false /* filterOnlyVisibleRecents */);
        return tasks.isEmpty() ? null : tasks.get(0);
        return groupedTasks;
    }

    /**
@@ -498,12 +549,13 @@ public class RecentTasksController implements TaskStackListenerCallback,
     * NOTE: This path currently makes assumptions that ignoreTaskToken is for the top task.
     */
    @Nullable
    public ActivityManager.RunningTaskInfo getTopRunningTask(
    public RunningTaskInfo getTopRunningTask(
            @Nullable WindowContainerToken ignoreTaskToken) {
        List<ActivityManager.RunningTaskInfo> tasks = mActivityTaskManager.getTasks(2,
                false /* filterOnlyVisibleRecents */);
        final List<RunningTaskInfo> tasks = enableShellTopTaskTracking()
                ? mVisibleTasks
                : mActivityTaskManager.getTasks(2, false /* filterOnlyVisibleRecents */);
        for (int i = 0; i < tasks.size(); i++) {
            final ActivityManager.RunningTaskInfo task = tasks.get(i);
            final RunningTaskInfo task = tasks.get(i);
            if (task.token.equals(ignoreTaskToken)) {
                continue;
            }
@@ -541,7 +593,7 @@ public class RecentTasksController implements TaskStackListenerCallback,
    }

    /**
     * Find the background task that match the given taskId.
     * Find the background task (in the recent tasks list) that matches the given taskId.
     */
    @Nullable
    public RecentTaskInfo findTaskInBackground(int taskId) {
@@ -638,29 +690,34 @@ public class RecentTasksController implements TaskStackListenerCallback,
            }

            @Override
            public void onRunningTaskAppeared(ActivityManager.RunningTaskInfo taskInfo) {
            public void onRunningTaskAppeared(RunningTaskInfo taskInfo) {
                mListener.call(l -> l.onRunningTaskAppeared(taskInfo));
            }

            @Override
            public void onRunningTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
            public void onRunningTaskVanished(RunningTaskInfo taskInfo) {
                mListener.call(l -> l.onRunningTaskVanished(taskInfo));
            }

            @Override
            public void onRunningTaskChanged(ActivityManager.RunningTaskInfo taskInfo) {
            public void onRunningTaskChanged(RunningTaskInfo taskInfo) {
                mListener.call(l -> l.onRunningTaskChanged(taskInfo));
            }

            @Override
            public void onTaskMovedToFront(GroupedTaskInfo[] taskInfo) {
                mListener.call(l -> l.onTaskMovedToFront(taskInfo));
            public void onTaskMovedToFront(GroupedTaskInfo taskToFront) {
                mListener.call(l -> l.onTaskMovedToFront(taskToFront));
            }

            @Override
            public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
                mListener.call(l -> l.onTaskInfoChanged(taskInfo));
            }

            @Override
            public void onVisibleTasksChanged(GroupedTaskInfo[] visibleTasks) {
                mListener.call(l -> l.onVisibleTasksChanged(visibleTasks));
            }
        };

        public IRecentTasksImpl(RecentTasksController controller) {
@@ -714,12 +771,12 @@ public class RecentTasksController implements TaskStackListenerCallback,
        }

        @Override
        public ActivityManager.RunningTaskInfo[] getRunningTasks(int maxNum) {
            final ActivityManager.RunningTaskInfo[][] tasks =
                    new ActivityManager.RunningTaskInfo[][]{null};
        public RunningTaskInfo[] getRunningTasks(int maxNum) {
            final RunningTaskInfo[][] tasks =
                    new RunningTaskInfo[][]{null};
            executeRemoteCallWithTaskPermission(mController, "getRunningTasks",
                    (controller) -> tasks[0] = ActivityTaskManager.getInstance().getTasks(maxNum)
                            .toArray(new ActivityManager.RunningTaskInfo[0]),
                            .toArray(new RunningTaskInfo[0]),
                    true /* blocking */);
            return tasks[0];
        }
Loading