Support adding task listeners for incoming tasks
- The task/transition paths currently have no guaranteed order, if
a task listener is registered before a task appears, we store it
as a pending listener which will be fully registered once the task
next appears. This pending listener is always removed whenever the
listener is unregistered, and priority of the launch cookie is >
than the pending listener (and the pending listener will be removed
if a launch cookie listener is used).
Bug: 385674612
Flag: EXEMPT bugfix
Test: atest ShellTaskOrganizerTests
Change-Id: Id07d7df4be90beb9271ba0de772a41814e3bf728
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
index 9a0e6ae..7821358e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -171,6 +171,9 @@
/** @see #setPendingLaunchCookieListener */
private final ArrayMap<IBinder, TaskListener> mLaunchCookieToListener = new ArrayMap<>();
+ /** @see #setPendingTaskListener(int, TaskListener) */
+ private final ArrayMap<Integer, TaskListener> mPendingTaskToListener = new ArrayMap<>();
+
// Keeps track of taskId's with visible locusIds. Used to notify any {@link LocusIdListener}s
// that might be set.
private final SparseArray<LocusId> mVisibleTasksWithLocusId = new SparseArray<>();
@@ -365,7 +368,7 @@
}
/**
- * Adds a listener for a specific task id.
+ * Adds a listener for a specific task id. This only applies if
*/
public void addListenerForTaskId(TaskListener listener, int taskId) {
synchronized (mLock) {
@@ -383,7 +386,12 @@
final TaskAppearedInfo info = mTasks.get(taskId);
if (info == null) {
- throw new IllegalArgumentException("addListenerForTaskId unknown taskId=" + taskId);
+ ProtoLog.v(WM_SHELL_TASK_ORG, "Queueing pending listener");
+ // The caller may have received a transition with the task before the organizer
+ // was notified of the task appearing, so set a pending task listener for the
+ // task to be retrieved when the task actually appears
+ mPendingTaskToListener.put(taskId, listener);
+ return;
}
final TaskListener oldListener = getTaskListener(info.getTaskInfo());
@@ -423,6 +431,14 @@
public void removeListener(TaskListener listener) {
synchronized (mLock) {
ProtoLog.v(WM_SHELL_TASK_ORG, "Remove listener=%s", listener);
+
+ // Remove all occurrences of the pending listener
+ for (int i = mPendingTaskToListener.size() - 1; i >= 0; --i) {
+ if (mPendingTaskToListener.valueAt(i) == listener) {
+ mPendingTaskToListener.removeAt(i);
+ }
+ }
+
final int index = mTaskListeners.indexOfValue(listener);
if (index == -1) {
Log.w(TAG, "No registered listener found");
@@ -438,7 +454,7 @@
tasks.add(data);
}
- // Remove listener, there can be the multiple occurrences, so search the whole list.
+ // Remove occurrences of the listener
for (int i = mTaskListeners.size() - 1; i >= 0; --i) {
if (mTaskListeners.valueAt(i) == listener) {
mTaskListeners.removeAt(i);
@@ -456,9 +472,11 @@
/**
* Associated a listener to a pending launch cookie so we can route the task later once it
- * appears.
+ * appears. If both this and a pending task-id listener is set, then this will take priority.
*/
public void setPendingLaunchCookieListener(IBinder cookie, TaskListener listener) {
+ ProtoLog.v(WM_SHELL_TASK_ORG, "setPendingLaunchCookieListener(): cookie=%s listener=%s",
+ cookie, listener);
synchronized (mLock) {
mLaunchCookieToListener.put(cookie, listener);
}
@@ -904,7 +922,7 @@
}
private TaskListener getTaskListener(RunningTaskInfo runningTaskInfo,
- boolean removeLaunchCookieIfNeeded) {
+ boolean removePendingIfNeeded) {
final int taskId = runningTaskInfo.taskId;
TaskListener listener;
@@ -916,16 +934,35 @@
listener = mLaunchCookieToListener.get(cookie);
if (listener == null) continue;
- if (removeLaunchCookieIfNeeded) {
+ if (removePendingIfNeeded) {
ProtoLog.v(WM_SHELL_TASK_ORG, "Migrating cookie listener to task: taskId=%d",
- runningTaskInfo.taskId);
+ taskId);
// Remove the cookie and add the listener.
mLaunchCookieToListener.remove(cookie);
+ if (mPendingTaskToListener.containsKey(taskId)
+ && mPendingTaskToListener.get(taskId) != listener) {
+ Log.w(TAG, "Conflicting pending task listeners reported for taskId=" + taskId);
+ }
+ mPendingTaskToListener.remove(taskId);
mTaskListeners.put(taskId, listener);
}
return listener;
}
+ // Next priority goes to the pending task id listener
+ if (mPendingTaskToListener.containsKey(taskId)) {
+ listener = mPendingTaskToListener.get(taskId);
+ if (listener != null) {
+ if (removePendingIfNeeded) {
+ ProtoLog.v(WM_SHELL_TASK_ORG, "Migrating pending listener to task: taskId=%d",
+ taskId);
+ mPendingTaskToListener.remove(taskId);
+ mTaskListeners.put(taskId, listener);
+ }
+ return listener;
+ }
+ }
+
// Next priority goes to taskId specific listeners.
listener = mTaskListeners.get(taskId);
if (listener != null) return listener;
@@ -1025,13 +1062,21 @@
}
pw.println();
- pw.println(innerPrefix + mLaunchCookieToListener.size() + " Launch Cookies");
+ pw.println(innerPrefix + mLaunchCookieToListener.size()
+ + " Pending launch cookies listeners");
for (int i = mLaunchCookieToListener.size() - 1; i >= 0; --i) {
final IBinder key = mLaunchCookieToListener.keyAt(i);
final TaskListener listener = mLaunchCookieToListener.valueAt(i);
pw.println(innerPrefix + "#" + i + " cookie=" + key + " listener=" + listener);
}
+ pw.println();
+ pw.println(innerPrefix + mPendingTaskToListener.size() + " Pending task listeners");
+ for (int i = mPendingTaskToListener.size() - 1; i >= 0; --i) {
+ final int taskId = mPendingTaskToListener.keyAt(i);
+ final TaskListener listener = mPendingTaskToListener.valueAt(i);
+ pw.println(innerPrefix + "#" + i + " taskId=" + taskId + " listener=" + listener);
+ }
}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewListener.java
index 2e2f2f0..b151dc3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewListener.java
@@ -123,8 +123,9 @@
// Post to keep the lifecycle normal
// TODO - currently based on type, really it's what the "launch item" is.
mParentView.post(() -> {
- ProtoLog.d(WM_SHELL_BUBBLES, "onInitialized: calling startActivity, bubble=%s",
- getBubbleKey());
+ ProtoLog.d(WM_SHELL_BUBBLES,
+ "onInitialized: calling startActivity, bubble=%s hasPreparingTransition=%b",
+ getBubbleKey(), mBubble.getPreparingTransition() != null);
try {
options.setTaskAlwaysOnTop(true /* alwaysOnTop */);
options.setPendingIntentBackgroundActivityStartMode(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java
index ae443fe..bab1771 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java
@@ -690,7 +690,9 @@
mTaskViewTransitions.prepareOpenAnimation(tv, true /* new */, startT, mFinishT,
(ActivityManager.RunningTaskInfo) mTaskInfo, mTaskLeash, mFinishWct);
// Add the task view task listener manually since we aren't going through
- // TaskViewTransitions (which normally sets up the listener via a pending launch cookie
+ // TaskViewTransitions (which normally sets up the listener via a pending launch cookie)
+ // Note: In this path, because a new task is being started, the transition may receive
+ // the transition for the task before the organizer does
mTaskOrganizer.addListenerForTaskId(tv, mTaskInfo.taskId);
if (mFinishWct.isEmpty()) {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
index deb61d8..e5f53ae 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
@@ -297,6 +297,34 @@
}
@Test
+ public void testAddPendingListenerForTaskId() {
+ RunningTaskInfo task1 = createTaskInfo(/* taskId= */ 1, WINDOWING_MODE_MULTI_WINDOW);
+ TrackingTaskListener listener = new TrackingTaskListener();
+
+ // Add the listener first, then report the task to the organizer
+ mOrganizer.addListenerForTaskId(listener, 1);
+ assertFalse(mOrganizer.hasTaskListener(1));
+ mOrganizer.onTaskAppeared(task1, /* leash= */ null);
+
+ // Verify that the listener got notified anyways
+ assertTrue(listener.appeared.contains(task1));
+ }
+
+ @Test
+ public void testRemovePendingListenerForTaskId() {
+ RunningTaskInfo task1 = createTaskInfo(/* taskId= */ 1, WINDOWING_MODE_MULTI_WINDOW);
+ TrackingTaskListener listener = new TrackingTaskListener();
+
+ // Add the listener, remove the listener, then report the task to the organizer
+ mOrganizer.addListenerForTaskId(listener, 1);
+ mOrganizer.removeListener(listener);
+ mOrganizer.onTaskAppeared(task1, /* leash= */ null);
+
+ // Verify that the pending listener does not get notified
+ assertFalse(listener.appeared.contains(task1));
+ }
+
+ @Test
public void testAddListenerForTaskId_afterTypeListener() {
RunningTaskInfo task1 = createTaskInfo(/* taskId= */ 1, WINDOWING_MODE_MULTI_WINDOW);
TrackingTaskListener mwListener = new TrackingTaskListener();