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

Commit b35b56af authored by Winson Chung's avatar Winson Chung Committed by Android (Google) Code Review
Browse files

Merge "Call into shell for recent tasks" into sc-v2-dev

parents 4112ae6a f097e628
Loading
Loading
Loading
Loading
+50 −65
Original line number Diff line number Diff line
@@ -22,35 +22,33 @@ import android.annotation.TargetApi;
import android.app.ActivityManager;
import android.os.Build;
import android.os.Process;
import android.util.Log;
import android.os.RemoteException;
import android.util.SparseBooleanArray;

import androidx.annotation.VisibleForTesting;

import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.util.LooperExecutor;
import com.android.systemui.shared.recents.model.GroupTask;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.KeyguardManagerCompat;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.TaskStackChangeListeners;
import com.android.wm.shell.recents.IRecentTasksListener;
import com.android.wm.shell.util.GroupedRecentTaskInfo;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;

/**
 * Manages the recent task list from the system, caching it as necessary.
 */
@TargetApi(Build.VERSION_CODES.R)
public class RecentTasksList extends TaskStackChangeListener {
public class RecentTasksList {

    private static final TaskLoadResult INVALID_RESULT = new TaskLoadResult(-1, false, 0);

    private final KeyguardManagerCompat mKeyguardManager;
    private final LooperExecutor mMainThreadExecutor;
    private final ActivityManagerWrapper mActivityManagerWrapper;
    private final SystemUiProxy mSysUiProxy;

