From 9f2c659f1a7595bb754b3375ed66f7136ff892b2 Mon Sep 17 00:00:00 2001 From: Garfield Tan Date: Mon, 2 Aug 2021 16:58:08 -0700 Subject: [PATCH] Add a minimal FreeformTaskListener WM team starts to remove functionalities that were made for unmanaged tasks so take this step 1 to avoid regressions from this type of changes. Also remove UNSUPPORTED_WINDOWING_MODES because ShellTaskOrganizer can handle all windowing modes now. Bug: 192310211 Bug: 165794880 Bug: 165794636 Test: Maximize, drag and resize freeform windows. Change-Id: If130b449fe6cf37c39f0e1424e4f23cdb2d33dd5 --- .../com/android/wm/shell/ShellInitImpl.java | 9 ++ .../android/wm/shell/ShellTaskOrganizer.java | 5 + .../shell/freeform/FreeformTaskListener.java | 147 ++++++++++++++++++ .../systemui/wmshell/WMShellBaseModule.java | 10 ++ .../systemui/wmshell/WMShellModule.java | 13 ++ .../com/android/server/wm/DisplayContent.java | 4 +- .../com/android/server/wm/InputMonitor.java | 5 +- .../core/java/com/android/server/wm/Task.java | 5 +- .../server/wm/TaskOrganizerController.java | 29 +--- 9 files changed, 195 insertions(+), 32 deletions(-) create mode 100644 libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java index d1fbf31e2b993..9bbede311d6da 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java @@ -24,6 +24,7 @@ import com.android.wm.shell.common.DisplayImeController; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.annotations.ExternalThread; import com.android.wm.shell.draganddrop.DragAndDropController; +import com.android.wm.shell.freeform.FreeformTaskListener; import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController; import com.android.wm.shell.pip.phone.PipTouchHandler; import com.android.wm.shell.splitscreen.SplitScreenController; @@ -47,6 +48,7 @@ public class ShellInitImpl { private final Optional mAppPairsOptional; private final Optional mPipTouchHandlerOptional; private final FullscreenTaskListener mFullscreenTaskListener; + private final Optional mFreeformTaskListenerOptional; private final ShellExecutor mMainExecutor; private final Transitions mTransitions; private final StartingWindowController mStartingWindow; @@ -62,6 +64,7 @@ public class ShellInitImpl { Optional appPairsOptional, Optional pipTouchHandlerOptional, FullscreenTaskListener fullscreenTaskListener, + Optional> freeformTaskListenerOptional, Transitions transitions, StartingWindowController startingWindow, ShellExecutor mainExecutor) { @@ -74,6 +77,7 @@ public class ShellInitImpl { mAppPairsOptional = appPairsOptional; mFullscreenTaskListener = fullscreenTaskListener; mPipTouchHandlerOptional = pipTouchHandlerOptional; + mFreeformTaskListenerOptional = freeformTaskListenerOptional.flatMap(f -> f); mTransitions = transitions; mMainExecutor = mainExecutor; mStartingWindow = startingWindow; @@ -108,6 +112,11 @@ public class ShellInitImpl { // controller instead of the feature interface, can just initialize the touch handler if // needed mPipTouchHandlerOptional.ifPresent((handler) -> handler.init()); + + // Initialize optional freeform + mFreeformTaskListenerOptional.ifPresent(f -> + mShellTaskOrganizer.addListenerForType( + f, ShellTaskOrganizer.TASK_LISTENER_TYPE_FREEFORM)); } @ExternalThread 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 ba0ab6db1003c..ab8a21c58e9c8 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java @@ -71,12 +71,14 @@ public class ShellTaskOrganizer extends TaskOrganizer implements public static final int TASK_LISTENER_TYPE_FULLSCREEN = -2; public static final int TASK_LISTENER_TYPE_MULTI_WINDOW = -3; public static final int TASK_LISTENER_TYPE_PIP = -4; + public static final int TASK_LISTENER_TYPE_FREEFORM = -5; @IntDef(prefix = {"TASK_LISTENER_TYPE_"}, value = { TASK_LISTENER_TYPE_UNDEFINED, TASK_LISTENER_TYPE_FULLSCREEN, TASK_LISTENER_TYPE_MULTI_WINDOW, TASK_LISTENER_TYPE_PIP, + TASK_LISTENER_TYPE_FREEFORM, }) public @interface TaskListenerType {} @@ -572,6 +574,7 @@ public class ShellTaskOrganizer extends TaskOrganizer implements case WINDOWING_MODE_PINNED: return TASK_LISTENER_TYPE_PIP; case WINDOWING_MODE_FREEFORM: + return TASK_LISTENER_TYPE_FREEFORM; case WINDOWING_MODE_UNDEFINED: default: return TASK_LISTENER_TYPE_UNDEFINED; @@ -586,6 +589,8 @@ public class ShellTaskOrganizer extends TaskOrganizer implements return "TASK_LISTENER_TYPE_MULTI_WINDOW"; case TASK_LISTENER_TYPE_PIP: return "TASK_LISTENER_TYPE_PIP"; + case TASK_LISTENER_TYPE_FREEFORM: + return "TASK_LISTENER_TYPE_FREEFORM"; case TASK_LISTENER_TYPE_UNDEFINED: return "TASK_LISTENER_TYPE_UNDEFINED"; default: diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java new file mode 100644 index 0000000000000..5fb3297aa6d30 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2021 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.freeform; + +import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT; +import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT; + +import android.app.ActivityManager.RunningTaskInfo; +import android.content.Context; +import android.graphics.Point; +import android.graphics.Rect; +import android.provider.Settings; +import android.util.Slog; +import android.util.SparseArray; +import android.view.SurfaceControl; + +import com.android.internal.protolog.common.ProtoLog; +import com.android.wm.shell.ShellTaskOrganizer; +import com.android.wm.shell.common.SyncTransactionQueue; +import com.android.wm.shell.protolog.ShellProtoLogGroup; + +import java.io.PrintWriter; + +/** + * {@link ShellTaskOrganizer.TaskListener} for {@link + * ShellTaskOrganizer#TASK_LISTENER_TYPE_FREEFORM}. + */ +public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener { + private static final String TAG = "FreeformTaskListener"; + + private final SyncTransactionQueue mSyncQueue; + + private final SparseArray mTasks = new SparseArray<>(); + + private static class State { + RunningTaskInfo mTaskInfo; + SurfaceControl mLeash; + } + + public FreeformTaskListener(SyncTransactionQueue syncQueue) { + mSyncQueue = syncQueue; + } + + @Override + public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) { + if (mTasks.get(taskInfo.taskId) != null) { + throw new RuntimeException("Task appeared more than once: #" + taskInfo.taskId); + } + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Freeform Task Appeared: #%d", + taskInfo.taskId); + final State state = new State(); + state.mTaskInfo = taskInfo; + state.mLeash = leash; + mTasks.put(taskInfo.taskId, state); + + final Rect taskBounds = taskInfo.configuration.windowConfiguration.getBounds(); + mSyncQueue.runInSync(t -> { + Point taskPosition = taskInfo.positionInParent; + t.setPosition(leash, taskPosition.x, taskPosition.y) + .setWindowCrop(leash, taskBounds.width(), taskBounds.height()) + .show(leash); + }); + } + + @Override + public void onTaskVanished(RunningTaskInfo taskInfo) { + State state = mTasks.get(taskInfo.taskId); + if (state == null) { + Slog.e(TAG, "Task already vanished: #" + taskInfo.taskId); + return; + } + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Freeform Task Vanished: #%d", + taskInfo.taskId); + mTasks.remove(taskInfo.taskId); + } + + @Override + public void onTaskInfoChanged(RunningTaskInfo taskInfo) { + State state = mTasks.get(taskInfo.taskId); + if (state == null) { + throw new RuntimeException( + "Task info changed before appearing: #" + taskInfo.taskId); + } + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Freeform Task Info Changed: #%d", + taskInfo.taskId); + state.mTaskInfo = taskInfo; + + final Rect taskBounds = taskInfo.configuration.windowConfiguration.getBounds(); + final SurfaceControl leash = state.mLeash; + mSyncQueue.runInSync(t -> { + Point taskPosition = taskInfo.positionInParent; + t.setPosition(leash, taskPosition.x, taskPosition.y) + .setWindowCrop(leash, taskBounds.width(), taskBounds.height()) + .show(leash); + }); + } + + @Override + public void dump(PrintWriter pw, String prefix) { + final String innerPrefix = prefix + " "; + pw.println(prefix + this); + pw.println(innerPrefix + mTasks.size() + " tasks"); + } + + @Override + public String toString() { + return TAG; + } + + /** + * Checks if freeform support is enabled in system. + * + * @param context context used to check settings and package manager. + * @return {@code true} if freeform is enabled, {@code false} if not. + */ + public static boolean isFreeformEnabled(Context context) { + return context.getPackageManager().hasSystemFeature(FEATURE_FREEFORM_WINDOW_MANAGEMENT) + || Settings.Global.getInt(context.getContentResolver(), + DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT, 0) != 0; + } + + /** + * Creates {@link FreeformTaskListener} if freeform is enabled. + */ + public static FreeformTaskListener create(Context context, + SyncTransactionQueue syncQueue) { + if (!isFreeformEnabled(context)) { + return null; + } + + return new FreeformTaskListener(syncQueue); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java index 7e733a9721ef4..77bde770f79df 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java @@ -55,6 +55,7 @@ import com.android.wm.shell.common.annotations.ShellAnimationThread; import com.android.wm.shell.common.annotations.ShellMainThread; import com.android.wm.shell.common.annotations.ShellSplashscreenThread; import com.android.wm.shell.draganddrop.DragAndDropController; +import com.android.wm.shell.freeform.FreeformTaskListener; import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout; import com.android.wm.shell.hidedisplaycutout.HideDisplayCutoutController; import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; @@ -212,6 +213,13 @@ public abstract class WMShellBaseModule { return new FullscreenTaskListener(syncQueue); } + // + // Freeform (optional feature) + // + + @BindsOptionalOf + abstract Optional optionalFreeformTaskListener(); + // // Hide display cutout // @@ -451,6 +459,7 @@ public abstract class WMShellBaseModule { Optional appPairsOptional, Optional pipTouchHandlerOptional, FullscreenTaskListener fullscreenTaskListener, + Optional> freeformTaskListener, Transitions transitions, StartingWindowController startingWindow, @ShellMainThread ShellExecutor mainExecutor) { @@ -463,6 +472,7 @@ public abstract class WMShellBaseModule { appPairsOptional, pipTouchHandlerOptional, fullscreenTaskListener, + freeformTaskListener, transitions, startingWindow, mainExecutor); diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java index be7813e19b11d..6397ce680a828 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java @@ -36,6 +36,7 @@ import com.android.wm.shell.common.TaskStackListenerImpl; import com.android.wm.shell.common.TransactionPool; import com.android.wm.shell.common.annotations.ChoreographerSfVsync; import com.android.wm.shell.common.annotations.ShellMainThread; +import com.android.wm.shell.freeform.FreeformTaskListener; import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController; import com.android.wm.shell.onehanded.OneHandedController; import com.android.wm.shell.pip.Pip; @@ -88,6 +89,18 @@ public class WMShellModule { transactionPool); } + // + // Freeform + // + + @WMSingleton + @Provides + static Optional provideFreeformTaskListener( + Context context, + SyncTransactionQueue syncQueue) { + return Optional.ofNullable(FreeformTaskListener.create(context, syncQueue)); + } + // // Split/multiwindow // diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index bd4140c0dabd7..eb9e9d9490eb8 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -4592,7 +4592,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp return true; } - if (task.isOrganized()) { + // TODO(b/165794880): Freeform task organizer doesn't support drag-resize yet. Remove + // the special case when it does. + if (task.isOrganized() && task.getWindowingMode() != WINDOWING_MODE_FREEFORM) { return true; } diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java index 8c781a13f7db3..e969214432de4 100644 --- a/services/core/java/com/android/server/wm/InputMonitor.java +++ b/services/core/java/com/android/server/wm/InputMonitor.java @@ -307,7 +307,10 @@ final class InputMonitor { boolean useSurfaceCrop = false; final Task task = w.getTask(); if (task != null) { - if (task.isOrganized() && task.getWindowingMode() != WINDOWING_MODE_FULLSCREEN) { + // TODO(b/165794636): Remove the special case for freeform window once drag resizing is + // handled by WM shell. + if (task.isOrganized() && task.getWindowingMode() != WINDOWING_MODE_FULLSCREEN + && !task.inFreeformWindowingMode()) { // If the window is in a TaskManaged by a TaskOrganizer then most cropping will // be applied using the SurfaceControl hierarchy from the Organizer. This means // we need to make sure that these changes in crop are reflected in the input diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 2355dde0486ec..1ce0e87d2a3cb 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -4139,8 +4139,7 @@ class Task extends TaskFragment { } private boolean canBeOrganized() { - if (mForceNotOrganized || !mAtmService.mTaskOrganizerController - .isSupportedWindowingMode(getWindowingMode())) { + if (mForceNotOrganized) { return false; } // All root tasks can be organized @@ -4297,7 +4296,7 @@ class Task extends TaskFragment { final int windowingMode = getWindowingMode(); final TaskOrganizerController controller = mWmService.mAtmService.mTaskOrganizerController; - final ITaskOrganizer organizer = controller.getTaskOrganizer(windowingMode); + final ITaskOrganizer organizer = controller.getTaskOrganizer(); if (!forceUpdate && mTaskOrganizer == organizer) { return false; } diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java index 9ca89cb793623..5a3c092826331 100644 --- a/services/core/java/com/android/server/wm/TaskOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java @@ -16,9 +16,6 @@ package com.android.server.wm; -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.ActivityTaskManagerService.enforceTaskPermission; import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING; @@ -67,14 +64,6 @@ import java.util.function.Consumer; class TaskOrganizerController extends ITaskOrganizerController.Stub { private static final String TAG = "TaskOrganizerController"; - // The set of modes that are currently supports - // TODO: Remove once the task organizer can support all modes - @VisibleForTesting - static final int[] UNSUPPORTED_WINDOWING_MODES = { - WINDOWING_MODE_UNDEFINED, - WINDOWING_MODE_FREEFORM - }; - private class DeathRecipient implements IBinder.DeathRecipient { ITaskOrganizer mTaskOrganizer; @@ -493,11 +482,6 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { final TaskOrganizerState state = mTaskOrganizerStates.get(organizer.asBinder()); mService.mRootWindowContainer.forAllTasks((task) -> { - if (ArrayUtils.contains(UNSUPPORTED_WINDOWING_MODES, - task.getWindowingMode())) { - return; - } - boolean returnTask = !task.mCreatedByOrganizer; task.updateTaskOrganizerState(true /* forceUpdate */, returnTask /* skipTaskAppeared */); @@ -553,14 +537,8 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { /** * @return the task organizer key for a given windowing mode. */ - ITaskOrganizer getTaskOrganizer(int windowingMode) { - return isSupportedWindowingMode(windowingMode) - ? mTaskOrganizers.peekLast() - : null; - } - - boolean isSupportedWindowingMode(int winMode) { - return !ArrayUtils.contains(UNSUPPORTED_WINDOWING_MODES, winMode); + ITaskOrganizer getTaskOrganizer() { + return mTaskOrganizers.peekLast(); } boolean addStartingWindow(Task task, ActivityRecord activity, int launchTheme, @@ -1017,9 +995,6 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { 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); }