Merge "Return the existing tasks when registering an organizer"
This commit is contained in:
@@ -5785,6 +5785,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);
|
||||
@@ -5796,10 +5805,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 {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
|
||||
24
core/java/android/window/TaskAppearedInfo.aidl
Normal file
24
core/java/android/window/TaskAppearedInfo.aidl
Normal file
@@ -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;
|
||||
|
||||
86
core/java/android/window/TaskAppearedInfo.java
Normal file
86
core/java/android/window/TaskAppearedInfo.java
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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
|
||||
@@ -115,9 +125,30 @@ 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;
|
||||
}
|
||||
|
||||
@@ -4773,9 +4773,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();
|
||||
@@ -4827,6 +4829,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;
|
||||
}
|
||||
@@ -4839,7 +4846,9 @@ class Task extends WindowContainer<WindowContainer> {
|
||||
sendTaskVanished(prevOrganizer);
|
||||
|
||||
if (mTaskOrganizer != null) {
|
||||
sendTaskAppeared();
|
||||
if (!skipTaskAppeared) {
|
||||
sendTaskAppeared();
|
||||
}
|
||||
} else {
|
||||
// No longer managed by any organizer.
|
||||
mTaskAppearedSent = false;
|
||||
@@ -4852,6 +4861,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.
|
||||
@@ -4859,9 +4872,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.
|
||||
@@ -4877,7 +4891,7 @@ class Task extends WindowContainer<WindowContainer> {
|
||||
if (!forceUpdate && mTaskOrganizer == organizer) {
|
||||
return false;
|
||||
}
|
||||
return setTaskOrganizer(organizer);
|
||||
return setTaskOrganizer(organizer, skipTaskAppeared);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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());
|
||||
|
||||
mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(organizer);
|
||||
return 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();
|
||||
final ArrayList<TaskAppearedInfo> existingTasks = new ArrayList<>();
|
||||
final ITaskOrganizer organizer = registerMockOrganizer(existingTasks);
|
||||
|
||||
// verify that tasks are appeared on registration
|
||||
verify(organizer, times(3))
|
||||
.onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
|
||||
// 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));
|
||||
}
|
||||
|
||||
@@ -954,9 +972,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);
|
||||
@@ -977,17 +995,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());
|
||||
|
||||
@@ -1014,9 +1029,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);
|
||||
@@ -1067,4 +1082,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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user