From b5550919ff46aebbd24da0d8ed10ffd14693a564 Mon Sep 17 00:00:00 2001 From: "[1;3C" Date: Wed, 2 Sep 2020 13:22:50 -0700 Subject: [PATCH] Organize Fullscreen tasks Fix a bug where taskinfo changes weren't recording the changes. Add FullscreenTaskListener which is very basic and just shows surfaces on appear. Fullscreen works fine with this much because the surfaces are always at 0,0 anyways. Bug: 162499076 Test: Existing tests pass, phone works like normal Change-Id: I606bc1cbc85c33c4fc40baf6b3080a1eaa37550c --- .../Shell/res/raw/wm_shell_protolog.json | 12 ++++ .../wm/shell/FullscreenTaskListener.java | 69 +++++++++++++++++++ .../android/wm/shell/ShellTaskOrganizer.java | 11 ++- .../wm/shell/ShellTaskOrganizerTests.java | 6 +- .../com/android/systemui/wmshell/WMShell.java | 6 ++ .../systemui/wmshell/WMShellBaseModule.java | 4 +- .../android/systemui/wmshell/WMShellTest.java | 4 +- .../com/android/server/wm/InputMonitor.java | 10 ++- .../core/java/com/android/server/wm/Task.java | 21 ++++-- .../server/wm/TaskOrganizerController.java | 67 ++++++++---------- .../android/server/wm/WindowContainer.java | 6 +- .../server/wm/WindowOrganizerTests.java | 38 ++-------- 12 files changed, 166 insertions(+), 88 deletions(-) create mode 100644 libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java diff --git a/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json b/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json index 7242793580f92..a13e98c0d1ada 100644 --- a/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json +++ b/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json @@ -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", diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java new file mode 100644 index 0000000000000..d0051db9e9fcf --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java @@ -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 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) { + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java index f9ba695c85032..2d82fb1d3a212 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java @@ -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> 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 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 diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java index 497b6b7142818..7b499d4d6e7d3 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java @@ -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 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 diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java index c7a9af3642e55..0dbab46ee3211 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java @@ -49,6 +49,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.protolog.ShellProtoLogImpl; @@ -76,6 +77,9 @@ public final class WMShell extends SystemUI implements ProtoTraceable mPipOptional; private final Optional mSplitScreenOptional; private final Optional 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 pipOptional, Optional splitScreenOptional, Optional oneHandedOptional, + ShellTaskOrganizer shellTaskOrganizer, ProtoTracer protoTracer) { super(context); mCommandQueue = commandQueue; @@ -101,6 +106,7 @@ public final class WMShell extends SystemUI implements ProtoTraceable { } 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 @@ -1408,11 +1409,16 @@ class Task extends 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()); @@ -4850,6 +4856,11 @@ class Task extends 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); } diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java index d9290fb18f08d..e07c5677214bb 100644 --- a/services/core/java/com/android/server/wm/TaskOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java @@ -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 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 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); } } diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index 8a5e70f2e3536..ae8f7a556ffdc 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -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 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; } diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java index 38909f6a28d44..b783bd2a9c0ca 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java @@ -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))