Remove invisible replaced recent task when going to home
There may be multiple tasks with the same base intent by
launching activity with intent flags (FLAG_ACTIVITY_NEW_TASK
| FLAG_ACTIVITY_MULTIPLE_TASK). Since the recents list only
stores the last task for the same task affinity or non-document
identical intent, if the previous task still contains activities,
it may be a leakage.
Bug: 142986867
Test: atest RecentTasksTest# \
testAddTasksHomeClearUntrackedTasks_expectFinish
Change-Id: Ic6b42f53af31cc574994f1eca4977559cb2db072
This commit is contained in:
@@ -173,6 +173,9 @@ class RecentTasks {
|
||||
private final ArrayList<Task> mTasks = new ArrayList<>();
|
||||
private final ArrayList<Callbacks> mCallbacks = new ArrayList<>();
|
||||
|
||||
/** The non-empty tasks that are removed from recent tasks (see {@link #removeForAddTask}). */
|
||||
private final ArrayList<Task> mHiddenTasks = new ArrayList<>();
|
||||
|
||||
// These values are generally loaded from resources, but can be set dynamically in the tests
|
||||
private boolean mHasVisibleRecentTasks;
|
||||
private int mGlobalMaxNumTasks;
|
||||
@@ -1024,6 +1027,12 @@ class RecentTasks {
|
||||
void add(Task task) {
|
||||
if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "add: task=" + task);
|
||||
|
||||
// Clean up the hidden tasks when going to home because the user may not be unable to return
|
||||
// to the task from recents.
|
||||
if (!mHiddenTasks.isEmpty() && task.isActivityTypeHome()) {
|
||||
removeUnreachableHiddenTasks(task.getWindowingMode());
|
||||
}
|
||||
|
||||
final boolean isAffiliated = task.mAffiliatedTaskId != task.mTaskId
|
||||
|| task.mNextAffiliateTaskId != INVALID_TASK_ID
|
||||
|| task.mPrevAffiliateTaskId != INVALID_TASK_ID;
|
||||
@@ -1390,6 +1399,28 @@ class RecentTasks {
|
||||
return display.getIndexOf(stack) < display.getIndexOf(display.getRootHomeTask());
|
||||
}
|
||||
|
||||
/** Remove the tasks that user may not be able to return. */
|
||||
private void removeUnreachableHiddenTasks(int windowingMode) {
|
||||
for (int i = mHiddenTasks.size() - 1; i >= 0; i--) {
|
||||
final Task hiddenTask = mHiddenTasks.get(i);
|
||||
if (!hiddenTask.hasChild()) {
|
||||
// The task was removed by other path.
|
||||
mHiddenTasks.remove(i);
|
||||
continue;
|
||||
}
|
||||
if (hiddenTask.getWindowingMode() != windowingMode
|
||||
|| hiddenTask.getTopVisibleActivity() != null) {
|
||||
// The task may be reachable from the back stack of other windowing mode or it is
|
||||
// currently in use. Keep the task in the hidden list to avoid losing track, e.g.
|
||||
// after dismissing primary split screen.
|
||||
continue;
|
||||
}
|
||||
mHiddenTasks.remove(i);
|
||||
mSupervisor.removeTask(hiddenTask, false /* killProcess */,
|
||||
!REMOVE_FROM_RECENTS, "remove-hidden-task");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If needed, remove oldest existing entries in recents that are for the same kind
|
||||
* of task as the given one.
|
||||
@@ -1406,6 +1437,14 @@ class RecentTasks {
|
||||
// callbacks here.
|
||||
final Task removedTask = mTasks.remove(removeIndex);
|
||||
if (removedTask != task) {
|
||||
// The added task is in recents so it is not hidden.
|
||||
mHiddenTasks.remove(task);
|
||||
if (removedTask.hasChild()) {
|
||||
// A non-empty task is replaced by a new task. Because the removed task is no longer
|
||||
// managed by the recent tasks list, add it to the hidden list to prevent the task
|
||||
// from becoming dangling.
|
||||
mHiddenTasks.add(removedTask);
|
||||
}
|
||||
notifyTaskRemoved(removedTask, false /* wasTrimmed */, false /* killProcess */);
|
||||
if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "Trimming task=" + removedTask
|
||||
+ " for addition of task=" + task);
|
||||
@@ -1662,6 +1701,9 @@ class RecentTasks {
|
||||
pw.println("mFreezeTaskListReordering=" + mFreezeTaskListReordering);
|
||||
pw.println("mFreezeTaskListReorderingPendingTimeout="
|
||||
+ mService.mH.hasCallbacks(mResetFreezeTaskListOnTimeoutRunnable));
|
||||
if (!mHiddenTasks.isEmpty()) {
|
||||
pw.println("mHiddenTasks=" + mHiddenTasks);
|
||||
}
|
||||
if (mTasks.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -39,6 +39,8 @@ import static com.google.common.truth.Truth.assertWithMessage;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
@@ -83,6 +85,7 @@ import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Build/Install/Run:
|
||||
@@ -418,6 +421,36 @@ public class RecentTasksTest extends ActivityTestsBase {
|
||||
assertThat(mCallbacksRecorder.mRemoved).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddTasksHomeClearUntrackedTasks_expectFinish() {
|
||||
// There may be multiple tasks with the same base intent by flags (FLAG_ACTIVITY_NEW_TASK |
|
||||
// FLAG_ACTIVITY_MULTIPLE_TASK). If the previous task is still active, it should be removed
|
||||
// because user may not be able to return to the task.
|
||||
final String className = ".PermissionsReview";
|
||||
final Function<Boolean, Task> taskBuilder = visible -> {
|
||||
final Task task = createTaskBuilder(className).build();
|
||||
// Make the task non-empty.
|
||||
final ActivityRecord r = new ActivityBuilder(mService).setTask(task).build();
|
||||
r.setVisibility(visible);
|
||||
return task;
|
||||
};
|
||||
|
||||
final Task task1 = taskBuilder.apply(false /* visible */);
|
||||
mRecentTasks.add(task1);
|
||||
final Task task2 = taskBuilder.apply(true /* visible */);
|
||||
mRecentTasks.add(task2);
|
||||
// Only the last task is kept in recents and the previous 2 tasks will becomes untracked
|
||||
// tasks because their intents are identical.
|
||||
mRecentTasks.add(createTaskBuilder(className).build());
|
||||
// Go home to trigger the removal of untracked tasks.
|
||||
mRecentTasks.add(createTaskBuilder(".Home").setStack(mDisplay.getRootHomeTask()).build());
|
||||
|
||||
// All activities in the invisible task should be finishing or removed.
|
||||
assertNull(task1.getTopNonFinishingActivity());
|
||||
// The visible task should not be affected.
|
||||
assertNotNull(task2.getTopNonFinishingActivity());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUsersTasks() {
|
||||
mRecentTasks.setOnlyTestVisibleRange();
|
||||
|
||||
Reference in New Issue
Block a user