Expose method to clear all visible recent tasks
- Atomically remove all the visible tasks so that SysUI doesn't need to
remove each task individually.
Bug: 80471073
Test: atest FrameworksServicesTests:RecentTasksTest#testRemoveAllVisibleTasks
Change-Id: I23a6e152e94d5462948ab40adc9d7baf593847e1
diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java
index 398644af..2f18b89 100644
--- a/core/java/android/app/ActivityTaskManager.java
+++ b/core/java/android/app/ActivityTaskManager.java
@@ -204,6 +204,19 @@
}
/**
+ * Removes all visible recent tasks from the system.
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.REMOVE_TASKS)
+ public void removeAllVisibleRecentTasks() {
+ try {
+ getService().removeAllVisibleRecentTasks();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Return the maximum number of recents entries that we will maintain and show.
* @hide
*/
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index ece7f83..46664c6 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -139,6 +139,7 @@
ComponentName getCallingActivity(in IBinder token);
void setFocusedTask(int taskId);
boolean removeTask(int taskId);
+ void removeAllVisibleRecentTasks();
List<ActivityManager.RunningTaskInfo> getTasks(int maxNum);
List<ActivityManager.RunningTaskInfo> getFilteredTasks(int maxNum, int ignoreActivityType,
int ignoreWindowingMode);
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
index dce72b4..0ded963 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
@@ -394,6 +394,22 @@
}
/**
+ * Removes all the recent tasks.
+ */
+ public void removeAllRecentTasks() {
+ mBackgroundExecutor.submit(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ ActivityTaskManager.getService().removeAllVisibleRecentTasks();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to remove all tasks", e);
+ }
+ }
+ });
+ }
+
+ /**
* Cancels the current window transtion to/from Recents for the given task id.
*/
public void cancelWindowTransition(int taskId) {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index d1585cf..609ad75 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -3230,12 +3230,12 @@
}
@Override
- public void onRecentTaskRemoved(TaskRecord task, boolean wasTrimmed) {
+ public void onRecentTaskRemoved(TaskRecord task, boolean wasTrimmed, boolean killProcess) {
if (wasTrimmed) {
// Task was trimmed from the recent tasks list -- remove the active task record as well
// since the user won't really be able to go back to it
- removeTaskByIdLocked(task.taskId, false /* killProcess */,
- false /* removeFromRecents */, !PAUSE_IMMEDIATELY, "recent-task-trimmed");
+ removeTaskByIdLocked(task.taskId, killProcess, false /* removeFromRecents */,
+ !PAUSE_IMMEDIATELY, "recent-task-trimmed");
}
task.removedFromRecents();
}
diff --git a/services/core/java/com/android/server/am/ActivityTaskManagerService.java b/services/core/java/com/android/server/am/ActivityTaskManagerService.java
index 3b3263c..8ab4a37 100644
--- a/services/core/java/com/android/server/am/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityTaskManagerService.java
@@ -1668,6 +1668,19 @@
}
@Override
+ public void removeAllVisibleRecentTasks() {
+ enforceCallerIsRecentsOrHasPermission(REMOVE_TASKS, "removeAllVisibleRecentTasks()");
+ synchronized (mGlobalLock) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ getRecentTasks().removeAllVisibleTasks();
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
+ @Override
public boolean shouldUpRecreateTask(IBinder token, String destAffinity) {
synchronized (mGlobalLock) {
final ActivityRecord srec = ActivityRecord.forTokenLocked(token);
diff --git a/services/core/java/com/android/server/am/RecentTasks.java b/services/core/java/com/android/server/am/RecentTasks.java
index fb6b5c1..e11e003 100644
--- a/services/core/java/com/android/server/am/RecentTasks.java
+++ b/services/core/java/com/android/server/am/RecentTasks.java
@@ -106,7 +106,6 @@
private static final String TAG = TAG_WITH_CLASS_NAME ? "RecentTasks" : TAG_AM;
private static final String TAG_RECENTS = TAG + POSTFIX_RECENTS;
private static final String TAG_TASKS = TAG + POSTFIX_TASKS;
- private static final boolean TRIMMED = true;
private static final int DEFAULT_INITIAL_CAPACITY = 5;
@@ -134,7 +133,7 @@
/**
* Called when a task is removed from the recent tasks list.
*/
- void onRecentTaskRemoved(TaskRecord task, boolean wasTrimmed);
+ void onRecentTaskRemoved(TaskRecord task, boolean wasTrimmed, boolean killProcess);
}
/**
@@ -322,9 +321,9 @@
}
}
- private void notifyTaskRemoved(TaskRecord task, boolean wasTrimmed) {
+ private void notifyTaskRemoved(TaskRecord task, boolean wasTrimmed, boolean killProcess) {
for (int i = 0; i < mCallbacks.size(); i++) {
- mCallbacks.get(i).onRecentTaskRemoved(task, wasTrimmed);
+ mCallbacks.get(i).onRecentTaskRemoved(task, wasTrimmed, killProcess);
}
}
@@ -547,6 +546,16 @@
}
}
+ void removeAllVisibleTasks() {
+ for (int i = mTasks.size() - 1; i >= 0; --i) {
+ final TaskRecord tr = mTasks.get(i);
+ if (isVisibleRecentTask(tr)) {
+ mTasks.remove(i);
+ notifyTaskRemoved(tr, true /* wasTrimmed */, true /* killProcess */);
+ }
+ }
+ }
+
void cleanupDisabledPackageTasksLocked(String packageName, Set<String> filterByClasses,
int userId) {
for (int i = mTasks.size() - 1; i >= 0; --i) {
@@ -1048,7 +1057,7 @@
*/
void remove(TaskRecord task) {
mTasks.remove(task);
- notifyTaskRemoved(task, !TRIMMED);
+ notifyTaskRemoved(task, false /* wasTrimmed */, false /* killProcess */);
}
/**
@@ -1060,7 +1069,7 @@
// Remove from the end of the list until we reach the max number of recents
while (recentsCount > mGlobalMaxNumTasks) {
final TaskRecord tr = mTasks.remove(recentsCount - 1);
- notifyTaskRemoved(tr, TRIMMED);
+ notifyTaskRemoved(tr, true /* wasTrimmed */, false /* killProcess */);
recentsCount--;
if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "Trimming over max-recents task=" + tr
+ " max=" + mGlobalMaxNumTasks);
@@ -1114,7 +1123,7 @@
// Task is no longer active, trim it from the list
mTasks.remove(task);
- notifyTaskRemoved(task, TRIMMED);
+ notifyTaskRemoved(task, true /* wasTrimmed */, false /* killProcess */);
notifyTaskPersisterLocked(task, false /* flush */);
}
}
@@ -1268,7 +1277,7 @@
// callbacks here.
final TaskRecord removedTask = mTasks.remove(removeIndex);
if (removedTask != task) {
- notifyTaskRemoved(removedTask, !TRIMMED);
+ notifyTaskRemoved(removedTask, false /* wasTrimmed */, false /* killProcess */);
if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "Trimming task=" + removedTask
+ " for addition of task=" + task);
}
diff --git a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
index 227a70f..ee484d6 100644
--- a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
@@ -17,6 +17,7 @@
package com.android.server.am;
import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
@@ -43,6 +44,7 @@
import android.app.ActivityManager.RecentTaskInfo;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityTaskManager;
+import android.app.WindowConfiguration;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -51,7 +53,6 @@
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Bundle;
-import android.os.Debug;
import android.os.Looper;
import android.os.RemoteException;
import android.os.SystemClock;
@@ -276,13 +277,11 @@
public void testAddTaskCompatibleActivityType_expectRemove() throws Exception {
// Test with undefined activity type since the type is not persisted by the task persister
// and we want to ensure that a new task will match a restored task
- Configuration config1 = new Configuration();
- config1.windowConfiguration.setActivityType(ACTIVITY_TYPE_UNDEFINED);
TaskRecord task1 = createTaskBuilder(".Task1")
.setFlags(FLAG_ACTIVITY_NEW_TASK)
.setStack(mStack)
.build();
- task1.onConfigurationChanged(config1);
+ setTaskActivityType(task1, ACTIVITY_TYPE_UNDEFINED);
assertTrue(task1.getActivityType() == ACTIVITY_TYPE_UNDEFINED);
mRecentTasks.add(task1);
mCallbacksRecorder.clear();
@@ -302,14 +301,12 @@
@Test
public void testAddTaskCompatibleActivityTypeDifferentUser_expectNoRemove() throws Exception {
- Configuration config1 = new Configuration();
- config1.windowConfiguration.setActivityType(ACTIVITY_TYPE_UNDEFINED);
TaskRecord task1 = createTaskBuilder(".Task1")
.setFlags(FLAG_ACTIVITY_NEW_TASK)
.setStack(mStack)
.setUserId(TEST_USER_0_ID)
.build();
- task1.onConfigurationChanged(config1);
+ setTaskActivityType(task1, ACTIVITY_TYPE_UNDEFINED);
assertTrue(task1.getActivityType() == ACTIVITY_TYPE_UNDEFINED);
mRecentTasks.add(task1);
mCallbacksRecorder.clear();
@@ -329,24 +326,20 @@
@Test
public void testAddTaskCompatibleWindowingMode_expectRemove() throws Exception {
- Configuration config1 = new Configuration();
- config1.windowConfiguration.setWindowingMode(WINDOWING_MODE_UNDEFINED);
TaskRecord task1 = createTaskBuilder(".Task1")
.setFlags(FLAG_ACTIVITY_NEW_TASK)
.setStack(mStack)
.build();
- task1.onConfigurationChanged(config1);
+ setTaskWindowingMode(task1, WINDOWING_MODE_UNDEFINED);
assertTrue(task1.getWindowingMode() == WINDOWING_MODE_UNDEFINED);
mRecentTasks.add(task1);
mCallbacksRecorder.clear();
- Configuration config2 = new Configuration();
- config2.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
TaskRecord task2 = createTaskBuilder(".Task1")
.setFlags(FLAG_ACTIVITY_NEW_TASK)
.setStack(mStack)
.build();
- task2.onConfigurationChanged(config2);
+ setTaskWindowingMode(task2, WINDOWING_MODE_FULLSCREEN);
assertTrue(task2.getWindowingMode() == WINDOWING_MODE_FULLSCREEN);
mRecentTasks.add(task2);
@@ -359,23 +352,19 @@
@Test
public void testAddTaskIncompatibleWindowingMode_expectNoRemove() throws Exception {
- Configuration config1 = new Configuration();
- config1.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
TaskRecord task1 = createTaskBuilder(".Task1")
.setFlags(FLAG_ACTIVITY_NEW_TASK)
.setStack(mStack)
.build();
- task1.onConfigurationChanged(config1);
+ setTaskWindowingMode(task1, WINDOWING_MODE_FULLSCREEN);
assertTrue(task1.getWindowingMode() == WINDOWING_MODE_FULLSCREEN);
mRecentTasks.add(task1);
- Configuration config2 = new Configuration();
- config2.windowConfiguration.setWindowingMode(WINDOWING_MODE_PINNED);
TaskRecord task2 = createTaskBuilder(".Task1")
.setFlags(FLAG_ACTIVITY_NEW_TASK)
.setStack(mStack)
.build();
- task2.onConfigurationChanged(config2);
+ setTaskWindowingMode(task2, WINDOWING_MODE_PINNED);
assertTrue(task2.getWindowingMode() == WINDOWING_MODE_PINNED);
mRecentTasks.add(task2);
@@ -644,6 +633,43 @@
}
@Test
+ public void testRemoveAllVisibleTasks() throws Exception {
+ mRecentTasks.setParameters(-1 /* min */, 3 /* max */, 100 /* ms */);
+
+ // Create some set of tasks, some of which are visible and some are not
+ TaskRecord t1 = createTaskBuilder("com.android.pkg1", ".Task1").build();
+ mRecentTasks.add(t1);
+ mRecentTasks.add(setTaskActivityType(
+ createTaskBuilder("com.android.pkg1", ".HomeTask").build(),
+ ACTIVITY_TYPE_HOME));
+ TaskRecord t2 = createTaskBuilder("com.android.pkg2", ".Task2").build();
+ mRecentTasks.add(t2);
+ mRecentTasks.add(setTaskWindowingMode(
+ createTaskBuilder("com.android.pkg1", ".PipTask").build(),
+ WINDOWING_MODE_PINNED));
+ TaskRecord t3 = createTaskBuilder("com.android.pkg3", ".Task3").build();
+ mRecentTasks.add(t3);
+
+ // Create some more tasks that are out of visible range, but are still visible
+ TaskRecord t4 = createTaskBuilder("com.android.pkg3", ".Task4").build();
+ mRecentTasks.add(t4);
+ TaskRecord t5 = createTaskBuilder("com.android.pkg3", ".Task5").build();
+ mRecentTasks.add(t5);
+
+ // Create some more tasks that are out of the active session range, but are still visible
+ TaskRecord t6 = createTaskBuilder("com.android.pkg3", ".Task6").build();
+ t6.lastActiveTime = SystemClock.elapsedRealtime() - 200;
+ mRecentTasks.add(t6);
+ TaskRecord t7 = createTaskBuilder("com.android.pkg3", ".Task7").build();
+ t7.lastActiveTime = SystemClock.elapsedRealtime() - 200;
+ mRecentTasks.add(t7);
+
+ // Remove all the visible tasks and ensure that they are removed
+ mRecentTasks.removeAllVisibleTasks();
+ assertTrimmed(t1, t2, t3, t4, t5, t6, t7);
+ }
+
+ @Test
public void testNotRecentsComponent_denyApiAccess() throws Exception {
doReturn(PackageManager.PERMISSION_DENIED).when(mService)
.checkGetTasksPermission(anyString(), anyInt(), anyInt());
@@ -754,6 +780,22 @@
return task;
}
+ private TaskRecord setTaskActivityType(TaskRecord task,
+ @WindowConfiguration.ActivityType int activityType) {
+ Configuration config1 = new Configuration();
+ config1.windowConfiguration.setActivityType(activityType);
+ task.onConfigurationChanged(config1);
+ return task;
+ }
+
+ private TaskRecord setTaskWindowingMode(TaskRecord task,
+ @WindowConfiguration.WindowingMode int windowingMode) {
+ Configuration config1 = new Configuration();
+ config1.windowConfiguration.setWindowingMode(windowingMode);
+ task.onConfigurationChanged(config1);
+ return task;
+ }
+
private boolean arrayContainsUser(int[] userIds, int targetUserId) {
Arrays.sort(userIds);
return Arrays.binarySearch(userIds, targetUserId) >= 0;
@@ -880,7 +922,7 @@
}
@Override
- public void onRecentTaskRemoved(TaskRecord task, boolean wasTrimmed) {
+ public void onRecentTaskRemoved(TaskRecord task, boolean wasTrimmed, boolean killProcess) {
if (wasTrimmed) {
trimmed.add(task);
}