Merge "Return the existing tasks when registering an organizer"

This commit is contained in:
Winson Chung
2020-10-14 15:45:19 +00:00
committed by Android (Google) Code Review
13 changed files with 381 additions and 95 deletions

View File

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

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

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

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