summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/app/ActivityOptions.java30
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java8
-rw-r--r--services/core/java/com/android/server/wm/ActivityStackSupervisor.java3
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java29
-rw-r--r--services/core/java/com/android/server/wm/RecentTasks.java163
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java120
6 files changed, 331 insertions, 22 deletions
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index e2d868f2c45e..da9ea8359854 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -222,6 +222,13 @@ public class ActivityOptions {
private static final String KEY_AVOID_MOVE_TO_FRONT = "android.activity.avoidMoveToFront";
/**
+ * See {@link #setFreezeRecentTasksReordering()}.
+ * @hide
+ */
+ private static final String KEY_FREEZE_RECENT_TASKS_REORDERING =
+ "android.activity.freezeRecentTasksReordering";
+
+ /**
* Where the split-screen-primary stack should be positioned.
* @hide
*/
@@ -324,6 +331,7 @@ public class ActivityOptions {
private boolean mTaskOverlay;
private boolean mTaskOverlayCanResume;
private boolean mAvoidMoveToFront;
+ private boolean mFreezeRecentTasksReordering;
private AppTransitionAnimationSpec mAnimSpecs[];
private int mRotationAnimationHint = -1;
private Bundle mAppVerificationBundle;
@@ -946,6 +954,7 @@ public class ActivityOptions {
mTaskOverlay = opts.getBoolean(KEY_TASK_OVERLAY, false);
mTaskOverlayCanResume = opts.getBoolean(KEY_TASK_OVERLAY_CAN_RESUME, false);
mAvoidMoveToFront = opts.getBoolean(KEY_AVOID_MOVE_TO_FRONT, false);
+ mFreezeRecentTasksReordering = opts.getBoolean(KEY_FREEZE_RECENT_TASKS_REORDERING, false);
mSplitScreenCreateMode = opts.getInt(KEY_SPLIT_SCREEN_CREATE_MODE,
SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT);
mDisallowEnterPictureInPictureWhileLaunching = opts.getBoolean(
@@ -1304,6 +1313,24 @@ public class ActivityOptions {
return mAvoidMoveToFront;
}
+ /**
+ * Sets whether the launch of this activity should freeze the recent task list reordering until
+ * the next user interaction or timeout. This flag is only applied when starting an activity
+ * in recents.
+ * @hide
+ */
+ public void setFreezeRecentTasksReordering() {
+ mFreezeRecentTasksReordering = true;
+ }
+
+ /**
+ * @return whether the launch of this activity should freeze the recent task list reordering
+ * @hide
+ */
+ public boolean freezeRecentTasksReordering() {
+ return mFreezeRecentTasksReordering;
+ }
+
/** @hide */
public int getSplitScreenCreateMode() {
return mSplitScreenCreateMode;
@@ -1502,6 +1529,9 @@ public class ActivityOptions {
if (mAvoidMoveToFront) {
b.putBoolean(KEY_AVOID_MOVE_TO_FRONT, mAvoidMoveToFront);
}
+ if (mFreezeRecentTasksReordering) {
+ b.putBoolean(KEY_FREEZE_RECENT_TASKS_REORDERING, mFreezeRecentTasksReordering);
+ }
if (mSplitScreenCreateMode != SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT) {
b.putInt(KEY_SPLIT_SCREEN_CREATE_MODE, mSplitScreenCreateMode);
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java
index ea6fb48c67e7..7b39ba3d2759 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java
@@ -78,4 +78,12 @@ public abstract class ActivityOptionsCompat {
}
});
}
+
+ /**
+ * Sets the flag to freeze the recents task list reordering as a part of launching the activity.
+ */
+ public static ActivityOptions setFreezeRecentTasksList(ActivityOptions opts) {
+ opts.setFreezeRecentTasksReordering();
+ return opts;
+ }
}
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index 9a5ec2ab17dc..7cea4587bec5 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -2736,6 +2736,9 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
if (activityOptions != null) {
activityType = activityOptions.getLaunchActivityType();
windowingMode = activityOptions.getLaunchWindowingMode();
+ if (activityOptions.freezeRecentTasksReordering()) {
+ mRecentTasks.setFreezeTaskListReordering();
+ }
}
if (activityType == ACTIVITY_TYPE_HOME || activityType == ACTIVITY_TYPE_RECENTS) {
throw new IllegalArgumentException("startActivityFromRecents: Task "
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 33573932199c..bcfbefe95479 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -41,12 +41,14 @@ import static android.view.View.GONE;
import static android.view.WindowManager.DOCKED_BOTTOM;
import static android.view.WindowManager.DOCKED_INVALID;
import static android.view.WindowManager.DOCKED_TOP;
+import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
+import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.NEEDS_MENU_SET_TRUE;
import static android.view.WindowManager.LayoutParams.NEEDS_MENU_UNSET;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
@@ -182,6 +184,8 @@ import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.ToBooleanFunction;
import com.android.internal.util.function.TriConsumer;
+import com.android.internal.util.function.pooled.PooledConsumer;
+import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.AnimationThread;
import com.android.server.policy.WindowManagerPolicy;
import com.android.server.wm.utils.DisplayRotationUtil;
@@ -887,6 +891,10 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
mTapDetector = new TaskTapPointerEventListener(mWmService, this);
registerPointerEventListener(mTapDetector);
registerPointerEventListener(mWmService.mMousePositionTracker);
+ if (mWmService.mAtmService.getRecentTasks() != null) {
+ registerPointerEventListener(
+ mWmService.mAtmService.getRecentTasks().getInputListener());
+ }
mDisplayPolicy = new DisplayPolicy(service, this);
mDisplayRotation = new DisplayRotation(service, this);
@@ -2381,6 +2389,27 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
/**
+ * Returns true if the input point is within an app window.
+ */
+ boolean pointWithinAppWindow(int x, int y) {
+ final int[] targetWindowType = {-1};
+ final Consumer fn = PooledLambda.obtainConsumer((w, nonArg) -> {
+ if (targetWindowType[0] != -1) {
+ return;
+ }
+
+ if (w.isOnScreen() && w.isVisibleLw() && w.getFrameLw().contains(x, y)) {
+ targetWindowType[0] = w.mAttrs.type;
+ return;
+ }
+ }, PooledLambda.__(WindowState.class), mTmpRect);
+ forAllWindows(fn, true /* traverseTopToBottom */);
+ ((PooledConsumer) fn).recycle();
+ return FIRST_APPLICATION_WINDOW <= targetWindowType[0]
+ && targetWindowType[0] <= LAST_APPLICATION_WINDOW;
+ }
+
+ /**
* Find the task whose outside touch area (for resizing) (x, y) falls within.
* Returns null if the touch doesn't fall into a resizing area.
*/
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index 0480d438e3cd..d69ae3d5c3c0 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -60,6 +60,7 @@ import android.os.Bundle;
import android.os.Environment;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.text.TextUtils;
@@ -67,8 +68,11 @@ import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
+import android.view.MotionEvent;
+import android.view.WindowManagerPolicyConstants.PointerEventListener;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.am.ActivityManagerService;
import com.google.android.collect.Sets;
@@ -109,8 +113,11 @@ class RecentTasks {
private static final int DEFAULT_INITIAL_CAPACITY = 5;
- // Whether or not to move all affiliated tasks to the front when one of the tasks is launched
- private static final boolean MOVE_AFFILIATED_TASKS_TO_FRONT = false;
+ // The duration of time after freezing the recent tasks list where getRecentTasks() will return
+ // a stable ordering of the tasks. Upon the next call to getRecentTasks() beyond this duration,
+ // the task list will be unfrozen and committed (the current top task will be moved to the
+ // front of the list)
+ private static final long FREEZE_TASK_LIST_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(5);
// Comparator to sort by taskId
private static final Comparator<TaskRecord> TASK_ID_COMPARATOR =
@@ -174,12 +181,45 @@ class RecentTasks {
private int mMaxNumVisibleTasks;
private long mActiveTasksSessionDurationMs;
+ // When set, the task list will not be reordered as tasks within the list are moved to the
+ // front. Newly created tasks, or tasks that are removed from the list will continue to change
+ // the list. This does not affect affiliated tasks.
+ private boolean mFreezeTaskListReordering;
+ private long mFreezeTaskListReorderingTime;
+ private long mFreezeTaskListTimeoutMs = FREEZE_TASK_LIST_TIMEOUT_MS;
+
// Mainly to avoid object recreation on multiple calls.
private final ArrayList<TaskRecord> mTmpRecents = new ArrayList<>();
private final HashMap<ComponentName, ActivityInfo> mTmpAvailActCache = new HashMap<>();
private final HashMap<String, ApplicationInfo> mTmpAvailAppCache = new HashMap<>();
private final SparseBooleanArray mTmpQuietProfileUserIds = new SparseBooleanArray();
+ // TODO(b/127498985): This is currently a rough heuristic for interaction inside an app
+ private final PointerEventListener mListener = new PointerEventListener() {
+ @Override
+ public void onPointerEvent(MotionEvent ev) {
+ if (!mFreezeTaskListReordering || ev.getAction() != MotionEvent.ACTION_DOWN) {
+ // Skip if we aren't freezing or starting a gesture
+ return;
+ }
+ int displayId = ev.getDisplayId();
+ int x = (int) ev.getX();
+ int y = (int) ev.getY();
+ mService.mH.post(PooledLambda.obtainRunnable((nonArg) -> {
+ synchronized (mService.mGlobalLock) {
+ // Unfreeze the task list once we touch down in a task
+ final RootActivityContainer rac = mService.mRootActivityContainer;
+ final DisplayContent dc = rac.getActivityDisplay(displayId).mDisplayContent;
+ if (dc.pointWithinAppWindow(x, y)) {
+ final ActivityStack stack = mService.getTopDisplayFocusedStack();
+ final TaskRecord topTask = stack != null ? stack.topTask() : null;
+ resetFreezeTaskListReordering(topTask);
+ }
+ }
+ }, null).recycleOnUse());
+ }
+ };
+
@VisibleForTesting
RecentTasks(ActivityTaskManagerService service, TaskPersister taskPersister) {
mService = service;
@@ -214,6 +254,73 @@ class RecentTasks {
mGlobalMaxNumTasks = globalMaxNumTasks;
}
+ @VisibleForTesting
+ void setFreezeTaskListTimeoutParams(long reorderingTime, long timeoutMs) {
+ mFreezeTaskListReorderingTime = reorderingTime;
+ mFreezeTaskListTimeoutMs = timeoutMs;
+ }
+
+ PointerEventListener getInputListener() {
+ return mListener;
+ }
+
+ /**
+ * Freezes the current recent task list order until either a user interaction with the current
+ * app, or a timeout occurs.
+ */
+ void setFreezeTaskListReordering() {
+ // Always update the reordering time when this is called to ensure that the timeout
+ // is reset
+ mFreezeTaskListReordering = true;
+ mFreezeTaskListReorderingTime = SystemClock.elapsedRealtime();
+ }
+
+ /**
+ * Commits the frozen recent task list order, moving the provided {@param topTask} to the
+ * front of the list.
+ */
+ void resetFreezeTaskListReordering(TaskRecord topTask) {
+ if (!mFreezeTaskListReordering) {
+ return;
+ }
+
+ // Once we end freezing the task list, reset the existing task order to the stable state
+ mFreezeTaskListReordering = false;
+
+ // If the top task is provided, then restore the top task to the front of the list
+ if (topTask != null) {
+ mTasks.remove(topTask);
+ mTasks.add(0, topTask);
+ }
+
+ // Resume trimming tasks
+ trimInactiveRecentTasks();
+ }
+
+ /**
+ * Resets the frozen recent task list order if the timeout has passed. This should be called
+ * before we need to iterate the task list in order (either for purposes of returning the list
+ * to SystemUI or if we need to trim tasks in order)
+ */
+ void resetFreezeTaskListReorderingOnTimeout() {
+ // Unfreeze the recent task list if the time heuristic has passed
+ if (mFreezeTaskListReorderingTime
+ > (SystemClock.elapsedRealtime() - mFreezeTaskListTimeoutMs)) {
+ return;
+ }
+
+ final ActivityStack focusedStack = mService.getTopDisplayFocusedStack();
+ final TaskRecord topTask = focusedStack != null
+ ? focusedStack.topTask()
+ : null;
+ resetFreezeTaskListReordering(topTask);
+ }
+
+ @VisibleForTesting
+ boolean isFreezeTaskListReorderingSet() {
+ return mFreezeTaskListReordering;
+ }
+
/**
* Loads the parameters from the system resources.
*/
@@ -351,7 +458,8 @@ class RecentTasks {
}
Slog.i(TAG, "Loading recents for user " + userId + " into memory.");
- mTasks.addAll(mTaskPersister.restoreTasksForUserLocked(userId, preaddedTasks));
+ List<TaskRecord> tasks = mTaskPersister.restoreTasksForUserLocked(userId, preaddedTasks);
+ mTasks.addAll(tasks);
cleanupLocked(userId);
mUsersWithRecentsLoaded.put(userId, true);
@@ -746,11 +854,10 @@ class RecentTasks {
getDetailedTasks, userId, callingUid));
}
-
/**
* @return the list of recent tasks for presentation.
*/
- ArrayList<ActivityManager.RecentTaskInfo> getRecentTasksImpl(int maxNum, int flags,
+ private ArrayList<ActivityManager.RecentTaskInfo> getRecentTasksImpl(int maxNum, int flags,
boolean getTasksAllowed, boolean getDetailedTasks, int userId, int callingUid) {
final boolean withExcluded = (flags & RECENT_WITH_EXCLUDED) != 0;
@@ -763,6 +870,9 @@ class RecentTasks {
final Set<Integer> includedUsers = getProfileIds(userId);
includedUsers.add(Integer.valueOf(userId));
+ // Check if the frozen task list has timed out
+ resetFreezeTaskListReorderingOnTimeout();
+
final ArrayList<ActivityManager.RecentTaskInfo> res = new ArrayList<>();
final int size = mTasks.size();
int numVisibleTasks = 0;
@@ -946,24 +1056,20 @@ class RecentTasks {
if (task.inRecents) {
int taskIndex = mTasks.indexOf(task);
if (taskIndex >= 0) {
- if (!isAffiliated || !MOVE_AFFILIATED_TASKS_TO_FRONT) {
- // Simple case: this is not an affiliated task, so we just move it to the front.
- mTasks.remove(taskIndex);
- mTasks.add(0, task);
+ if (!isAffiliated) {
+ if (!mFreezeTaskListReordering) {
+ // Simple case: this is not an affiliated task, so we just move it to the
+ // front unless overridden by the provided activity options
+ mTasks.remove(taskIndex);
+ mTasks.add(0, task);
+
+ if (DEBUG_RECENTS) {
+ Slog.d(TAG_RECENTS, "addRecent: moving to top " + task
+ + " from " + taskIndex);
+ }
+ }
notifyTaskPersisterLocked(task, false);
- if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: moving to top " + task
- + " from " + taskIndex);
return;
- } else {
- // More complicated: need to keep all affiliated tasks together.
- if (moveAffiliatedTasksToFront(task, taskIndex)) {
- // All went well.
- return;
- }
-
- // Uh oh... something bad in the affiliation chain, try to rebuild
- // everything and then go through our general path of adding a new task.
- needAffiliationFix = true;
}
} else {
Slog.wtf(TAG, "Task with inRecent not in recents: " + task);
@@ -1063,6 +1169,11 @@ class RecentTasks {
* Trims the recents task list to the global max number of recents.
*/
private void trimInactiveRecentTasks() {
+ if (mFreezeTaskListReordering) {
+ // Defer trimming inactive recent tasks until we are unfrozen
+ return;
+ }
+
int recentsCount = mTasks.size();
// Remove from the end of the list until we reach the max number of recents
@@ -1086,7 +1197,7 @@ class RecentTasks {
+ " quiet=" + mTmpQuietProfileUserIds.get(userId));
}
- // Remove any inactive tasks, calculate the latest set of visible tasks
+ // Remove any inactive tasks, calculate the latest set of visible tasks.
int numVisibleTasks = 0;
for (int i = 0; i < mTasks.size();) {
final TaskRecord task = mTasks.get(i);
@@ -1300,6 +1411,11 @@ class RecentTasks {
* list (if any).
*/
private int findRemoveIndexForAddTask(TaskRecord task) {
+ if (mFreezeTaskListReordering) {
+ // Defer removing tasks due to the addition of new tasks until the task list is unfrozen
+ return -1;
+ }
+
final int recentsCount = mTasks.size();
final Intent intent = task.intent;
final boolean document = intent != null && intent.isDocument();
@@ -1536,6 +1652,9 @@ class RecentTasks {
pw.println("ACTIVITY MANAGER RECENT TASKS (dumpsys activity recents)");
pw.println("mRecentsUid=" + mRecentsUid);
pw.println("mRecentsComponent=" + mRecentsComponent);
+ pw.println("mFreezeTaskListReordering=" + mFreezeTaskListReordering);
+ pw.println("mFreezeTaskListReorderingTime (time since)="
+ + (SystemClock.elapsedRealtime() - mFreezeTaskListReorderingTime) + "ms");
if (mTasks.isEmpty()) {
return;
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index fc1eb1c57198..68e7470b8763 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -583,6 +583,114 @@ public class RecentTasksTest extends ActivityTestsBase {
}
@Test
+ public void testFreezeTaskListOrder_reorderExistingTask() {
+ // Add some tasks
+ mRecentTasks.add(mTasks.get(0));
+ mRecentTasks.add(mTasks.get(1));
+ mRecentTasks.add(mTasks.get(2));
+ mRecentTasks.add(mTasks.get(3));
+ mRecentTasks.add(mTasks.get(4));
+ mCallbacksRecorder.clear();
+
+ // Freeze the list
+ mRecentTasks.setFreezeTaskListReordering();
+ assertTrue(mRecentTasks.isFreezeTaskListReorderingSet());
+
+ // Relaunch a few tasks
+ mRecentTasks.add(mTasks.get(3));
+ mRecentTasks.add(mTasks.get(2));
+
+ // Commit the task ordering with a specific task focused
+ mRecentTasks.resetFreezeTaskListReordering(mTasks.get(2));
+ assertFalse(mRecentTasks.isFreezeTaskListReorderingSet());
+
+ // Ensure that the order of the task list is the same as before, but with the focused task
+ // at the front
+ assertRecentTasksOrder(mTasks.get(2),
+ mTasks.get(4),
+ mTasks.get(3),
+ mTasks.get(1),
+ mTasks.get(0));
+
+ assertThat(mCallbacksRecorder.mAdded).isEmpty();
+ assertThat(mCallbacksRecorder.mTrimmed).isEmpty();
+ assertThat(mCallbacksRecorder.mRemoved).isEmpty();
+ }
+
+ @Test
+ public void testFreezeTaskListOrder_addRemoveTasks() {
+ // Add some tasks
+ mRecentTasks.add(mTasks.get(0));
+ mRecentTasks.add(mTasks.get(1));
+ mRecentTasks.add(mTasks.get(2));
+ mCallbacksRecorder.clear();
+
+ // Freeze the list
+ mRecentTasks.setFreezeTaskListReordering();
+ assertTrue(mRecentTasks.isFreezeTaskListReorderingSet());
+
+ // Add and remove some tasks
+ mRecentTasks.add(mTasks.get(3));
+ mRecentTasks.add(mTasks.get(4));
+ mRecentTasks.remove(mTasks.get(0));
+ mRecentTasks.remove(mTasks.get(1));
+
+ // Unfreeze the list
+ mRecentTasks.resetFreezeTaskListReordering(null);
+ assertFalse(mRecentTasks.isFreezeTaskListReorderingSet());
+
+ // Ensure that the order of the task list accounts for the added and removed tasks (added
+ // at the end)
+ assertRecentTasksOrder(mTasks.get(4),
+ mTasks.get(3),
+ mTasks.get(2));
+
+ assertThat(mCallbacksRecorder.mAdded).hasSize(2);
+ assertThat(mCallbacksRecorder.mAdded).contains(mTasks.get(3));
+ assertThat(mCallbacksRecorder.mAdded).contains(mTasks.get(4));
+ assertThat(mCallbacksRecorder.mRemoved).hasSize(2);
+ assertThat(mCallbacksRecorder.mRemoved).contains(mTasks.get(0));
+ assertThat(mCallbacksRecorder.mRemoved).contains(mTasks.get(1));
+ }
+
+ @Test
+ public void testFreezeTaskListOrder_timeout() {
+ // Add some tasks
+ mRecentTasks.add(mTasks.get(0));
+ mRecentTasks.add(mTasks.get(1));
+ mRecentTasks.add(mTasks.get(2));
+ mRecentTasks.add(mTasks.get(3));
+ mRecentTasks.add(mTasks.get(4));
+
+ // Freeze the list
+ long freezeTime = SystemClock.elapsedRealtime();
+ mRecentTasks.setFreezeTaskListReordering();
+ assertTrue(mRecentTasks.isFreezeTaskListReorderingSet());
+
+ // Relaunch a few tasks
+ mRecentTasks.add(mTasks.get(2));
+ mRecentTasks.add(mTasks.get(1));
+
+ // Override the freeze timeout params to simulate the timeout (simulate the freeze at 100ms
+ // ago with a timeout of 1ms)
+ mRecentTasks.setFreezeTaskListTimeoutParams(freezeTime - 100, 1);
+
+ ActivityStack stack = mTasks.get(2).getStack();
+ stack.moveToFront("", mTasks.get(2));
+ doReturn(stack).when(mTestService.mRootActivityContainer).getTopDisplayFocusedStack();
+ mRecentTasks.resetFreezeTaskListReorderingOnTimeout();
+ assertFalse(mRecentTasks.isFreezeTaskListReorderingSet());
+
+ // Ensure that the order of the task list is the same as before, but with the focused task
+ // at the front
+ assertRecentTasksOrder(mTasks.get(2),
+ mTasks.get(4),
+ mTasks.get(3),
+ mTasks.get(1),
+ mTasks.get(0));
+ }
+
+ @Test
public void testBackStackTasks_expectNoTrim() {
mRecentTasks.setParameters(-1 /* min */, 1 /* max */, -1 /* ms */);
@@ -721,6 +829,18 @@ public class RecentTasksTest extends ActivityTestsBase {
true /* showRecents */));
}
+ /**
+ * Ensures that the recent tasks list is in the provided order. Note that the expected tasks
+ * should be ordered from least to most recent.
+ */
+ private void assertRecentTasksOrder(TaskRecord... expectedTasks) {
+ ArrayList<TaskRecord> tasks = mRecentTasks.getRawTasks();
+ assertTrue(expectedTasks.length == tasks.size());
+ for (int i = 0; i < tasks.size(); i++) {
+ assertTrue(expectedTasks[i] == tasks.get(i));
+ }
+ }
+
private void assertNotRestoreTask(Runnable action) {
// Verify stack count doesn't change because task with fullscreen mode and standard type
// would have its own stack.