summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Winson Chung <winsonc@google.com> 2020-08-27 12:42:48 -0700
committer Winson Chung <winsonc@google.com> 2020-10-13 20:58:25 -0700
commit3f4c002e1f8730ae7469fa54bf06b9683e1e925a (patch)
treea98c3d7173bbc4214fe19f7a5c89f6081b906b3c
parentdc6aaed888c6c8c8d3a4b2d3d4a6095925037dbf (diff)
Return the existing tasks when registering an organizer
- Existing tasks for supported windowing modes are returned when registering the organizer and the organizer will not receive onTaskAppeared() for these tasks. For now, the shell task organizer will keep these tasks for reporting task appeared when individual components add listeners for their windowing modes. - Fixed some tests now that we organize fullscreen tasks Bug: 162028888 Test: atest WindowOrganizerTests Test: atest ShellTaskOrganizerTests Change-Id: Iedc5f58fbfa819e753a5dcea7a8eb9dee8b6843b
-rw-r--r--api/test-current.txt13
-rw-r--r--core/java/android/app/IActivityTaskManager.aidl3
-rw-r--r--core/java/android/window/ITaskOrganizerController.aidl7
-rw-r--r--core/java/android/window/TaskAppearedInfo.aidl24
-rw-r--r--core/java/android/window/TaskAppearedInfo.java86
-rw-r--r--core/java/android/window/TaskOrganizer.java13
-rw-r--r--libs/WindowManager/Shell/res/raw/wm_shell_protolog.json12
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java39
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java2
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java46
-rw-r--r--services/core/java/com/android/server/wm/Task.java24
-rw-r--r--services/core/java/com/android/server/wm/TaskOrganizerController.java77
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java130
13 files changed, 381 insertions, 95 deletions
diff --git a/api/test-current.txt b/api/test-current.txt
index 9069eea8d93c..6f6b1b28cf39 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -5784,6 +5784,15 @@ package android.window {
field public static final int FEATURE_WINDOW_TOKENS = 2; // 0x2
}
+ public final class TaskAppearedInfo implements android.os.Parcelable {
+ ctor public TaskAppearedInfo(@NonNull android.app.ActivityManager.RunningTaskInfo, @NonNull android.view.SurfaceControl);
+ method public int describeContents();
+ method @NonNull public android.view.SurfaceControl getLeash();
+ method @NonNull public android.app.ActivityManager.RunningTaskInfo getTaskInfo();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.window.TaskAppearedInfo> CREATOR;
+ }
+
public class TaskOrganizer extends android.window.WindowOrganizer {
ctor public TaskOrganizer();
method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public android.app.ActivityManager.RunningTaskInfo createRootTask(int, int);
@@ -5795,10 +5804,10 @@ package android.window {
method @BinderThread public void onTaskAppeared(@NonNull android.app.ActivityManager.RunningTaskInfo, @NonNull android.view.SurfaceControl);
method @BinderThread public void onTaskInfoChanged(@NonNull android.app.ActivityManager.RunningTaskInfo);
method @BinderThread public void onTaskVanished(@NonNull android.app.ActivityManager.RunningTaskInfo);
- method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public final void registerOrganizer();
+ method @CallSuper @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public java.util.List<android.window.TaskAppearedInfo> registerOrganizer();
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void setInterceptBackPressedOnTaskRoot(@NonNull android.window.WindowContainerToken, boolean);
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void setLaunchRoot(int, @NonNull android.window.WindowContainerToken);
- method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public final void unregisterOrganizer();
+ method @CallSuper @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void unregisterOrganizer();
}
public final class WindowContainerToken implements android.os.Parcelable {
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 66007e5c17d6..75302293088f 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -165,7 +165,8 @@ interface IActivityTaskManager {
int getTaskForActivity(in IBinder token, in boolean onlyRoot);
/** Finish all activities that were started for result from the specified activity. */
void finishSubActivity(in IBinder token, in String resultWho, int requestCode);
- ParceledListSlice getRecentTasks(int maxNum, int flags, int userId);
+ ParceledListSlice<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum, int flags,
+ int userId);
boolean willActivityBeVisible(in IBinder token);
void setRequestedOrientation(in IBinder token, int requestedOrientation);
int getRequestedOrientation(in IBinder token);
diff --git a/core/java/android/window/ITaskOrganizerController.aidl b/core/java/android/window/ITaskOrganizerController.aidl
index 12b16ff6645c..3a84c1f98ce6 100644
--- a/core/java/android/window/ITaskOrganizerController.aidl
+++ b/core/java/android/window/ITaskOrganizerController.aidl
@@ -17,7 +17,9 @@
package android.window;
import android.app.ActivityManager;
+import android.content.pm.ParceledListSlice;
import android.window.ITaskOrganizer;
+import android.window.TaskAppearedInfo;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
@@ -26,8 +28,11 @@ interface ITaskOrganizerController {
/**
* Register a TaskOrganizer to manage all the tasks with supported windowing modes.
+ *
+ * @return a list of the tasks that should be managed by the organizer, not including tasks
+ * created via {@link #createRootTask}.
*/
- void registerTaskOrganizer(ITaskOrganizer organizer);
+ ParceledListSlice<TaskAppearedInfo> registerTaskOrganizer(ITaskOrganizer organizer);
/**
* Unregisters a previously registered task organizer.
diff --git a/core/java/android/window/TaskAppearedInfo.aidl b/core/java/android/window/TaskAppearedInfo.aidl
new file mode 100644
index 000000000000..13eba25f37a3
--- /dev/null
+++ b/core/java/android/window/TaskAppearedInfo.aidl
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.window;
+
+/**
+ * Data object for the task info provided when a task is presented to an organizer.
+ * @hide
+ */
+parcelable TaskAppearedInfo;
+
diff --git a/core/java/android/window/TaskAppearedInfo.java b/core/java/android/window/TaskAppearedInfo.java
new file mode 100644
index 000000000000..2ff331eb22e5
--- /dev/null
+++ b/core/java/android/window/TaskAppearedInfo.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.window;
+
+import android.annotation.NonNull;
+import android.annotation.TestApi;
+import android.app.ActivityManager.RunningTaskInfo;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.SurfaceControl;
+
+/**
+ * Data object for the task info provided when a task is presented to an organizer.
+ * @hide
+ */
+@TestApi
+public final class TaskAppearedInfo implements Parcelable {
+
+ @NonNull
+ private final RunningTaskInfo mTaskInfo;
+
+ @NonNull
+ private final SurfaceControl mLeash;
+
+ @NonNull
+ public static final Creator<TaskAppearedInfo> CREATOR = new Creator<TaskAppearedInfo>() {
+ @Override
+ public TaskAppearedInfo createFromParcel(Parcel source) {
+ final RunningTaskInfo taskInfo = source.readTypedObject(RunningTaskInfo.CREATOR);
+ final SurfaceControl leash = source.readTypedObject(SurfaceControl.CREATOR);
+ return new TaskAppearedInfo(taskInfo, leash);
+ }
+
+ @Override
+ public TaskAppearedInfo[] newArray(int size) {
+ return new TaskAppearedInfo[size];
+ }
+
+ };
+
+ public TaskAppearedInfo(@NonNull RunningTaskInfo taskInfo, @NonNull SurfaceControl leash) {
+ mTaskInfo = taskInfo;
+ mLeash = leash;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeTypedObject(mTaskInfo, flags);
+ dest.writeTypedObject(mLeash, flags);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * @return the task info.
+ */
+ @NonNull
+ public RunningTaskInfo getTaskInfo() {
+ return mTaskInfo;
+ }
+
+ /**
+ * @return the leash for the task.
+ */
+ @NonNull
+ public SurfaceControl getLeash() {
+ return mLeash;
+ }
+}
diff --git a/core/java/android/window/TaskOrganizer.java b/core/java/android/window/TaskOrganizer.java
index a7cb642b83f9..909bb47bf1a5 100644
--- a/core/java/android/window/TaskOrganizer.java
+++ b/core/java/android/window/TaskOrganizer.java
@@ -17,6 +17,7 @@
package android.window;
import android.annotation.BinderThread;
+import android.annotation.CallSuper;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -51,11 +52,16 @@ public class TaskOrganizer extends WindowOrganizer {
/**
* Register a TaskOrganizer to manage tasks as they enter a supported windowing mode.
+ *
+ * @return a list of the tasks that should be managed by the organizer, not including tasks
+ * created via {@link #createRootTask}.
*/
@RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
- public final void registerOrganizer() {
+ @CallSuper
+ @NonNull
+ public List<TaskAppearedInfo> registerOrganizer() {
try {
- mTaskOrganizerController.registerTaskOrganizer(mInterface);
+ return mTaskOrganizerController.registerTaskOrganizer(mInterface).getList();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -63,7 +69,8 @@ public class TaskOrganizer extends WindowOrganizer {
/** Unregisters a previously registered task organizer. */
@RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
- public final void unregisterOrganizer() {
+ @CallSuper
+ public void unregisterOrganizer() {
try {
mTaskOrganizerController.unregisterTaskOrganizer(mInterface);
} catch (RemoteException e) {
diff --git a/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json b/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json
index bcef154beeb3..44744bc227a9 100644
--- a/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json
+++ b/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json
@@ -7,6 +7,12 @@
"group": "WM_SHELL_TASK_ORG",
"at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
},
+ "-1683614271": {
+ "message": "Existing task: id=%d component=%s",
+ "level": "VERBOSE",
+ "group": "WM_SHELL_TASK_ORG",
+ "at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
+ },
"-1534364071": {
"message": "onTransitionReady %s: %s",
"level": "VERBOSE",
@@ -61,6 +67,12 @@
"group": "WM_SHELL_TASK_ORG",
"at": "com\/android\/wm\/shell\/FullscreenTaskListener.java"
},
+ "580605218": {
+ "message": "Registering organizer",
+ "level": "VERBOSE",
+ "group": "WM_SHELL_TASK_ORG",
+ "at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
+ },
"980952660": {
"message": "Task root back pressed taskId=%d",
"level": "VERBOSE",
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 7ce65fd63334..d87de5a06c55 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -28,12 +28,14 @@ import android.annotation.IntDef;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.WindowConfiguration.WindowingMode;
import android.util.Log;
-import android.util.Pair;
import android.util.SparseArray;
import android.view.SurfaceControl;
import android.window.ITaskOrganizerController;
+import android.window.TaskAppearedInfo;
import android.window.TaskOrganizer;
+import androidx.annotation.NonNull;
+
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.common.ShellExecutor;
@@ -42,6 +44,7 @@ import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import java.util.Arrays;
+import java.util.List;
/**
* Unified task organizer for all components in the shell.
@@ -82,7 +85,7 @@ public class ShellTaskOrganizer extends TaskOrganizer {
// Keeps track of all the tasks reported to this organizer (changes in windowing mode will
// require us to report to both old and new listeners)
- private final SparseArray<Pair<RunningTaskInfo, SurfaceControl>> mTasks = new SparseArray<>();
+ private final SparseArray<TaskAppearedInfo> mTasks = new SparseArray<>();
// TODO(shell-transitions): move to a more "global" Shell location as this isn't only for Tasks
private final Transitions mTransitions;
@@ -102,6 +105,19 @@ public class ShellTaskOrganizer extends TaskOrganizer {
if (Transitions.ENABLE_SHELL_TRANSITIONS) registerTransitionPlayer(mTransitions);
}
+ @Override
+ public List<TaskAppearedInfo> registerOrganizer() {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Registering organizer");
+ final List<TaskAppearedInfo> taskInfos = super.registerOrganizer();
+ for (int i = 0; i < taskInfos.size(); i++) {
+ final TaskAppearedInfo info = taskInfos.get(i);
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Existing task: id=%d component=%s",
+ info.getTaskInfo().taskId, info.getTaskInfo().baseIntent);
+ onTaskAppeared(info.getTaskInfo(), info.getLeash());
+ }
+ return taskInfos;
+ }
+
/**
* Adds a listener for tasks with given types.
*/
@@ -117,10 +133,11 @@ public class ShellTaskOrganizer extends TaskOrganizer {
// Notify the listener of all existing tasks with the given type.
for (int i = mTasks.size() - 1; i >= 0; i--) {
- Pair<RunningTaskInfo, SurfaceControl> data = mTasks.valueAt(i);
- final @TaskListenerType int taskListenerType = getTaskListenerType(data.first);
+ TaskAppearedInfo data = mTasks.valueAt(i);
+ final @TaskListenerType int taskListenerType = getTaskListenerType(
+ data.getTaskInfo());
if (taskListenerType == listenerType) {
- listener.onTaskAppeared(data.first, data.second);
+ listener.onTaskAppeared(data.getTaskInfo(), data.getLeash());
}
}
}
@@ -143,7 +160,7 @@ public class ShellTaskOrganizer extends TaskOrganizer {
public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Task appeared taskId=%d",
taskInfo.taskId);
- mTasks.put(taskInfo.taskId, new Pair<>(taskInfo, leash));
+ mTasks.put(taskInfo.taskId, new TaskAppearedInfo(taskInfo, leash));
final TaskListener listener = mTaskListenersByType.get(getTaskListenerType(taskInfo));
if (listener != null) {
listener.onTaskAppeared(taskInfo, leash);
@@ -154,10 +171,10 @@ public class ShellTaskOrganizer extends TaskOrganizer {
public void onTaskInfoChanged(RunningTaskInfo taskInfo) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Task info changed taskId=%d",
taskInfo.taskId);
- final Pair<RunningTaskInfo, SurfaceControl> data = mTasks.get(taskInfo.taskId);
+ final TaskAppearedInfo data = mTasks.get(taskInfo.taskId);
final @TaskListenerType int listenerType = getTaskListenerType(taskInfo);
- final @TaskListenerType int prevListenerType = getTaskListenerType(data.first);
- mTasks.put(taskInfo.taskId, new Pair<>(taskInfo, data.second));
+ final @TaskListenerType int prevListenerType = getTaskListenerType(data.getTaskInfo());
+ mTasks.put(taskInfo.taskId, new TaskAppearedInfo(taskInfo, data.getLeash()));
if (prevListenerType != listenerType) {
// TODO: We currently send vanished/appeared as the task moves between types, but
// we should consider adding a different mode-changed callback
@@ -167,7 +184,7 @@ public class ShellTaskOrganizer extends TaskOrganizer {
}
listener = mTaskListenersByType.get(listenerType);
if (listener != null) {
- SurfaceControl leash = data.second;
+ SurfaceControl leash = data.getLeash();
listener.onTaskAppeared(taskInfo, leash);
}
} else {
@@ -193,7 +210,7 @@ public class ShellTaskOrganizer extends TaskOrganizer {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Task vanished taskId=%d",
taskInfo.taskId);
final @TaskListenerType int prevListenerType =
- getTaskListenerType(mTasks.get(taskInfo.taskId).first);
+ getTaskListenerType(mTasks.get(taskInfo.taskId).getTaskInfo());
mTasks.remove(taskInfo.taskId);
final TaskListener listener = mTaskListenersByType.get(prevListenerType);
if (listener != null) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
index a0ce9dabffe6..f3dadfcb933a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
@@ -26,7 +26,7 @@ import com.android.internal.protolog.common.IProtoLogGroup;
public enum ShellProtoLogGroup implements IProtoLogGroup {
// NOTE: Since we enable these from the same WM ShellCommand, these names should not conflict
// with those in the framework ProtoLogGroup
- WM_SHELL_TASK_ORG(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
+ WM_SHELL_TASK_ORG(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true,
Consts.TAG_WM_SHELL),
WM_SHELL_TRANSITIONS(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true,
Consts.TAG_WM_SHELL),
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 f01fc517d5ba..5418a5b21680 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
@@ -22,6 +22,10 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_MULTI_WINDOW;
import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_PIP;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
@@ -29,10 +33,12 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import android.app.ActivityManager.RunningTaskInfo;
+import android.content.pm.ParceledListSlice;
import android.os.RemoteException;
import android.view.SurfaceControl;
import android.window.ITaskOrganizer;
import android.window.ITaskOrganizerController;
+import android.window.TaskAppearedInfo;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -93,8 +99,12 @@ public class ShellTaskOrganizerTests {
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mOrganizer = new ShellTaskOrganizer(mTaskOrganizerController, mSyncTransactionQueue,
- mTransactionPool, mTestExecutor, mTestExecutor);
+ try {
+ doReturn(ParceledListSlice.<TaskAppearedInfo>emptyList())
+ .when(mTaskOrganizerController).registerTaskOrganizer(any());
+ } catch (RemoteException e) {}
+ mOrganizer = spy(new ShellTaskOrganizer(mTaskOrganizerController, mSyncTransactionQueue,
+ mTransactionPool, mTestExecutor, mTestExecutor));
}
@Test
@@ -116,8 +126,29 @@ public class ShellTaskOrganizerTests {
}
@Test
+ public void testRegisterWithExistingTasks() throws RemoteException {
+ // Setup some tasks
+ RunningTaskInfo task1 = createTaskInfo(1, WINDOWING_MODE_MULTI_WINDOW);
+ RunningTaskInfo task2 = createTaskInfo(2, WINDOWING_MODE_MULTI_WINDOW);
+ ArrayList<TaskAppearedInfo> taskInfos = new ArrayList<>();
+ taskInfos.add(new TaskAppearedInfo(task1, new SurfaceControl()));
+ taskInfos.add(new TaskAppearedInfo(task2, new SurfaceControl()));
+ doReturn(new ParceledListSlice(taskInfos))
+ .when(mTaskOrganizerController).registerTaskOrganizer(any());
+
+ // Register and expect the tasks to be stored
+ mOrganizer.registerOrganizer();
+
+ // Check that the tasks are next reported when the listener is added
+ TrackingTaskListener listener = new TrackingTaskListener();
+ mOrganizer.addListener(listener, TASK_LISTENER_TYPE_MULTI_WINDOW);
+ assertTrue(listener.appeared.contains(task1));
+ assertTrue(listener.appeared.contains(task2));
+ }
+
+ @Test
public void testAppearedVanished() {
- RunningTaskInfo taskInfo = createTaskInfo(WINDOWING_MODE_MULTI_WINDOW);
+ RunningTaskInfo taskInfo = createTaskInfo(1, WINDOWING_MODE_MULTI_WINDOW);
TrackingTaskListener listener = new TrackingTaskListener();
mOrganizer.addListener(listener, TASK_LISTENER_TYPE_MULTI_WINDOW);
mOrganizer.onTaskAppeared(taskInfo, null);
@@ -129,7 +160,7 @@ public class ShellTaskOrganizerTests {
@Test
public void testAddListenerExistingTasks() {
- RunningTaskInfo taskInfo = createTaskInfo(WINDOWING_MODE_MULTI_WINDOW);
+ RunningTaskInfo taskInfo = createTaskInfo(1, WINDOWING_MODE_MULTI_WINDOW);
mOrganizer.onTaskAppeared(taskInfo, null);
TrackingTaskListener listener = new TrackingTaskListener();
@@ -139,7 +170,7 @@ public class ShellTaskOrganizerTests {
@Test
public void testWindowingModeChange() {
- RunningTaskInfo taskInfo = createTaskInfo(WINDOWING_MODE_MULTI_WINDOW);
+ RunningTaskInfo taskInfo = createTaskInfo(1, WINDOWING_MODE_MULTI_WINDOW);
TrackingTaskListener mwListener = new TrackingTaskListener();
TrackingTaskListener pipListener = new TrackingTaskListener();
mOrganizer.addListener(mwListener, TASK_LISTENER_TYPE_MULTI_WINDOW);
@@ -148,14 +179,15 @@ public class ShellTaskOrganizerTests {
assertTrue(mwListener.appeared.contains(taskInfo));
assertTrue(pipListener.appeared.isEmpty());
- taskInfo = createTaskInfo(WINDOWING_MODE_PINNED);
+ taskInfo = createTaskInfo(1, WINDOWING_MODE_PINNED);
mOrganizer.onTaskInfoChanged(taskInfo);
assertTrue(mwListener.vanished.contains(taskInfo));
assertTrue(pipListener.appeared.contains(taskInfo));
}
- private RunningTaskInfo createTaskInfo(int windowingMode) {
+ private RunningTaskInfo createTaskInfo(int taskId, int windowingMode) {
RunningTaskInfo taskInfo = new RunningTaskInfo();
+ taskInfo.taskId = taskId;
taskInfo.configuration.windowConfiguration.setWindowingMode(windowingMode);
return taskInfo;
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 249fe031b757..b76ce74f8318 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -4792,9 +4792,11 @@ class Task extends WindowContainer<WindowContainer> {
// If the task is not yet visible when it is added to the task organizer, then we should
// hide it to allow the task organizer to show it when it is properly reparented. We
// skip this for tasks created by the organizer because they can synchronously update
- // the leash before new children are added to the task.
+ // the leash before new children are added to the task. Also skip this if the task
+ // has already been sent to the organizer which can happen before the first draw if
+ // an existing task is reported to the organizer when it first registers.
if (!mAtmService.getTransitionController().isShellTransitionsEnabled()
- && !mCreatedByOrganizer
+ && !mCreatedByOrganizer && !mTaskAppearedSent
&& mTaskOrganizer != null && !prevHasBeenVisible) {
getSyncTransaction().hide(getSurfaceControl());
commitPendingTransaction();
@@ -4846,6 +4848,11 @@ class Task extends WindowContainer<WindowContainer> {
@VisibleForTesting
boolean setTaskOrganizer(ITaskOrganizer organizer) {
+ return setTaskOrganizer(organizer, false /* skipTaskAppeared */);
+ }
+
+ @VisibleForTesting
+ boolean setTaskOrganizer(ITaskOrganizer organizer, boolean skipTaskAppeared) {
if (mTaskOrganizer == organizer) {
return false;
}
@@ -4858,7 +4865,9 @@ class Task extends WindowContainer<WindowContainer> {
sendTaskVanished(prevOrganizer);
if (mTaskOrganizer != null) {
- sendTaskAppeared();
+ if (!skipTaskAppeared) {
+ sendTaskAppeared();
+ }
} else {
// No longer managed by any organizer.
mTaskAppearedSent = false;
@@ -4871,6 +4880,10 @@ class Task extends WindowContainer<WindowContainer> {
return true;
}
+ boolean updateTaskOrganizerState(boolean forceUpdate) {
+ return updateTaskOrganizerState(forceUpdate, false /* skipTaskAppeared */);
+ }
+
/**
* Called when the task state changes (ie. from windowing mode change) an the task organizer
* state should also be updated.
@@ -4878,9 +4891,10 @@ class Task extends WindowContainer<WindowContainer> {
* @param forceUpdate Updates the task organizer to the one currently specified in the task
* org controller for the task's windowing mode, ignoring the cached
* windowing mode checks.
+ * @param skipTaskAppeared Skips calling taskAppeared for the new organizer if it has changed
* @return {@code true} if task organizer changed.
*/
- boolean updateTaskOrganizerState(boolean forceUpdate) {
+ boolean updateTaskOrganizerState(boolean forceUpdate, boolean skipTaskAppeared) {
if (getSurfaceControl() == null) {
// Can't call onTaskAppeared without a surfacecontrol, so defer this until after one
// is created.
@@ -4896,7 +4910,7 @@ class Task extends WindowContainer<WindowContainer> {
if (!forceUpdate && mTaskOrganizer == organizer) {
return false;
}
- return setTaskOrganizer(organizer);
+ return setTaskOrganizer(organizer, skipTaskAppeared);
}
@Override
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 8201d108c883..48550ed016c3 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -32,6 +32,7 @@ import android.app.ActivityManager.TaskDescription;
import android.app.WindowConfiguration;
import android.content.Intent;
import android.content.pm.ActivityInfo;
+import android.content.pm.ParceledListSlice;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
@@ -39,6 +40,7 @@ import android.util.Slog;
import android.view.SurfaceControl;
import android.window.ITaskOrganizer;
import android.window.ITaskOrganizerController;
+import android.window.TaskAppearedInfo;
import android.window.WindowContainerToken;
import com.android.internal.annotations.VisibleForTesting;
@@ -76,8 +78,6 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
WINDOWING_MODE_FREEFORM
};
- private final WindowManagerGlobalLock mGlobalLock;
-
private class DeathRecipient implements IBinder.DeathRecipient {
ITaskOrganizer mTaskOrganizer;
@@ -103,39 +103,38 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
* transaction before they are presented to the task org.
*/
private class TaskOrganizerCallbacks {
- final WindowManagerService mService;
final ITaskOrganizer mTaskOrganizer;
final Consumer<Runnable> mDeferTaskOrgCallbacksConsumer;
- private final SurfaceControl.Transaction mTransaction;
-
- TaskOrganizerCallbacks(WindowManagerService wm, ITaskOrganizer taskOrg,
+ TaskOrganizerCallbacks(ITaskOrganizer taskOrg,
Consumer<Runnable> deferTaskOrgCallbacksConsumer) {
- mService = wm;
mDeferTaskOrgCallbacksConsumer = deferTaskOrgCallbacksConsumer;
mTaskOrganizer = taskOrg;
- mTransaction = wm.mTransactionFactory.get();
}
IBinder getBinder() {
return mTaskOrganizer.asBinder();
}
+ SurfaceControl prepareLeash(Task task, boolean visible, String reason) {
+ SurfaceControl outSurfaceControl = new SurfaceControl(task.getSurfaceControl(), reason);
+ if (!task.mCreatedByOrganizer && !visible) {
+ // To prevent flashes, we hide the task prior to sending the leash to the
+ // task org if the task has previously hidden (ie. when entering PIP)
+ mTransaction.hide(outSurfaceControl);
+ mTransaction.apply();
+ }
+ return outSurfaceControl;
+ }
+
void onTaskAppeared(Task task) {
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Task appeared taskId=%d", task.mTaskId);
final boolean visible = task.isVisible();
final RunningTaskInfo taskInfo = task.getTaskInfo();
mDeferTaskOrgCallbacksConsumer.accept(() -> {
try {
- SurfaceControl outSurfaceControl = new SurfaceControl(task.getSurfaceControl(),
- "TaskOrganizerController.onTaskAppeared");
- if (!task.mCreatedByOrganizer && !visible) {
- // To prevent flashes, we hide the task prior to sending the leash to the
- // task org if the task has previously hidden (ie. when entering PIP)
- mTransaction.hide(outSurfaceControl);
- mTransaction.apply();
- }
- mTaskOrganizer.onTaskAppeared(taskInfo, outSurfaceControl);
+ mTaskOrganizer.onTaskAppeared(taskInfo, prepareLeash(task, visible,
+ "TaskOrganizerController.onTaskAppeared"));
} catch (RemoteException e) {
Slog.e(TAG, "Exception sending onTaskAppeared callback", e);
}
@@ -208,8 +207,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
mDeferTaskOrgCallbacksConsumer != null
? mDeferTaskOrgCallbacksConsumer
: mService.mWindowManager.mAnimator::addAfterPrepareSurfacesRunnable;
- mOrganizer = new TaskOrganizerCallbacks(mService.mWindowManager, organizer,
- deferTaskOrgCallbacksConsumer);
+ mOrganizer = new TaskOrganizerCallbacks(organizer, deferTaskOrgCallbacksConsumer);
mDeathRecipient = new DeathRecipient(organizer);
try {
organizer.asBinder().linkToDeath(mDeathRecipient, 0);
@@ -219,6 +217,18 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
mUid = uid;
}
+ /**
+ * Register this task with this state, but doesn't trigger the task appeared callback to
+ * the organizer.
+ */
+ SurfaceControl addTaskWithoutCallback(Task t, String reason) {
+ t.mTaskAppearedSent = true;
+ if (!mOrganizedTasks.contains(t)) {
+ mOrganizedTasks.add(t);
+ }
+ return mOrganizer.prepareLeash(t, t.isVisible(), reason);
+ }
+
void addTask(Task t) {
if (t.mTaskAppearedSent) return;
@@ -265,6 +275,9 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
}
}
+ private final ActivityTaskManagerService mService;
+ private final WindowManagerGlobalLock mGlobalLock;
+
// List of task organizers by priority
private final LinkedList<ITaskOrganizer> mTaskOrganizers = new LinkedList<>();
private final HashMap<IBinder, TaskOrganizerState> mTaskOrganizerStates = new HashMap<>();
@@ -273,8 +286,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
// Set of organized tasks (by taskId) that dispatch back pressed to their organizers
private final HashSet<Integer> mInterceptBackPressedOnRootTasks = new HashSet();
- private final ActivityTaskManagerService mService;
-
+ private SurfaceControl.Transaction mTransaction;
private RunningTaskInfo mTmpTaskInfo;
private Consumer<Runnable> mDeferTaskOrgCallbacksConsumer;
@@ -299,7 +311,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
* Register a TaskOrganizer to manage tasks as they enter the a supported windowing mode.
*/
@Override
- public void registerTaskOrganizer(ITaskOrganizer organizer) {
+ public ParceledListSlice<TaskAppearedInfo> registerTaskOrganizer(ITaskOrganizer organizer) {
enforceStackPermission("registerTaskOrganizer()");
final int uid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity();
@@ -307,17 +319,36 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
synchronized (mGlobalLock) {
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Register task organizer=%s uid=%d",
organizer.asBinder(), uid);
+
+ // Defer initializing the transaction since the transaction factory can be set up
+ // by the tests after construction of the controller
+ if (mTransaction == null) {
+ mTransaction = mService.mWindowManager.mTransactionFactory.get();
+ }
+
if (!mTaskOrganizerStates.containsKey(organizer.asBinder())) {
mTaskOrganizers.add(organizer);
mTaskOrganizerStates.put(organizer.asBinder(),
new TaskOrganizerState(organizer, uid));
}
+
+ final ArrayList<TaskAppearedInfo> taskInfos = new ArrayList<>();
+ final TaskOrganizerState state = mTaskOrganizerStates.get(organizer.asBinder());
mService.mRootWindowContainer.forAllTasks((task) -> {
if (ArrayUtils.contains(UNSUPPORTED_WINDOWING_MODES, task.getWindowingMode())) {
return;
}
- task.updateTaskOrganizerState(true /* forceUpdate */);
+
+ boolean returnTask = !task.mCreatedByOrganizer;
+ task.updateTaskOrganizerState(true /* forceUpdate */,
+ returnTask /* skipTaskAppeared */);
+ if (returnTask) {
+ SurfaceControl outSurfaceControl = state.addTaskWithoutCallback(task,
+ "TaskOrganizerController.registerTaskOrganizer");
+ taskInfos.add(new TaskAppearedInfo(task.getTaskInfo(), outSurfaceControl));
+ }
});
+ return new ParceledListSlice<>(taskInfos);
}
} finally {
Binder.restoreCallingIdentity(origId);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index 0152fc607f73..cc2691279bdb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -54,12 +54,14 @@ import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.atLeastOnce;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityTaskManager.RootTaskInfo;
import android.app.PictureInPictureParams;
import android.content.pm.ActivityInfo;
+import android.content.pm.ParceledListSlice;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Binder;
@@ -72,6 +74,7 @@ import android.view.Display;
import android.view.SurfaceControl;
import android.window.ITaskOrganizer;
import android.window.IWindowContainerTransactionCallback;
+import android.window.TaskAppearedInfo;
import android.window.WindowContainerTransaction;
import androidx.test.filters.SmallTest;
@@ -79,8 +82,10 @@ import androidx.test.filters.SmallTest;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import java.util.ArrayList;
+import java.util.HashSet;
import java.util.List;
/**
@@ -93,14 +98,27 @@ import java.util.List;
@Presubmit
@RunWith(WindowTestRunner.class)
public class WindowOrganizerTests extends WindowTestsBase {
- private ITaskOrganizer registerMockOrganizer() {
+
+ private ITaskOrganizer createMockOrganizer() {
final ITaskOrganizer organizer = mock(ITaskOrganizer.class);
when(organizer.asBinder()).thenReturn(new Binder());
+ return organizer;
+ }
- mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(organizer);
+ private ITaskOrganizer registerMockOrganizer(ArrayList<TaskAppearedInfo> existingTasks) {
+ final ITaskOrganizer organizer = createMockOrganizer();
+ ParceledListSlice<TaskAppearedInfo> tasks =
+ mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(organizer);
+ if (existingTasks != null) {
+ existingTasks.addAll(tasks.getList());
+ }
return organizer;
}
+ private ITaskOrganizer registerMockOrganizer() {
+ return registerMockOrganizer(null);
+ }
+
Task createTask(Task stack, boolean fakeDraw) {
final Task task = createTaskInStack(stack, 0);
@@ -128,27 +146,21 @@ public class WindowOrganizerTests extends WindowTestsBase {
@Test
public void testAppearVanish() throws RemoteException {
+ final ITaskOrganizer organizer = registerMockOrganizer();
final Task stack = createStack();
final Task task = createTask(stack);
- final ITaskOrganizer organizer = registerMockOrganizer();
- stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
- stack.setTaskOrganizer(organizer);
verify(organizer).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
-
stack.removeImmediately();
verify(organizer).onTaskVanished(any());
}
@Test
public void testAppearWaitsForVisibility() throws RemoteException {
+ final ITaskOrganizer organizer = registerMockOrganizer();
final Task stack = createStack();
final Task task = createTask(stack, false);
- final ITaskOrganizer organizer = registerMockOrganizer();
-
- stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
- stack.setTaskOrganizer(organizer);
verify(organizer, never())
.onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
@@ -163,9 +175,9 @@ public class WindowOrganizerTests extends WindowTestsBase {
@Test
public void testNoVanishedIfNoAppear() throws RemoteException {
+ final ITaskOrganizer organizer = registerMockOrganizer();
final Task stack = createStack();
final Task task = createTask(stack, false /* hasBeenVisible */);
- final ITaskOrganizer organizer = registerMockOrganizer();
// In this test we skip making the Task visible, and verify
// that even though a TaskOrganizer is set remove doesn't emit
@@ -179,28 +191,25 @@ public class WindowOrganizerTests extends WindowTestsBase {
@Test
public void testTaskNoDraw() throws RemoteException {
+ final ITaskOrganizer organizer = registerMockOrganizer();
final Task stack = createStack();
final Task task = createTask(stack, false /* fakeDraw */);
- final ITaskOrganizer organizer = registerMockOrganizer();
- stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
verify(organizer, never())
.onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
assertTrue(stack.isOrganized());
mWm.mAtmService.mTaskOrganizerController.unregisterTaskOrganizer(organizer);
- verify(organizer, never()).onTaskVanished(any());
+ assertTaskVanished(organizer, false /* expectVanished */, stack);
assertFalse(stack.isOrganized());
}
@Test
public void testClearOrganizer() throws RemoteException {
+ final ITaskOrganizer organizer = registerMockOrganizer();
final Task stack = createStack();
final Task task = createTask(stack);
- final ITaskOrganizer organizer = registerMockOrganizer();
- stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
- stack.setTaskOrganizer(organizer);
verify(organizer).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
assertTrue(stack.isOrganized());
@@ -211,16 +220,15 @@ public class WindowOrganizerTests extends WindowTestsBase {
@Test
public void testUnregisterOrganizer() throws RemoteException {
+ final ITaskOrganizer organizer = registerMockOrganizer();
final Task stack = createStack();
final Task task = createTask(stack);
- final ITaskOrganizer organizer = registerMockOrganizer();
- stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
verify(organizer).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
assertTrue(stack.isOrganized());
mWm.mAtmService.mTaskOrganizerController.unregisterTaskOrganizer(organizer);
- verify(organizer).onTaskVanished(any());
+ assertTaskVanished(organizer, true /* expectVanished */, stack);
assertFalse(stack.isOrganized());
}
@@ -232,37 +240,47 @@ public class WindowOrganizerTests extends WindowTestsBase {
final Task task2 = createTask(stack2);
final Task stack3 = createStack();
final Task task3 = createTask(stack3);
- final ITaskOrganizer organizer = registerMockOrganizer();
-
- // verify that tasks are appeared on registration
- verify(organizer, times(3))
- .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
+ final ArrayList<TaskAppearedInfo> existingTasks = new ArrayList<>();
+ final ITaskOrganizer organizer = registerMockOrganizer(existingTasks);
+
+ // verify that tasks are returned and taskAppeared is not called
+ assertContainsTasks(existingTasks, stack, stack2, stack3);
+ verify(organizer, times(0)).onTaskAppeared(any(RunningTaskInfo.class),
+ any(SurfaceControl.class));
+ verify(organizer, times(0)).onTaskVanished(any());
assertTrue(stack.isOrganized());
- // Now we replace the registration and1 verify the new organizer receives tasks
- final ITaskOrganizer organizer2 = registerMockOrganizer();
- verify(organizer2, times(3))
- .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
+ // Now we replace the registration and verify the new organizer receives existing tasks
+ final ArrayList<TaskAppearedInfo> existingTasks2 = new ArrayList<>();
+ final ITaskOrganizer organizer2 = registerMockOrganizer(existingTasks2);
+ assertContainsTasks(existingTasks2, stack, stack2, stack3);
+ verify(organizer2, times(0)).onTaskAppeared(any(RunningTaskInfo.class),
+ any(SurfaceControl.class));
verify(organizer2, times(0)).onTaskVanished(any());
- // One for task
- verify(organizer, times(3)).onTaskVanished(any());
+ // Removed tasks from the original organizer
+ assertTaskVanished(organizer, true /* expectVanished */, stack, stack2, stack3);
assertTrue(stack2.isOrganized());
// Now we unregister the second one, the first one should automatically be reregistered
// so we verify that it's now seeing changes.
mWm.mAtmService.mTaskOrganizerController.unregisterTaskOrganizer(organizer2);
- verify(organizer, times(6))
+ verify(organizer, times(3))
.onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
- verify(organizer2, times(3)).onTaskVanished(any());
+ assertTaskVanished(organizer2, true /* expectVanished */, stack, stack2, stack3);
}
@Test
public void testRegisterTaskOrganizerWithExistingTasks() throws RemoteException {
final Task stack = createStack();
final Task task = createTask(stack);
+ final Task stack2 = createStack();
+ final Task task2 = createTask(stack2);
+ ArrayList<TaskAppearedInfo> existingTasks = new ArrayList<>();
+ final ITaskOrganizer organizer = registerMockOrganizer(existingTasks);
+ assertContainsTasks(existingTasks, stack, stack2);
- final ITaskOrganizer organizer = registerMockOrganizer();
- verify(organizer, times(1))
+ // Verify we don't get onTaskAppeared if we are returned the tasks
+ verify(organizer, never())
.onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
}
@@ -922,9 +940,9 @@ public class WindowOrganizerTests extends WindowTestsBase {
@Test
public void testPreventDuplicateAppear() throws RemoteException {
+ final ITaskOrganizer organizer = registerMockOrganizer();
final Task stack = createStack();
final Task task = createTask(stack, false /* fakeDraw */);
- final ITaskOrganizer organizer = registerMockOrganizer();
stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
stack.setTaskOrganizer(organizer);
@@ -945,17 +963,14 @@ public class WindowOrganizerTests extends WindowTestsBase {
@Test
public void testInterceptBackPressedOnTaskRoot() throws RemoteException {
+ final ITaskOrganizer organizer = registerMockOrganizer();
final Task stack = createStack();
final Task task = createTask(stack);
final ActivityRecord activity = createActivityRecordInTask(stack.mDisplayContent, task);
final Task stack2 = createStack();
final Task task2 = createTask(stack2);
final ActivityRecord activity2 = createActivityRecordInTask(stack.mDisplayContent, task2);
- final ITaskOrganizer organizer = registerMockOrganizer();
- // Setup the task to be controlled by the MW mode organizer
- stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
- stack2.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
assertTrue(stack.isOrganized());
assertTrue(stack2.isOrganized());
@@ -982,9 +997,9 @@ public class WindowOrganizerTests extends WindowTestsBase {
@Test
public void testBLASTCallbackWithMultipleWindows() throws Exception {
+ final ITaskOrganizer organizer = registerMockOrganizer();
final Task stackController = createStack();
final Task task = createTask(stackController);
- final ITaskOrganizer organizer = registerMockOrganizer();
final WindowState w1 = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window 1");
final WindowState w2 = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window 2");
makeWindowVisible(w1);
@@ -1035,4 +1050,37 @@ public class WindowOrganizerTests extends WindowTestsBase {
assertFalse(daTask.isForceHidden());
});
}
+
+ /**
+ * Verifies that task vanished is called for a specific task.
+ */
+ private void assertTaskVanished(ITaskOrganizer organizer, boolean expectVanished, Task... tasks)
+ throws RemoteException {
+ ArgumentCaptor<RunningTaskInfo> arg = ArgumentCaptor.forClass(RunningTaskInfo.class);
+ verify(organizer, atLeastOnce()).onTaskVanished(arg.capture());
+ List<RunningTaskInfo> taskInfos = arg.getAllValues();
+
+ HashSet<Integer> vanishedTaskIds = new HashSet<>();
+ for (int i = 0; i < taskInfos.size(); i++) {
+ vanishedTaskIds.add(taskInfos.get(i).taskId);
+ }
+ HashSet<Integer> taskIds = new HashSet<>();
+ for (int i = 0; i < tasks.length; i++) {
+ taskIds.add(tasks[i].mTaskId);
+ }
+
+ assertTrue(expectVanished
+ ? vanishedTaskIds.containsAll(taskIds)
+ : !vanishedTaskIds.removeAll(taskIds));
+ }
+
+ private void assertContainsTasks(List<TaskAppearedInfo> taskInfos, Task... expectedTasks) {
+ HashSet<Integer> taskIds = new HashSet<>();
+ for (int i = 0; i < taskInfos.size(); i++) {
+ taskIds.add(taskInfos.get(i).getTaskInfo().taskId);
+ }
+ for (int i = 0; i < expectedTasks.length; i++) {
+ assertTrue(taskIds.contains(expectedTasks[i].mTaskId));
+ }
+ }
}