diff --git a/core/java/android/app/ITaskStackListener.aidl b/core/java/android/app/ITaskStackListener.aidl index 650147b45bd24..61867ea737e77 100644 --- a/core/java/android/app/ITaskStackListener.aidl +++ b/core/java/android/app/ITaskStackListener.aidl @@ -185,4 +185,9 @@ oneway interface ITaskStackListener { * @param newDisplayId id of the new display. */ void onTaskDisplayChanged(int taskId, int newDisplayId); + + /** + * Called when any additions or deletions to the recent tasks list have been made. + */ + void onRecentTaskListUpdated(); } diff --git a/core/java/android/app/TaskStackListener.java b/core/java/android/app/TaskStackListener.java index b63feb590ad13..e3a0e11c3c685 100644 --- a/core/java/android/app/TaskStackListener.java +++ b/core/java/android/app/TaskStackListener.java @@ -182,4 +182,8 @@ public abstract class TaskStackListener extends ITaskStackListener.Stub { @Override public void onTaskDisplayChanged(int taskId, int newDisplayId) throws RemoteException { } + + @Override + public void onRecentTaskListUpdated() throws RemoteException { + } } diff --git a/core/java/android/util/LruCache.java b/core/java/android/util/LruCache.java index f04e7cbc9e8fc..3cbf727d7718a 100644 --- a/core/java/android/util/LruCache.java +++ b/core/java/android/util/LruCache.java @@ -17,6 +17,7 @@ package android.util; import android.annotation.UnsupportedAppUsage; + import java.util.LinkedHashMap; import java.util.Map; @@ -260,7 +261,7 @@ public class LruCache { * @param evicted true if the entry is being removed to make space, false * if the removal was caused by a {@link #put} or {@link #remove}. * @param newValue the new value for {@code key}, if it exists. If non-null, - * this removal was caused by a {@link #put}. Otherwise it was caused by + * this removal was caused by a {@link #put} or a {@link #get}. Otherwise it was caused by * an eviction or a {@link #remove}. */ protected void entryRemoved(boolean evicted, K key, V oldValue, V newValue) {} diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskKeyCache.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskKeyCache.java index 342cb75b2c14f..8a244bf81c7cc 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskKeyCache.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskKeyCache.java @@ -21,6 +21,9 @@ import android.util.SparseArray; import com.android.systemui.shared.recents.model.Task.TaskKey; +import java.util.ArrayList; +import java.util.Collection; + /** * Base class for both strong and LRU task key cache. */ @@ -76,6 +79,15 @@ public abstract class TaskKeyCache { mKeys.remove(key.id); } + /** @return {@link Collection} of {@link TaskKey} */ + public Collection getValues() { + Collection result = new ArrayList<>(mKeys.size()); + for (int i = 0; i < mKeys.size(); i++) { + result.add(mKeys.valueAt(i)); + } + return result; + } + /** Removes all the entries in the cache. */ public final synchronized void evictAll() { evictAllCache(); diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java index c215d0fc13d64..77571613f6af2 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java @@ -94,6 +94,11 @@ public abstract class TaskStackChangeListener { */ public void onTaskDisplayChanged(int taskId, int newDisplayId) { } + /** + * Called when any additions or deletions to the recent tasks list have been made. + */ + public void onRecentTaskListUpdated() { } + /** * Checks that the current user matches the process. Since * {@link android.app.ITaskStackListener} is not multi-user aware, handlers of diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java index d570a586f9613..a7f4396fabed8 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java @@ -213,6 +213,11 @@ public class TaskStackChangeListeners extends TaskStackListener { mHandler.obtainMessage(H.ON_TASK_DISPLAY_CHANGED, taskId, newDisplayId).sendToTarget(); } + @Override + public void onRecentTaskListUpdated() throws RemoteException { + mHandler.obtainMessage(H.ON_TASK_LIST_UPDATED).sendToTarget(); + } + private final class H extends Handler { private static final int ON_TASK_STACK_CHANGED = 1; private static final int ON_TASK_SNAPSHOT_CHANGED = 2; @@ -234,6 +239,7 @@ public class TaskStackChangeListeners extends TaskStackListener { private static final int ON_BACK_PRESSED_ON_TASK_ROOT = 18; private static final int ON_SINGLE_TASK_DISPLAY_DRAWN = 19; private static final int ON_TASK_DISPLAY_CHANGED = 20; + private static final int ON_TASK_LIST_UPDATED = 21; public H(Looper looper) { @@ -382,6 +388,12 @@ public class TaskStackChangeListeners extends TaskStackListener { } break; } + case ON_TASK_LIST_UPDATED: { + for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) { + mTaskStackListeners.get(i).onRecentTaskListUpdated(); + } + break; + } } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java index c67512c119225..f3201ec73d639 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java @@ -54,7 +54,6 @@ import com.android.systemui.R; import com.android.systemui.SysUiServiceProvider; import com.android.systemui.SystemUI; import com.android.systemui.UiOffloadThread; -import com.android.systemui.shared.system.TaskStackChangeListener; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.policy.KeyguardMonitor; import com.android.systemui.util.NotificationChannels; @@ -138,15 +137,6 @@ public class InstantAppNotifier extends SystemUI } }; - private final TaskStackChangeListener mTaskListener = - new TaskStackChangeListener() { - @Override - public void onTaskStackChanged() { - // Listen for changes to stacks and then check which instant apps are - // foreground. - updateForegroundInstantApps(); - } - }; private void updateForegroundInstantApps() { NotificationManager noMan = mContext.getSystemService(NotificationManager.class); diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java index 541a8bbc88656..fb6b5da87f2d6 100644 --- a/services/core/java/com/android/server/wm/RecentTasks.java +++ b/services/core/java/com/android/server/wm/RecentTasks.java @@ -126,6 +126,7 @@ class RecentTasks { // iterating through the recents list private static final ActivityInfo NO_ACTIVITY_INFO_TOKEN = new ActivityInfo(); private static final ApplicationInfo NO_APPLICATION_INFO_TOKEN = new ApplicationInfo(); + private TaskChangeNotificationController mTaskNotificationController; /** * Callbacks made when manipulating the list. @@ -228,6 +229,7 @@ class RecentTasks { mTaskPersister = taskPersister; mGlobalMaxNumTasks = ActivityTaskManager.getMaxRecentTasksStatic(); mHasVisibleRecentTasks = true; + mTaskNotificationController = service.getTaskChangeNotificationController(); } RecentTasks(ActivityTaskManagerService service, ActivityStackSupervisor stackSupervisor) { @@ -238,6 +240,7 @@ class RecentTasks { mTaskPersister = new TaskPersister(systemDir, stackSupervisor, service, this, stackSupervisor.mPersisterQueue); mGlobalMaxNumTasks = ActivityTaskManager.getMaxRecentTasksStatic(); + mTaskNotificationController = service.getTaskChangeNotificationController(); mHasVisibleRecentTasks = res.getBoolean(com.android.internal.R.bool.config_hasRecents); loadParametersFromResources(res); } @@ -298,7 +301,7 @@ class RecentTasks { // Resume trimming tasks trimInactiveRecentTasks(); - mService.getTaskChangeNotificationController().notifyTaskStackChanged(); + mTaskNotificationController.notifyTaskStackChanged(); } /** @@ -427,12 +430,14 @@ class RecentTasks { for (int i = 0; i < mCallbacks.size(); i++) { mCallbacks.get(i).onRecentTaskAdded(task); } + mTaskNotificationController.notifyTaskListUpdated(); } private void notifyTaskRemoved(TaskRecord task, boolean wasTrimmed, boolean killProcess) { for (int i = 0; i < mCallbacks.size(); i++) { mCallbacks.get(i).onRecentTaskRemoved(task, wasTrimmed, killProcess); } + mTaskNotificationController.notifyTaskListUpdated(); } /** diff --git a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java index f776062b31a11..c2c4767419638 100644 --- a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java +++ b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java @@ -56,6 +56,7 @@ class TaskChangeNotificationController { private static final int NOTIFY_BACK_PRESSED_ON_TASK_ROOT = 21; private static final int NOTIFY_SINGLE_TASK_DISPLAY_DRAWN = 22; private static final int NOTIFY_TASK_DISPLAY_CHANGED_LISTENERS_MSG = 23; + private static final int NOTIFY_TASK_LIST_UPDATED_LISTENERS_MSG = 24; // Delay in notifying task stack change listeners (in millis) private static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_DELAY = 100; @@ -164,6 +165,10 @@ class TaskChangeNotificationController { l.onTaskDisplayChanged(m.arg1, m.arg2); }; + private final TaskStackConsumer mNotifyTaskListUpdated = (l, m) -> { + l.onRecentTaskListUpdated(); + }; + @FunctionalInterface public interface TaskStackConsumer { void accept(ITaskStackListener t, Message m) throws RemoteException; @@ -249,6 +254,9 @@ class TaskChangeNotificationController { case NOTIFY_TASK_DISPLAY_CHANGED_LISTENERS_MSG: forAllRemoteListeners(mNotifyTaskDisplayChanged, msg); break; + case NOTIFY_TASK_LIST_UPDATED_LISTENERS_MSG: + forAllRemoteListeners(mNotifyTaskListUpdated, msg); + break; } } } @@ -513,4 +521,13 @@ class TaskChangeNotificationController { forAllLocalListeners(mNotifyTaskStackChanged, msg); msg.sendToTarget(); } + + /** + * Called when any additions or deletions to the recent tasks list have been made. + */ + void notifyTaskListUpdated() { + final Message msg = mHandler.obtainMessage(NOTIFY_TASK_LIST_UPDATED_LISTENERS_MSG); + forAllLocalListeners(mNotifyTaskListUpdated, msg); + msg.sendToTarget(); + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java index ecf3acd32d4f9..e673a627dcd67 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java @@ -467,6 +467,7 @@ class ActivityTestsBase { spyOn(getLifecycleManager()); spyOn(getLockTaskController()); + spyOn(getTaskChangeNotificationController()); doReturn(mock(IPackageManager.class)).when(this).getPackageManager(); // allow background activity starts by default doReturn(true).when(this).isBackgroundActivityStartsEnabled(); 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 a1999c9017028..b7a85d7bb5b7a 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java @@ -43,6 +43,8 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; import static java.lang.Integer.MAX_VALUE; @@ -900,6 +902,46 @@ public class RecentTasksTest extends ActivityTestsBase { true /* showRecents */)); } + @Test + public void addTask_callsTaskNotificationController() { + final TaskRecord task = createTaskBuilder(".Task").build(); + + mRecentTasks.add(task); + mRecentTasks.remove(task); + + TaskChangeNotificationController controller = + mTestService.getTaskChangeNotificationController(); + verify(controller, times(2)).notifyTaskListUpdated(); + } + + @Test + public void removeTask_callsTaskNotificationController() { + final TaskRecord task = createTaskBuilder(".Task").build(); + + mRecentTasks.add(task); + mRecentTasks.remove(task); + + // 2 calls - Once for add and once for remove + TaskChangeNotificationController controller = + mTestService.getTaskChangeNotificationController(); + verify(controller, times(2)).notifyTaskListUpdated(); + } + + @Test + public void removeALlVisibleTask_callsTaskNotificationController_twice() { + final TaskRecord task1 = createTaskBuilder(".Task").build(); + final TaskRecord task2 = createTaskBuilder(".Task2").build(); + + mRecentTasks.add(task1); + mRecentTasks.add(task2); + mRecentTasks.removeAllVisibleTasks(TEST_USER_0_ID); + + // 4 calls - Twice for add and twice for remove + TaskChangeNotificationController controller = + mTestService.getTaskChangeNotificationController(); + verify(controller, times(4)).notifyTaskListUpdated(); + } + /** * Ensures that the raw recent tasks list is in the provided order. Note that the expected tasks * should be ordered from least to most recent.