diff options
7 files changed, 125 insertions, 31 deletions
diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java index 398644afd17a..2f18b89d9c69 100644 --- a/core/java/android/app/ActivityTaskManager.java +++ b/core/java/android/app/ActivityTaskManager.java @@ -204,6 +204,19 @@ public class ActivityTaskManager { } /** + * 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 ece7f839381d..46664c61fbb9 100644 --- a/core/java/android/app/IActivityTaskManager.aidl +++ b/core/java/android/app/IActivityTaskManager.aidl @@ -139,6 +139,7 @@ interface IActivityTaskManager { 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 dce72b440f58..0ded9631cb3d 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 @@ public class ActivityManagerWrapper { } /** + * 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 d1585cf8fb48..609ad752a3dc 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 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } @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 748b2d2d612a..f2b097b28efd 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 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } @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 fb6b5c1f05ec..e11e00368a78 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 @@ class RecentTasks { 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 @@ class RecentTasks { /** * 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 @@ class RecentTasks { } } - 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 @@ class RecentTasks { } } + 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 @@ class RecentTasks { */ void remove(TaskRecord task) { mTasks.remove(task); - notifyTaskRemoved(task, !TRIMMED); + notifyTaskRemoved(task, false /* wasTrimmed */, false /* killProcess */); } /** @@ -1060,7 +1069,7 @@ class RecentTasks { // 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 @@ class RecentTasks { // 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 @@ class RecentTasks { // 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 227a70f7a5a3..ee484d6a5a50 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 static java.lang.Integer.MAX_VALUE; 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.pm.UserInfo; 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 class RecentTasksTest extends ActivityTestsBase { 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 @@ public class RecentTasksTest extends ActivityTestsBase { @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 @@ public class RecentTasksTest extends ActivityTestsBase { @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 @@ public class RecentTasksTest extends ActivityTestsBase { @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 @@ public class RecentTasksTest extends ActivityTestsBase { } @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 @@ public class RecentTasksTest extends ActivityTestsBase { 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 @@ public class RecentTasksTest extends ActivityTestsBase { } @Override - public void onRecentTaskRemoved(TaskRecord task, boolean wasTrimmed) { + public void onRecentTaskRemoved(TaskRecord task, boolean wasTrimmed, boolean killProcess) { if (wasTrimmed) { trimmed.add(task); } |