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
This commit is contained in:
Winson Chung
2020-08-27 12:42:48 -07:00
parent dc6aaed888
commit 3f4c002e1f
13 changed files with 381 additions and 95 deletions

View File

@@ -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 {

View File

@@ -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);

View File

@@ -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.

View 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;

View 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;
}
}

View File

@@ -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) {

View File

@@ -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",

View File

@@ -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) {

View File

@@ -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),

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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);

View File

@@ -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));
}
@@ -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));
}
}
}