Merge "Organize Fullscreen tasks"

This commit is contained in:
Evan Rosky
2020-09-11 01:56:00 +00:00
committed by Android (Google) Code Review
12 changed files with 166 additions and 88 deletions

View File

@@ -1,6 +1,12 @@
{
"version": "1.0.0",
"messages": {
"-1501874464": {
"message": "Fullscreen Task Appeared: #%d",
"level": "VERBOSE",
"group": "WM_SHELL_TASK_ORG",
"at": "com\/android\/wm\/shell\/FullscreenTaskListener.java"
},
"-1340279385": {
"message": "Remove listener=%s",
"level": "VERBOSE",
@@ -31,6 +37,12 @@
"group": "WM_SHELL_TASK_ORG",
"at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
},
"564235578": {
"message": "Fullscreen Task Vanished: #%d",
"level": "VERBOSE",
"group": "WM_SHELL_TASK_ORG",
"at": "com\/android\/wm\/shell\/FullscreenTaskListener.java"
},
"980952660": {
"message": "Task root back pressed taskId=%d",
"level": "VERBOSE",

View File

@@ -0,0 +1,69 @@
/*
* 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 com.android.wm.shell;
import android.app.ActivityManager;
import android.util.ArraySet;
import android.util.Slog;
import android.view.SurfaceControl;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener {
private static final String TAG = "FullscreenTaskOrg";
private final TransactionPool mTransactionPool;
private final ArraySet<Integer> mTasks = new ArraySet<>();
FullscreenTaskListener(TransactionPool transactionPool) {
mTransactionPool = transactionPool;
}
@Override
public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
synchronized (mTasks) {
if (mTasks.contains(taskInfo.taskId)) {
throw new RuntimeException("Task appeared more than once: #" + taskInfo.taskId);
}
mTasks.add(taskInfo.taskId);
final SurfaceControl.Transaction t = mTransactionPool.acquire();
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Fullscreen Task Appeared: #%d",
taskInfo.taskId);
t.show(leash);
t.apply();
}
}
@Override
public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
synchronized (mTasks) {
if (!mTasks.remove(taskInfo.taskId)) {
Slog.e(TAG, "Task already vanished: #" + taskInfo.taskId);
return;
}
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Fullscreen Task Vanished: #%d",
taskInfo.taskId);
}
}
@Override
public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
}
}

View File

@@ -16,6 +16,8 @@
package com.android.wm.shell;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import android.app.ActivityManager.RunningTaskInfo;
import android.util.Log;
import android.util.Pair;
@@ -26,6 +28,7 @@ import android.window.TaskOrganizer;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import java.util.ArrayList;
@@ -56,13 +59,16 @@ public class ShellTaskOrganizer extends TaskOrganizer {
// require us to report to both old and new listeners)
private final SparseArray<Pair<RunningTaskInfo, SurfaceControl>> mTasks = new SparseArray<>();
public ShellTaskOrganizer() {
public ShellTaskOrganizer(TransactionPool transactionPool) {
super();
addListener(new FullscreenTaskListener(transactionPool), WINDOWING_MODE_FULLSCREEN);
}
@VisibleForTesting
ShellTaskOrganizer(ITaskOrganizerController taskOrganizerController) {
ShellTaskOrganizer(ITaskOrganizerController taskOrganizerController,
TransactionPool transactionPool) {
super(taskOrganizerController);
addListener(new FullscreenTaskListener(transactionPool), WINDOWING_MODE_FULLSCREEN);
}
/**
@@ -125,6 +131,7 @@ public class ShellTaskOrganizer extends TaskOrganizer {
Pair<RunningTaskInfo, SurfaceControl> data = mTasks.get(taskInfo.taskId);
int winMode = getWindowingMode(taskInfo);
int prevWinMode = getWindowingMode(data.first);
mTasks.put(taskInfo.taskId, new Pair<>(taskInfo, data.second));
if (prevWinMode != -1 && prevWinMode != winMode) {
// TODO: We currently send vanished/appeared as the task moves between win modes, but
// we should consider adding a different mode-changed callback

View File

@@ -21,6 +21,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import android.app.ActivityManager.RunningTaskInfo;
@@ -32,6 +33,8 @@ import android.window.ITaskOrganizerController;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.wm.shell.common.TransactionPool;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -51,6 +54,7 @@ public class ShellTaskOrganizerTests {
private ITaskOrganizerController mTaskOrganizerController;
ShellTaskOrganizer mOrganizer;
private final TransactionPool mTransactionPool = mock(TransactionPool.class);
private class TrackingTaskListener implements ShellTaskOrganizer.TaskListener {
final ArrayList<RunningTaskInfo> appeared = new ArrayList<>();
@@ -81,7 +85,7 @@ public class ShellTaskOrganizerTests {
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mOrganizer = new ShellTaskOrganizer(mTaskOrganizerController);
mOrganizer = new ShellTaskOrganizer(mTaskOrganizerController, mTransactionPool);
}
@Test

View File

@@ -45,6 +45,7 @@ import com.android.systemui.shared.tracing.ProtoTraceable;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.tracing.ProtoTracer;
import com.android.systemui.tracing.nano.SystemUiTraceProto;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.nano.WmShellTraceProto;
import com.android.wm.shell.onehanded.OneHanded;
@@ -76,6 +77,9 @@ public final class WMShell extends SystemUI implements ProtoTraceable<SystemUiTr
private final Optional<Pip> mPipOptional;
private final Optional<SplitScreen> mSplitScreenOptional;
private final Optional<OneHanded> mOneHandedOptional;
// Inject the organizer directly in case the optionals aren't loaded to depend on it. There
// are non-optional windowing features like FULLSCREEN.
private final ShellTaskOrganizer mShellTaskOrganizer;
private final ProtoTracer mProtoTracer;
@Inject
@@ -89,6 +93,7 @@ public final class WMShell extends SystemUI implements ProtoTraceable<SystemUiTr
Optional<Pip> pipOptional,
Optional<SplitScreen> splitScreenOptional,
Optional<OneHanded> oneHandedOptional,
ShellTaskOrganizer shellTaskOrganizer,
ProtoTracer protoTracer) {
super(context);
mCommandQueue = commandQueue;
@@ -101,6 +106,7 @@ public final class WMShell extends SystemUI implements ProtoTraceable<SystemUiTr
mPipOptional = pipOptional;
mSplitScreenOptional = splitScreenOptional;
mOneHandedOptional = oneHandedOptional;
mShellTaskOrganizer = shellTaskOrganizer;
mProtoTracer = protoTracer;
mProtoTracer.add(this);
}

View File

@@ -96,8 +96,8 @@ public abstract class WMShellBaseModule {
@SysUISingleton
@Provides
static ShellTaskOrganizer provideShellTaskOrganizer() {
ShellTaskOrganizer organizer = new ShellTaskOrganizer();
static ShellTaskOrganizer provideShellTaskOrganizer(TransactionPool transactionPool) {
ShellTaskOrganizer organizer = new ShellTaskOrganizer(transactionPool);
organizer.registerOrganizer();
return organizer;
}

View File

@@ -35,6 +35,7 @@ import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.tracing.ProtoTracer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.onehanded.OneHanded;
import com.android.wm.shell.onehanded.OneHandedGestureHandler;
@@ -64,6 +65,7 @@ public class WMShellTest extends SysuiTestCase {
@Mock Pip mPip;
@Mock SplitScreen mSplitScreen;
@Mock OneHanded mOneHanded;
@Mock ShellTaskOrganizer mTaskOrganizer;
@Mock ProtoTracer mProtoTracer;
@Before
@@ -72,7 +74,7 @@ public class WMShellTest extends SysuiTestCase {
mWMShell = new WMShell(mContext, mCommandQueue, mKeyguardUpdateMonitor,
mActivityManagerWrapper, mDisplayImeController, mNavigationModeController,
mScreenLifecycle, mSysUiState, Optional.of(mPip), Optional.of(mSplitScreen),
Optional.of(mOneHanded), mProtoTracer);
Optional.of(mOneHanded), mTaskOrganizer, mProtoTracer);
}
@Test

View File

@@ -16,6 +16,7 @@
package com.android.server.wm;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.WindowManager.INPUT_CONSUMER_NAVIGATION;
@@ -308,10 +309,13 @@ final class InputMonitor {
* This means we need to make sure that these changes in crop are reflected
* in the input windows, and so ensure this flag is set so that
* the input crop always reflects the surface hierarchy.
* we may have some issues with modal-windows, but I guess we can
* cross that bridge when we come to implementing full-screen TaskOrg
*
* TODO(b/168252846): we have some issues with modal-windows, so we need to
* cross that bridge now that we organize full-screen Tasks.
*/
if (child.getTask() != null && child.getTask().isOrganized()) {
if (child.getTask() != null
&& child.getTask().isOrganized()
&& child.getTask().getWindowingMode() != WINDOWING_MODE_FULLSCREEN) {
inputWindowHandle.replaceTouchableRegionWithCrop(null /* Use this surfaces crop */);
}

View File

@@ -1393,7 +1393,8 @@ class Task extends WindowContainer<WindowContainer> {
}
if (display != null) {
// TODO(NOW!): Chat with the erosky@ of this code to see if this really makes sense here...
// TODO(b/168037178): Chat with the erosky@ of this code to see if this really makes
// sense here...
// Rotations are relative to the display. This means if there are 2 displays rotated
// differently (eg. 2 monitors with one landscape and one portrait), moving a stack
// from one to the other could look like a rotation change. To prevent this
@@ -1407,11 +1408,16 @@ class Task extends WindowContainer<WindowContainer> {
super.onParentChanged(newParent, oldParent);
// TODO(NOW): The check for null display content and setting it to null doesn't really
// make sense here...
// Call this again after super onParentChanged in-case the surface wasn't created yet
// (happens when the task is first inserted into the hierarchy). It's a no-op if it
// already ran fully within super.onParentChanged
updateTaskOrganizerState(false /* forceUpdate */);
// TODO(stack-merge): This is mostly taking care of the case where the stask is removing from
// the display, so we should probably consolidate it there instead.
// TODO(b/168037178): The check for null display content and setting it to null doesn't
// really make sense here...
// TODO(b/168037178): This is mostly taking care of the case where the stask is removing
// from the display, so we should probably consolidate it there instead.
if (getParent() == null && mDisplayContent != null) {
EventLogTags.writeWmStackRemoved(getRootTaskId());
@@ -4849,6 +4855,11 @@ class Task extends WindowContainer<WindowContainer> {
* @return {@code true} if task organizer changed.
*/
boolean updateTaskOrganizerState(boolean forceUpdate) {
if (getSurfaceControl() == null) {
// Can't call onTaskAppeared without a surfacecontrol, so defer this until after one
// is created.
return false;
}
if (!isRootTask()) {
return setTaskOrganizer(null);
}

View File

@@ -18,10 +18,8 @@ package com.android.server.wm;
import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER;
import static com.android.server.wm.WindowOrganizerController.CONTROLLABLE_CONFIGS;
@@ -73,11 +71,9 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
// The set of modes that are currently supports
// TODO: Remove once the task organizer can support all modes
@VisibleForTesting
static final int[] SUPPORTED_WINDOWING_MODES = {
WINDOWING_MODE_PINNED,
WINDOWING_MODE_SPLIT_SCREEN_PRIMARY,
WINDOWING_MODE_SPLIT_SCREEN_SECONDARY,
WINDOWING_MODE_MULTI_WINDOW,
static final int[] UNSUPPORTED_WINDOWING_MODES = {
WINDOWING_MODE_UNDEFINED,
WINDOWING_MODE_FREEFORM
};
private final WindowManagerGlobalLock mGlobalLock;
@@ -311,18 +307,17 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
synchronized (mGlobalLock) {
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Register task organizer=%s uid=%d",
organizer.asBinder(), uid);
for (int winMode : SUPPORTED_WINDOWING_MODES) {
if (!mTaskOrganizerStates.containsKey(organizer.asBinder())) {
mTaskOrganizers.add(organizer);
mTaskOrganizerStates.put(organizer.asBinder(),
new TaskOrganizerState(organizer, uid));
}
mService.mRootWindowContainer.forAllTasks((task) -> {
if (task.getWindowingMode() == winMode) {
task.updateTaskOrganizerState(true /* forceUpdate */);
}
});
if (!mTaskOrganizerStates.containsKey(organizer.asBinder())) {
mTaskOrganizers.add(organizer);
mTaskOrganizerStates.put(organizer.asBinder(),
new TaskOrganizerState(organizer, uid));
}
mService.mRootWindowContainer.forAllTasks((task) -> {
if (ArrayUtils.contains(UNSUPPORTED_WINDOWING_MODES, task.getWindowingMode())) {
return;
}
task.updateTaskOrganizerState(true /* forceUpdate */);
});
}
} finally {
Binder.restoreCallingIdentity(origId);
@@ -360,12 +355,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
}
private boolean isSupportedWindowingMode(int winMode) {
for (int i = 0; i < SUPPORTED_WINDOWING_MODES.length; i++) {
if (SUPPORTED_WINDOWING_MODES[i] == winMode) {
return true;
}
}
return false;
return !ArrayUtils.contains(UNSUPPORTED_WINDOWING_MODES, winMode);
}
void onTaskAppeared(ITaskOrganizer organizer, Task task) {
@@ -659,21 +649,18 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
public void dump(PrintWriter pw, String prefix) {
final String innerPrefix = prefix + " ";
pw.print(prefix); pw.println("TaskOrganizerController:");
pw.print(innerPrefix); pw.println("Per windowing mode:");
for (int i = 0; i < SUPPORTED_WINDOWING_MODES.length; i++) {
final int windowingMode = SUPPORTED_WINDOWING_MODES[i];
pw.println(innerPrefix + " "
+ WindowConfiguration.windowingModeToString(windowingMode) + ":");
for (final TaskOrganizerState state : mTaskOrganizerStates.values()) {
final ArrayList<Task> tasks = state.mOrganizedTasks;
pw.print(innerPrefix + " ");
pw.println(state.mOrganizer.mTaskOrganizer + " uid=" + state.mUid + ":");
for (int k = 0; k < tasks.size(); k++) {
final Task task = tasks.get(k);
if (windowingMode == task.getWindowingMode()) {
pw.println(innerPrefix + " " + task);
}
for (final TaskOrganizerState state : mTaskOrganizerStates.values()) {
final ArrayList<Task> tasks = state.mOrganizedTasks;
pw.print(innerPrefix + " ");
pw.println(state.mOrganizer.mTaskOrganizer + " uid=" + state.mUid + ":");
for (int k = 0; k < tasks.size(); k++) {
final Task task = tasks.get(k);
final int mode = task.getWindowingMode();
if (ArrayUtils.contains(UNSUPPORTED_WINDOWING_MODES, mode)) {
continue;
}
pw.println(innerPrefix + " ("
+ WindowConfiguration.windowingModeToString(mode) + ") " + task);
}
}

View File

@@ -16,6 +16,7 @@
package com.android.server.wm;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
@@ -2464,8 +2465,9 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
private Animation loadAnimation(WindowManager.LayoutParams lp, int transit, boolean enter,
boolean isVoiceInteraction) {
if (isOrganized()) {
// Defer to the task organizer to run animations
if (isOrganized()
// TODO(b/161711458): Clean-up when moved to shell.
&& getWindowingMode() != WINDOWING_MODE_FULLSCREEN) {
return null;
}

View File

@@ -229,58 +229,32 @@ public class WindowOrganizerTests extends WindowTestsBase {
final Task task3 = createTask(stack3);
final ITaskOrganizer organizer = registerMockOrganizer();
// First organizer is registered, verify a task appears when changing windowing mode
stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
verify(organizer, times(1))
// verify that tasks are appeared on registration
verify(organizer, times(3))
.onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
assertTrue(stack.isOrganized());
// Now we replace the registration and1 verify the new organizer receives tasks
// newly entering the windowing mode.
final ITaskOrganizer organizer2 = registerMockOrganizer();
stack2.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
// One each for task and task2
verify(organizer2, times(2))
verify(organizer2, times(3))
.onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
verify(organizer2, times(0)).onTaskVanished(any());
// One for task
verify(organizer).onTaskVanished(any());
verify(organizer, times(3)).onTaskVanished(any());
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(3))
verify(organizer, times(6))
.onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
verify(organizer2, times(2)).onTaskVanished(any());
stack3.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
verify(organizer, times(4))
.onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
verify(organizer2, times(2)).onTaskVanished(any());
assertTrue(stack3.isOrganized());
}
@Test
public void testRegisterTaskOrganizerStackWindowingModeChanges() throws RemoteException {
final ITaskOrganizer organizer = registerMockOrganizer();
final Task stack = createStack();
final Task task = createTask(stack);
final Task task2 = createTask(stack);
stack.setWindowingMode(WINDOWING_MODE_PINNED);
verify(organizer, times(1))
.onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
verify(organizer, times(1)).onTaskVanished(any());
verify(organizer2, times(3)).onTaskVanished(any());
}
@Test
public void testRegisterTaskOrganizerWithExistingTasks() throws RemoteException {
final Task stack = createStack();
final Task task = createTask(stack);
stack.setWindowingMode(WINDOWING_MODE_PINNED);
final ITaskOrganizer organizer = registerMockOrganizer();
verify(organizer, times(1))