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 98c0b1e99fc49..6f681e8cc6c74 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java @@ -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 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 @@ -1407,11 +1408,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()); @@ -4849,6 +4855,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 5d8a2a11dce8d..11eaf8c5cea5c 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))