    // The list change id, increments as the task list changes in the system
    private int mChangeId;
@@ -62,12 +60,17 @@ public class RecentTasksList extends TaskStackChangeListener {
    private TaskLoadResult mResultsUi = INVALID_RESULT;

    public RecentTasksList(LooperExecutor mainThreadExecutor,
            KeyguardManagerCompat keyguardManager, ActivityManagerWrapper activityManagerWrapper) {
            KeyguardManagerCompat keyguardManager, SystemUiProxy sysUiProxy) {
        mMainThreadExecutor = mainThreadExecutor;
        mKeyguardManager = keyguardManager;
        mChangeId = 1;
        mActivityManagerWrapper = activityManagerWrapper;
        TaskStackChangeListeners.getInstance().registerTaskStackListener(this);
        mSysUiProxy = sysUiProxy;
        sysUiProxy.registerRecentTasksListener(new IRecentTasksListener.Stub() {
            @Override
            public void onRecentTasksChanged() throws RemoteException {
                mMainThreadExecutor.execute(RecentTasksList.this::onRecentTasksChanged);
            }
        });
    }

    @VisibleForTesting
@@ -78,10 +81,11 @@ public class RecentTasksList extends TaskStackChangeListener {
    /**
     * Fetches the task keys skipping any local cache.
     */
    public void getTaskKeys(int numTasks, Consumer<ArrayList<Task>> callback) {
    public void getTaskKeys(int numTasks, Consumer<ArrayList<GroupTask>> callback) {
        // Kick off task loading in the background
        UI_HELPER_EXECUTOR.execute(() -> {
            ArrayList<Task> tasks = loadTasksInBackground(numTasks, -1, true /* loadKeysOnly */);
            ArrayList<GroupTask> tasks = loadTasksInBackground(numTasks, -1,
                    true /* loadKeysOnly */);
            mMainThreadExecutor.execute(() -> callback.accept(tasks));
        });
    }
@@ -93,14 +97,15 @@ public class RecentTasksList extends TaskStackChangeListener {
     * @param callback The callback to receive the list of recent tasks
     * @return The change id of the current task list
     */
    public synchronized int getTasks(boolean loadKeysOnly, Consumer<ArrayList<Task>> callback) {
    public synchronized int getTasks(boolean loadKeysOnly,
            Consumer<ArrayList<GroupTask>> callback) {
        final int requestLoadId = mChangeId;
        if (mResultsUi.isValidForRequest(requestLoadId, loadKeysOnly)) {
            // The list is up to date, send the callback on the next frame,
            // so that requestID can be returned first.
            if (callback != null) {
                // Copy synchronously as the changeId might change by next frame
                ArrayList<Task> result = copyOf(mResultsUi);
                ArrayList<GroupTask> result = copyOf(mResultsUi);
                mMainThreadExecutor.post(() -> {
                    callback.accept(result);
                });
@@ -120,7 +125,7 @@ public class RecentTasksList extends TaskStackChangeListener {
                mLoadingTasksInBackground = false;
                mResultsUi = loadResult;
                if (callback != null) {
                    ArrayList<Task> result = copyOf(mResultsUi);
                    ArrayList<GroupTask> result = copyOf(mResultsUi);
                    callback.accept(result);
                }
            });
@@ -136,35 +141,7 @@ public class RecentTasksList extends TaskStackChangeListener {
        return mChangeId == changeId;
    }

    @Override
    public void onTaskStackChanged() {
        invalidateLoadedTasks();
    }

    @Override
    public void onRecentTaskListUpdated() {
        // In some cases immediately after booting, the tasks in the system recent task list may be
        // loaded, but not in the active task hierarchy in the system.  These tasks are displayed in 
        // overview, but removing them don't result in a onTaskStackChanged() nor a onTaskRemoved()
        // callback (those are for changes to the active tasks), but the task list is still updated,
        // so we should also invalidate the change id to ensure we load a new list instead of 
        // reusing a stale list.
        invalidateLoadedTasks();
    }

    @Override
    public void onTaskRemoved(int taskId) {
        invalidateLoadedTasks();
    }


    @Override
    public void onActivityPinned(String packageName, int userId, int taskId, int stackId) {
        invalidateLoadedTasks();
    }

    @Override
    public synchronized void onActivityUnpinned() {
    public void onRecentTasksChanged() {
        invalidateLoadedTasks();
    }

@@ -180,8 +157,8 @@ public class RecentTasksList extends TaskStackChangeListener {
    @VisibleForTesting
    TaskLoadResult loadTasksInBackground(int numTasks, int requestId, boolean loadKeysOnly) {
        int currentUserId = Process.myUserHandle().getIdentifier();
        List<ActivityManager.RecentTaskInfo> rawTasks =
                mActivityManagerWrapper.getRecentTasks(numTasks, currentUserId);
        ArrayList<GroupedRecentTaskInfo> rawTasks =
                mSysUiProxy.getRecentTasks(numTasks, currentUserId);
        // The raw tasks are given in most-recent to least-recent order, we need to reverse it
        Collections.reverse(rawTasks);

@@ -197,45 +174,53 @@ public class RecentTasksList extends TaskStackChangeListener {
        };

        TaskLoadResult allTasks = new TaskLoadResult(requestId, loadKeysOnly, rawTasks.size());
        for (ActivityManager.RecentTaskInfo rawTask : rawTasks) {
            Task.TaskKey taskKey = new Task.TaskKey(rawTask);
            Task task;
            if (!loadKeysOnly) {
                boolean isLocked = tmpLockedUsers.get(taskKey.userId);
                task = Task.from(taskKey, rawTask, isLocked);
            } else {
                task = new Task(taskKey);
            }
            task.setLastSnapshotData(rawTask);
            allTasks.add(task);
        for (GroupedRecentTaskInfo rawTask : rawTasks) {
            ActivityManager.RecentTaskInfo taskInfo1 = rawTask.mTaskInfo1;
            ActivityManager.RecentTaskInfo taskInfo2 = rawTask.mTaskInfo2;
            Task.TaskKey task1Key = new Task.TaskKey(taskInfo1);
            Task task1 = loadKeysOnly
                    ? new Task(task1Key)
                    : Task.from(task1Key, taskInfo1,
                            tmpLockedUsers.get(task1Key.userId) /* isLocked */);
            task1.setLastSnapshotData(taskInfo1);
            Task task2 = null;
            if (taskInfo2 != null) {
                Task.TaskKey task2Key = new Task.TaskKey(taskInfo2);
                task2 = loadKeysOnly
                        ? new Task(task2Key)
                        : Task.from(task2Key, taskInfo2,
                                tmpLockedUsers.get(task2Key.userId) /* isLocked */);
                task2.setLastSnapshotData(taskInfo2);
            }
            allTasks.add(new GroupTask(task1, task2));
        }

        return allTasks;
    }

    private ArrayList<Task> copyOf(ArrayList<Task> tasks) {
        ArrayList<Task> newTasks = new ArrayList<>();
    private ArrayList<GroupTask> copyOf(ArrayList<GroupTask> tasks) {
        ArrayList<GroupTask> newTasks = new ArrayList<>();
        for (int i = 0; i < tasks.size(); i++) {
            newTasks.add(new Task(tasks.get(i)));
            newTasks.add(new GroupTask(tasks.get(i)));
        }
        return newTasks;
    }

    private static class TaskLoadResult extends ArrayList<Task> {
    private static class TaskLoadResult extends ArrayList<GroupTask> {

        final int mId;
        final int mRequestId;

        // If the result was loaded with keysOnly  = true
        final boolean mKeysOnly;

        TaskLoadResult(int id, boolean keysOnly, int size) {
        TaskLoadResult(int requestId, boolean keysOnly, int size) {
            super(size);
            mId = id;
            mRequestId = requestId;
            mKeysOnly = keysOnly;
        }

        boolean isValidForRequest(int requestId, boolean loadKeysOnly) {
            return mId == requestId && (!mKeysOnly || loadKeysOnly);
            return mRequestId == requestId && (!mKeysOnly || loadKeysOnly);
        }
    }
}
 No newline at end of file
+11 −9
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@ import com.android.launcher3.icons.IconProvider;
import com.android.launcher3.icons.IconProvider.IconChangeListener;
import com.android.launcher3.util.Executors.SimpleThreadFactory;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.systemui.shared.recents.model.GroupTask;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -70,7 +71,7 @@ public class RecentsModel extends TaskStackChangeListener implements IconChangeL
    private RecentsModel(Context context) {
        mContext = context;
        mTaskList = new RecentTasksList(MAIN_EXECUTOR,
                new KeyguardManagerCompat(context), ActivityManagerWrapper.getInstance());
                new KeyguardManagerCompat(context), SystemUiProxy.INSTANCE.get(context));

        IconProvider iconProvider = new IconProvider(context);
        mIconCache = new TaskIconCache(context, RECENTS_MODEL_EXECUTOR, iconProvider);
@@ -95,7 +96,7 @@ public class RecentsModel extends TaskStackChangeListener implements IconChangeL
     *                always called on the UI thread.
     * @return the request id associated with this call.
     */
    public int getTasks(Consumer<ArrayList<Task>> callback) {
    public int getTasks(Consumer<ArrayList<GroupTask>> callback) {
        return mTaskList.getTasks(false /* loadKeysOnly */, callback);
    }

@@ -120,9 +121,9 @@ public class RecentsModel extends TaskStackChangeListener implements IconChangeL
     * @param callback Receives true if task is removed, false otherwise
     */
    public void isTaskRemoved(int taskId, Consumer<Boolean> callback) {
        mTaskList.getTasks(true /* loadKeysOnly */, (tasks) -> {
            for (Task task : tasks) {
                if (task.key.id == taskId) {
        mTaskList.getTasks(true /* loadKeysOnly */, (taskGroups) -> {
            for (GroupTask group : taskGroups) {
                if (group.containsTask(taskId)) {
                    callback.accept(false);
                    return;
                }
@@ -148,14 +149,15 @@ public class RecentsModel extends TaskStackChangeListener implements IconChangeL
        ActivityManager.RunningTaskInfo runningTask =
                ActivityManagerWrapper.getInstance().getRunningTask();
        int runningTaskId = runningTask != null ? runningTask.id : -1;
        mTaskList.getTaskKeys(mThumbnailCache.getCacheSize(), tasks -> {
            for (Task task : tasks) {
                if (task.key.id == runningTaskId) {
        mTaskList.getTaskKeys(mThumbnailCache.getCacheSize(), taskGroups -> {
            for (GroupTask group : taskGroups) {
                if (group.containsTask(runningTaskId)) {
                    // Skip the running task, it's not going to have an up-to-date snapshot by the
                    // time the user next enters overview
                    continue;
                }
                mThumbnailCache.updateThumbnailInCache(task);
                mThumbnailCache.updateThumbnailInCache(group.task1);
                mThumbnailCache.updateThumbnailInCache(group.task2);
            }
        });
    }
+54 −3
Original line number Diff line number Diff line
@@ -15,6 +15,8 @@
 */
package com.android.quickstep;

import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE;

import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;

import android.app.PendingIntent;
@@ -48,6 +50,9 @@ import com.android.systemui.shared.system.smartspace.ISmartspaceTransitionContro
import com.android.wm.shell.onehanded.IOneHanded;
import com.android.wm.shell.pip.IPip;
import com.android.wm.shell.pip.IPipAnimationListener;
import com.android.wm.shell.recents.IRecentTasks;
import com.android.wm.shell.recents.IRecentTasksListener;
import com.android.wm.shell.util.GroupedRecentTaskInfo;
import com.android.wm.shell.splitscreen.ISplitScreen;
import com.android.wm.shell.splitscreen.ISplitScreenListener;
import com.android.wm.shell.startingsurface.IStartingWindow;
@@ -55,6 +60,7 @@ import com.android.wm.shell.startingsurface.IStartingWindowListener;
import com.android.wm.shell.transition.IShellTransitions;

import java.util.ArrayList;
import java.util.Arrays;

/**
 * Holds the reference to SystemUI.
@@ -73,6 +79,7 @@ public class SystemUiProxy implements ISystemUiProxy,
    private IOneHanded mOneHanded;
    private IShellTransitions mShellTransitions;
    private IStartingWindow mStartingWindow;
    private IRecentTasks mRecentTasks;
    private final DeathRecipient mSystemUiProxyDeathRecipient = () -> {
        MAIN_EXECUTOR.execute(() -> clearProxy());
    };
@@ -83,6 +90,7 @@ public class SystemUiProxy implements ISystemUiProxy,
    private ISplitScreenListener mPendingSplitScreenListener;
    private IStartingWindowListener mPendingStartingWindowListener;
    private ISmartspaceCallback mPendingSmartspaceCallback;
    private IRecentTasksListener mPendingRecentTasksListener;
    private final ArrayList<RemoteTransitionCompat> mPendingRemoteTransitions = new ArrayList<>();

    // Used to dedupe calls to SystemUI
@@ -147,7 +155,7 @@ public class SystemUiProxy implements ISystemUiProxy,

    public void setProxy(ISystemUiProxy proxy, IPip pip, ISplitScreen splitScreen,
            IOneHanded oneHanded, IShellTransitions shellTransitions,
            IStartingWindow startingWindow,
            IStartingWindow startingWindow, IRecentTasks recentTasks,
            ISmartspaceTransitionController smartSpaceTransitionController) {
        unlinkToDeath();
        mSystemUiProxy = proxy;
@@ -157,6 +165,7 @@ public class SystemUiProxy implements ISystemUiProxy,
        mShellTransitions = shellTransitions;
        mStartingWindow = startingWindow;
        mSmartspaceTransitionController = smartSpaceTransitionController;
        mRecentTasks = recentTasks;
        linkToDeath();
        // re-attach the listeners once missing due to setProxy has not been initialized yet.
        if (mPendingPipAnimationListener != null && mPip != null) {
@@ -179,6 +188,10 @@ public class SystemUiProxy implements ISystemUiProxy,
            registerRemoteTransition(mPendingRemoteTransitions.get(i));
        }
        mPendingRemoteTransitions.clear();
        if (mPendingRecentTasksListener != null && mRecentTasks != null) {
            registerRecentTasksListener(mPendingRecentTasksListener);
            mPendingRecentTasksListener = null;
        }

        if (mPendingSetNavButtonAlpha != null) {
            mPendingSetNavButtonAlpha.run();
@@ -187,7 +200,7 @@ public class SystemUiProxy implements ISystemUiProxy,
    }

    public void clearProxy() {
        setProxy(null, null, null, null, null, null, null);
        setProxy(null, null, null, null, null, null, null, null);
    }

    // TODO(141886704): Find a way to remove this
@@ -759,7 +772,6 @@ public class SystemUiProxy implements ISystemUiProxy,
        }
    }


    //
    // SmartSpace transitions
    //
@@ -775,4 +787,43 @@ public class SystemUiProxy implements ISystemUiProxy,
            mPendingSmartspaceCallback = callback;
        }
    }

    //
    // Recents
    //

    public void registerRecentTasksListener(IRecentTasksListener listener) {
        if (mRecentTasks != null) {
            try {
                mRecentTasks.registerRecentTasksListener(listener);
            } catch (RemoteException e) {
                Log.w(TAG, "Failed call registerRecentTasksListener", e);
            }
        } else {
            mPendingRecentTasksListener = listener;
        }
    }

    public void unregisterRecentTasksListener(IRecentTasksListener listener) {
        if (mRecentTasks != null) {
            try {
                mRecentTasks.unregisterRecentTasksListener(listener);
            } catch (RemoteException e) {
                Log.w(TAG, "Failed call unregisterRecentTasksListener");
            }
        }
        mPendingRecentTasksListener = null;
    }

    public ArrayList<GroupedRecentTaskInfo> getRecentTasks(int numTasks, int userId) {
        if (mRecentTasks != null) {
            try {
                return new ArrayList<>(Arrays.asList(mRecentTasks.getRecentTasks(numTasks,
                        RECENT_IGNORE_UNAVAILABLE, userId)));
            } catch (RemoteException e) {
                Log.w(TAG, "Failed call getRecentTasks", e);
            }
        }
        return new ArrayList<>();
    }
}
+3 −0
Original line number Diff line number Diff line
@@ -104,6 +104,9 @@ public class TaskThumbnailCache {
     * Synchronously fetches the thumbnail for the given {@param task} and puts it in the cache.
     */
    public void updateThumbnailInCache(Task task) {
        if (task == null) {
            return;
        }
        Preconditions.assertUIThread();
        // Fetch the thumbnail for this task and put it in the cache
        if (task.thumbnail == null) {
+5 −1
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import static com.android.launcher3.testing.TestProtocol.TASKBAR_WINDOW_CRASH;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.quickstep.GestureState.DEFAULT_STATE;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_RECENT_TASKS;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_ONE_HANDED;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_PIP;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_SHELL_TRANSITIONS;
@@ -113,6 +114,7 @@ import com.android.systemui.shared.system.smartspace.ISmartspaceTransitionContro
import com.android.systemui.shared.tracing.ProtoTraceable;
import com.android.wm.shell.onehanded.IOneHanded;
import com.android.wm.shell.pip.IPip;
import com.android.wm.shell.recents.IRecentTasks;
import com.android.wm.shell.splitscreen.ISplitScreen;
import com.android.wm.shell.startingsurface.IStartingWindow;
import com.android.wm.shell.transition.IShellTransitions;
@@ -171,9 +173,11 @@ public class TouchInteractionService extends Service
            ISmartspaceTransitionController smartspaceTransitionController =
                    ISmartspaceTransitionController.Stub.asInterface(
                            bundle.getBinder(KEY_EXTRA_SMARTSPACE_TRANSITION_CONTROLLER));
            IRecentTasks recentTasks = IRecentTasks.Stub.asInterface(
                    bundle.getBinder(KEY_EXTRA_RECENT_TASKS));
            MAIN_EXECUTOR.execute(() -> {
                SystemUiProxy.INSTANCE.get(TouchInteractionService.this).setProxy(proxy, pip,
                        splitscreen, onehanded, shellTransitions, startingWindow,
                        splitscreen, onehanded, shellTransitions, startingWindow, recentTasks,
                        smartspaceTransitionController);
                TouchInteractionService.this.initInputMonitor();
                preloadOverview(true /* fromInit */);
Loading