From 98f15cc9ea23b646e8745bc49b1ac1c55ca3c27c Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Wed, 20 Jul 2022 22:23:24 +0000 Subject: Remove ShellInit/ShellCommandHandler interfaces to SysUI - SysUI now calls init/commands/dump via the ShellController interface - Move ShellInit/ShellCommandHandler into the sysui package since it's only called on sysui startup Test: atest WMShellUnitTests Test: atest SystemUITests Bug: 238217847 Change-Id: I7b05dbab7648cb8a8c6f9381b0b5f545ed3398f1 --- .../com/android/wm/shell/ShellCommandHandler.java | 39 ---- .../android/wm/shell/ShellCommandHandlerImpl.java | 191 ----------------- .../Shell/src/com/android/wm/shell/ShellInit.java | 30 --- .../src/com/android/wm/shell/ShellInitImpl.java | 225 --------------------- .../android/wm/shell/dagger/WMShellBaseModule.java | 38 ++-- .../wm/shell/sysui/ShellCommandHandler.java | 166 +++++++++++++++ .../android/wm/shell/sysui/ShellController.java | 53 +++++ .../src/com/android/wm/shell/sysui/ShellInit.java | 211 +++++++++++++++++++ .../com/android/wm/shell/sysui/ShellInterface.java | 21 +- .../com/android/wm/shell/ShellInitImplTest.java | 131 ------------ .../src/com/android/wm/shell/ShellInitTest.java | 134 ++++++++++++ .../com/android/systemui/SystemUIInitializer.java | 2 - .../android/systemui/dagger/SysUIComponent.java | 4 - .../com/android/systemui/dagger/WMComponent.java | 19 +- .../src/com/android/systemui/wmshell/WMShell.java | 10 +- .../com/android/systemui/wmshell/WMShellTest.java | 10 +- 16 files changed, 614 insertions(+), 670 deletions(-) delete mode 100644 libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandler.java delete mode 100644 libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java delete mode 100644 libs/WindowManager/Shell/src/com/android/wm/shell/ShellInit.java delete mode 100644 libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java create mode 100644 libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellCommandHandler.java create mode 100644 libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellInit.java delete mode 100644 libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellInitImplTest.java create mode 100644 libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellInitTest.java diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandler.java deleted file mode 100644 index 73fd6931066d..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandler.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.wm.shell.common.annotations.ExternalThread; - -import java.io.PrintWriter; - -/** - * An entry point into the shell for dumping shell internal state and running adb commands. - * - * Use with {@code adb shell dumpsys activity service SystemUIService WMShell ...}. - */ -@ExternalThread -public interface ShellCommandHandler { - /** - * Dumps the shell state. - */ - void dump(PrintWriter pw); - - /** - * Handles a shell command. - */ - boolean handleCommand(final String[] args, PrintWriter pw); -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java deleted file mode 100644 index c5f7c19518a7..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Copyright (C) 2019 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 static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT; - -import com.android.wm.shell.common.ShellExecutor; -import com.android.wm.shell.hidedisplaycutout.HideDisplayCutoutController; -import com.android.wm.shell.kidsmode.KidsModeTaskOrganizer; -import com.android.wm.shell.onehanded.OneHandedController; -import com.android.wm.shell.pip.Pip; -import com.android.wm.shell.recents.RecentTasksController; -import com.android.wm.shell.splitscreen.SplitScreenController; - -import java.io.PrintWriter; -import java.util.Optional; - -/** - * An entry point into the shell for dumping shell internal state and running adb commands. - * - * Use with {@code adb shell dumpsys activity service SystemUIService WMShell ...}. - */ -public final class ShellCommandHandlerImpl { - private static final String TAG = ShellCommandHandlerImpl.class.getSimpleName(); - - private final Optional mSplitScreenOptional; - private final Optional mPipOptional; - private final Optional mOneHandedOptional; - private final Optional mHideDisplayCutout; - private final Optional mRecentTasks; - private final ShellTaskOrganizer mShellTaskOrganizer; - private final KidsModeTaskOrganizer mKidsModeTaskOrganizer; - private final ShellExecutor mMainExecutor; - private final HandlerImpl mImpl = new HandlerImpl(); - - public ShellCommandHandlerImpl( - ShellTaskOrganizer shellTaskOrganizer, - KidsModeTaskOrganizer kidsModeTaskOrganizer, - Optional splitScreenOptional, - Optional pipOptional, - Optional oneHandedOptional, - Optional hideDisplayCutout, - Optional recentTasks, - ShellExecutor mainExecutor) { - mShellTaskOrganizer = shellTaskOrganizer; - mKidsModeTaskOrganizer = kidsModeTaskOrganizer; - mRecentTasks = recentTasks; - mSplitScreenOptional = splitScreenOptional; - mPipOptional = pipOptional; - mOneHandedOptional = oneHandedOptional; - mHideDisplayCutout = hideDisplayCutout; - mMainExecutor = mainExecutor; - } - - public ShellCommandHandler asShellCommandHandler() { - return mImpl; - } - - /** Dumps WM Shell internal state. */ - private void dump(PrintWriter pw) { - mShellTaskOrganizer.dump(pw, ""); - pw.println(); - pw.println(); - mPipOptional.ifPresent(pip -> pip.dump(pw)); - mOneHandedOptional.ifPresent(oneHanded -> oneHanded.dump(pw)); - mHideDisplayCutout.ifPresent(hideDisplayCutout -> hideDisplayCutout.dump(pw)); - pw.println(); - pw.println(); - mSplitScreenOptional.ifPresent(splitScreen -> splitScreen.dump(pw, "")); - pw.println(); - pw.println(); - mRecentTasks.ifPresent(recentTasks -> recentTasks.dump(pw, "")); - pw.println(); - pw.println(); - mKidsModeTaskOrganizer.dump(pw, ""); - } - - - /** Returns {@code true} if command was found and executed. */ - private boolean handleCommand(final String[] args, PrintWriter pw) { - if (args.length < 2) { - // Argument at position 0 is "WMShell". - return false; - } - switch (args[1]) { - case "moveToSideStage": - return runMoveToSideStage(args, pw); - case "removeFromSideStage": - return runRemoveFromSideStage(args, pw); - case "setSideStagePosition": - return runSetSideStagePosition(args, pw); - case "help": - return runHelp(pw); - default: - return false; - } - } - - private boolean runMoveToSideStage(String[] args, PrintWriter pw) { - if (args.length < 3) { - // First arguments are "WMShell" and command name. - pw.println("Error: task id should be provided as arguments"); - return false; - } - final int taskId = new Integer(args[2]); - final int sideStagePosition = args.length > 3 - ? new Integer(args[3]) : SPLIT_POSITION_BOTTOM_OR_RIGHT; - mSplitScreenOptional.ifPresent(split -> split.moveToSideStage(taskId, sideStagePosition)); - return true; - } - - private boolean runRemoveFromSideStage(String[] args, PrintWriter pw) { - if (args.length < 3) { - // First arguments are "WMShell" and command name. - pw.println("Error: task id should be provided as arguments"); - return false; - } - final int taskId = new Integer(args[2]); - mSplitScreenOptional.ifPresent(split -> split.removeFromSideStage(taskId)); - return true; - } - - private boolean runSetSideStagePosition(String[] args, PrintWriter pw) { - if (args.length < 3) { - // First arguments are "WMShell" and command name. - pw.println("Error: side stage position should be provided as arguments"); - return false; - } - final int position = new Integer(args[2]); - mSplitScreenOptional.ifPresent(split -> split.setSideStagePosition(position)); - return true; - } - - private boolean runHelp(PrintWriter pw) { - pw.println("Window Manager Shell commands:"); - pw.println(" help"); - pw.println(" Print this help text."); - pw.println(" "); - pw.println(" Dump Window Manager Shell internal state"); - pw.println(" pair "); - pw.println(" unpair "); - pw.println(" Pairs/unpairs tasks with given ids."); - pw.println(" moveToSideStage "); - pw.println(" Move a task with given id in split-screen mode."); - pw.println(" removeFromSideStage "); - pw.println(" Remove a task with given id in split-screen mode."); - pw.println(" setSideStageOutline "); - pw.println(" Enable/Disable outline on the side-stage."); - pw.println(" setSideStagePosition "); - pw.println(" Sets the position of the side-stage."); - return true; - } - - private class HandlerImpl implements ShellCommandHandler { - @Override - public void dump(PrintWriter pw) { - try { - mMainExecutor.executeBlocking(() -> ShellCommandHandlerImpl.this.dump(pw)); - } catch (InterruptedException e) { - throw new RuntimeException("Failed to dump the Shell in 2s", e); - } - } - - @Override - public boolean handleCommand(String[] args, PrintWriter pw) { - try { - boolean[] result = new boolean[1]; - mMainExecutor.executeBlocking(() -> { - result[0] = ShellCommandHandlerImpl.this.handleCommand(args, pw); - }); - return result[0]; - } catch (InterruptedException e) { - throw new RuntimeException("Failed to handle Shell command in 2s", e); - } - } - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInit.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInit.java deleted file mode 100644 index d7010b174744..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInit.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.wm.shell.common.annotations.ExternalThread; - -/** - * An entry point into the shell for initializing shell internal state. - */ -@ExternalThread -public interface ShellInit { - /** - * Initializes the shell state. - */ - void init(); -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java deleted file mode 100644 index 6694e441084b..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java +++ /dev/null @@ -1,225 +0,0 @@ -/* - * Copyright (C) 2019 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 static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_FULLSCREEN; -import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_INIT; - -import android.os.Build; -import android.os.SystemClock; -import android.util.Pair; - -import androidx.annotation.VisibleForTesting; - -import com.android.internal.protolog.common.ProtoLog; -import com.android.wm.shell.activityembedding.ActivityEmbeddingController; -import com.android.wm.shell.bubbles.BubbleController; -import com.android.wm.shell.common.DisplayController; -import com.android.wm.shell.common.DisplayImeController; -import com.android.wm.shell.common.DisplayInsetsController; -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.fullscreen.FullscreenTaskListener; -import com.android.wm.shell.kidsmode.KidsModeTaskOrganizer; -import com.android.wm.shell.pip.phone.PipTouchHandler; -import com.android.wm.shell.recents.RecentTasksController; -import com.android.wm.shell.splitscreen.SplitScreenController; -import com.android.wm.shell.startingsurface.StartingWindowController; -import com.android.wm.shell.transition.DefaultMixedHandler; -import com.android.wm.shell.transition.Transitions; -import com.android.wm.shell.unfold.UnfoldAnimationController; -import com.android.wm.shell.unfold.UnfoldTransitionHandler; - -import java.util.ArrayList; -import java.util.Optional; - -/** - * The entry point implementation into the shell for initializing shell internal state. Classes - * which need to setup on start should inject an instance of this class and add an init callback. - */ -public class ShellInitImpl { - private static final String TAG = ShellInitImpl.class.getSimpleName(); - - private final DisplayController mDisplayController; - private final DisplayImeController mDisplayImeController; - private final DisplayInsetsController mDisplayInsetsController; - private final DragAndDropController mDragAndDropController; - private final ShellTaskOrganizer mShellTaskOrganizer; - private final KidsModeTaskOrganizer mKidsModeTaskOrganizer; - private final Optional mBubblesOptional; - private final Optional mSplitScreenOptional; - private final Optional mPipTouchHandlerOptional; - private final FullscreenTaskListener mFullscreenTaskListener; - private final Optional mUnfoldController; - private final Optional mUnfoldTransitionHandler; - private final Optional> mFreeformTaskListenerOptional; - private final ShellExecutor mMainExecutor; - private final Transitions mTransitions; - private final StartingWindowController mStartingWindow; - private final Optional mRecentTasks; - private final Optional mActivityEmbeddingOptional; - - private final InitImpl mImpl = new InitImpl(); - // An ordered list of init callbacks to be made once shell is first started - private final ArrayList> mInitCallbacks = new ArrayList<>(); - private boolean mHasInitialized; - - public ShellInitImpl( - DisplayController displayController, - DisplayImeController displayImeController, - DisplayInsetsController displayInsetsController, - DragAndDropController dragAndDropController, - ShellTaskOrganizer shellTaskOrganizer, - KidsModeTaskOrganizer kidsModeTaskOrganizer, - Optional bubblesOptional, - Optional splitScreenOptional, - Optional pipTouchHandlerOptional, - FullscreenTaskListener fullscreenTaskListener, - Optional unfoldAnimationController, - Optional unfoldTransitionHandler, - Optional> freeformTaskListenerOptional, - Optional recentTasks, - Optional activityEmbeddingOptional, - Transitions transitions, - StartingWindowController startingWindow, - ShellExecutor mainExecutor) { - mDisplayController = displayController; - mDisplayImeController = displayImeController; - mDisplayInsetsController = displayInsetsController; - mDragAndDropController = dragAndDropController; - mShellTaskOrganizer = shellTaskOrganizer; - mKidsModeTaskOrganizer = kidsModeTaskOrganizer; - mBubblesOptional = bubblesOptional; - mSplitScreenOptional = splitScreenOptional; - mFullscreenTaskListener = fullscreenTaskListener; - mPipTouchHandlerOptional = pipTouchHandlerOptional; - mUnfoldController = unfoldAnimationController; - mUnfoldTransitionHandler = unfoldTransitionHandler; - mFreeformTaskListenerOptional = freeformTaskListenerOptional; - mRecentTasks = recentTasks; - mActivityEmbeddingOptional = activityEmbeddingOptional; - mTransitions = transitions; - mMainExecutor = mainExecutor; - mStartingWindow = startingWindow; - } - - public ShellInit asShellInit() { - return mImpl; - } - - private void legacyInit() { - // Start listening for display and insets changes - mDisplayController.initialize(); - mDisplayInsetsController.initialize(); - mDisplayImeController.startMonitorDisplays(); - - // Setup the shell organizer - mShellTaskOrganizer.addListenerForType( - mFullscreenTaskListener, TASK_LISTENER_TYPE_FULLSCREEN); - mShellTaskOrganizer.initStartingWindow(mStartingWindow); - mShellTaskOrganizer.registerOrganizer(); - - mSplitScreenOptional.ifPresent(SplitScreenController::onOrganizerRegistered); - mBubblesOptional.ifPresent(BubbleController::initialize); - - // Bind the splitscreen impl to the drag drop controller - mDragAndDropController.initialize(mSplitScreenOptional); - - if (Transitions.ENABLE_SHELL_TRANSITIONS) { - mTransitions.register(mShellTaskOrganizer); - mActivityEmbeddingOptional.ifPresent(ActivityEmbeddingController::init); - mUnfoldTransitionHandler.ifPresent(UnfoldTransitionHandler::init); - if (mSplitScreenOptional.isPresent() && mPipTouchHandlerOptional.isPresent()) { - final DefaultMixedHandler mixedHandler = new DefaultMixedHandler(mTransitions, - mPipTouchHandlerOptional.get().getTransitionHandler(), - mSplitScreenOptional.get().getTransitionHandler()); - // Added at end so that it has highest priority. - mTransitions.addHandler(mixedHandler); - } - } - - // TODO(b/181599115): This should really be the pip controller, but until we can provide the - // 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)); - - mUnfoldController.ifPresent(UnfoldAnimationController::init); - mRecentTasks.ifPresent(RecentTasksController::init); - - // Initialize kids mode task organizer - mKidsModeTaskOrganizer.initialize(mStartingWindow); - } - - /** - * Adds a callback to the ordered list of callbacks be made when Shell is first started. This - * can be used in class constructors when dagger is used to ensure that the initialization order - * matches the dependency order. - */ - public void addInitCallback(Runnable r, T instance) { - if (mHasInitialized) { - if (Build.isDebuggable()) { - // All callbacks must be added prior to the Shell being initialized - throw new IllegalArgumentException("Can not add callback after init"); - } - return; - } - final String className = instance.getClass().getSimpleName(); - mInitCallbacks.add(new Pair<>(className, r)); - ProtoLog.v(WM_SHELL_INIT, "Adding init callback for %s", className); - } - - /** - * Calls all the init callbacks when the Shell is first starting. - */ - @VisibleForTesting - public void init() { - ProtoLog.v(WM_SHELL_INIT, "Initializing Shell Components: %d", mInitCallbacks.size()); - // Init in order of registration - for (int i = 0; i < mInitCallbacks.size(); i++) { - final Pair info = mInitCallbacks.get(i); - final long t1 = SystemClock.uptimeMillis(); - info.second.run(); - final long t2 = SystemClock.uptimeMillis(); - ProtoLog.v(WM_SHELL_INIT, "\t%s took %dms", info.first, (t2 - t1)); - } - mInitCallbacks.clear(); - - // TODO: To be removed - legacyInit(); - - mHasInitialized = true; - } - - @ExternalThread - private class InitImpl implements ShellInit { - @Override - public void init() { - try { - mMainExecutor.executeBlocking(ShellInitImpl.this::init); - } catch (InterruptedException e) { - throw new RuntimeException("Failed to initialize the Shell in 2s", e); - } - } - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java index 586eab07649f..f85f9d63a827 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java @@ -29,10 +29,8 @@ import com.android.internal.logging.UiEventLogger; import com.android.launcher3.icons.IconProvider; import com.android.wm.shell.RootDisplayAreaOrganizer; import com.android.wm.shell.RootTaskDisplayAreaOrganizer; -import com.android.wm.shell.ShellCommandHandler; -import com.android.wm.shell.ShellCommandHandlerImpl; -import com.android.wm.shell.ShellInit; -import com.android.wm.shell.ShellInitImpl; +import com.android.wm.shell.sysui.ShellCommandHandler; +import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.TaskViewFactory; import com.android.wm.shell.TaskViewFactoryController; @@ -624,13 +622,9 @@ public abstract class WMShellBaseModule { @WMSingleton @Provides - static ShellInit provideShellInit(ShellInitImpl impl) { - return impl.asShellInit(); - } - - @WMSingleton - @Provides - static ShellInitImpl provideShellInitImpl(DisplayController displayController, + static ShellInit provideShellInitImpl( + ShellController shellController, + DisplayController displayController, DisplayImeController displayImeController, DisplayInsetsController displayInsetsController, DragAndDropController dragAndDropController, @@ -648,7 +642,8 @@ public abstract class WMShellBaseModule { Transitions transitions, StartingWindowController startingWindow, @ShellMainThread ShellExecutor mainExecutor) { - return new ShellInitImpl(displayController, + return new ShellInit(shellController, + displayController, displayImeController, displayInsetsController, dragAndDropController, @@ -668,19 +663,10 @@ public abstract class WMShellBaseModule { mainExecutor); } - /** - * Note, this is only optional because we currently pass this to the SysUI component scope and - * for non-primary users, we may inject a null-optional for that dependency. - */ @WMSingleton @Provides - static Optional provideShellCommandHandler(ShellCommandHandlerImpl impl) { - return Optional.of(impl.asShellCommandHandler()); - } - - @WMSingleton - @Provides - static ShellCommandHandlerImpl provideShellCommandHandlerImpl( + static ShellCommandHandler provideShellCommandHandlerImpl( + ShellController shellController, ShellTaskOrganizer shellTaskOrganizer, KidsModeTaskOrganizer kidsModeTaskOrganizer, Optional splitScreenOptional, @@ -689,9 +675,9 @@ public abstract class WMShellBaseModule { Optional hideDisplayCutout, Optional recentTasksOptional, @ShellMainThread ShellExecutor mainExecutor) { - return new ShellCommandHandlerImpl(shellTaskOrganizer, kidsModeTaskOrganizer, - splitScreenOptional, pipOptional, oneHandedOptional, hideDisplayCutout, - recentTasksOptional, mainExecutor); + return new ShellCommandHandler(shellController, shellTaskOrganizer, + kidsModeTaskOrganizer, splitScreenOptional, pipOptional, oneHandedOptional, + hideDisplayCutout, recentTasksOptional, mainExecutor); } @WMSingleton diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellCommandHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellCommandHandler.java new file mode 100644 index 000000000000..0427efb30b9f --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellCommandHandler.java @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2019 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.sysui; + +import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT; + +import com.android.wm.shell.ShellTaskOrganizer; +import com.android.wm.shell.common.ShellExecutor; +import com.android.wm.shell.hidedisplaycutout.HideDisplayCutoutController; +import com.android.wm.shell.kidsmode.KidsModeTaskOrganizer; +import com.android.wm.shell.onehanded.OneHandedController; +import com.android.wm.shell.pip.Pip; +import com.android.wm.shell.recents.RecentTasksController; +import com.android.wm.shell.splitscreen.SplitScreenController; + +import java.io.PrintWriter; +import java.util.Optional; + +/** + * An entry point into the shell for dumping shell internal state and running adb commands. + * + * Use with {@code adb shell dumpsys activity service SystemUIService WMShell ...}. + */ +public final class ShellCommandHandler { + private static final String TAG = ShellCommandHandler.class.getSimpleName(); + + private final Optional mSplitScreenOptional; + private final Optional mPipOptional; + private final Optional mOneHandedOptional; + private final Optional mHideDisplayCutout; + private final Optional mRecentTasks; + private final ShellTaskOrganizer mShellTaskOrganizer; + private final KidsModeTaskOrganizer mKidsModeTaskOrganizer; + private final ShellExecutor mMainExecutor; + + public ShellCommandHandler( + ShellController shellController, + ShellTaskOrganizer shellTaskOrganizer, + KidsModeTaskOrganizer kidsModeTaskOrganizer, + Optional splitScreenOptional, + Optional pipOptional, + Optional oneHandedOptional, + Optional hideDisplayCutout, + Optional recentTasks, + ShellExecutor mainExecutor) { + mShellTaskOrganizer = shellTaskOrganizer; + mKidsModeTaskOrganizer = kidsModeTaskOrganizer; + mRecentTasks = recentTasks; + mSplitScreenOptional = splitScreenOptional; + mPipOptional = pipOptional; + mOneHandedOptional = oneHandedOptional; + mHideDisplayCutout = hideDisplayCutout; + mMainExecutor = mainExecutor; + // TODO(238217847): To be removed once the command handler dependencies are inverted + shellController.setShellCommandHandler(this); + } + + /** Dumps WM Shell internal state. */ + public void dump(PrintWriter pw) { + mShellTaskOrganizer.dump(pw, ""); + pw.println(); + pw.println(); + mPipOptional.ifPresent(pip -> pip.dump(pw)); + mOneHandedOptional.ifPresent(oneHanded -> oneHanded.dump(pw)); + mHideDisplayCutout.ifPresent(hideDisplayCutout -> hideDisplayCutout.dump(pw)); + pw.println(); + pw.println(); + mSplitScreenOptional.ifPresent(splitScreen -> splitScreen.dump(pw, "")); + pw.println(); + pw.println(); + mRecentTasks.ifPresent(recentTasks -> recentTasks.dump(pw, "")); + pw.println(); + pw.println(); + mKidsModeTaskOrganizer.dump(pw, ""); + } + + + /** Returns {@code true} if command was found and executed. */ + public boolean handleCommand(final String[] args, PrintWriter pw) { + if (args.length < 2) { + // Argument at position 0 is "WMShell". + return false; + } + switch (args[1]) { + case "moveToSideStage": + return runMoveToSideStage(args, pw); + case "removeFromSideStage": + return runRemoveFromSideStage(args, pw); + case "setSideStagePosition": + return runSetSideStagePosition(args, pw); + case "help": + return runHelp(pw); + default: + return false; + } + } + + private boolean runMoveToSideStage(String[] args, PrintWriter pw) { + if (args.length < 3) { + // First arguments are "WMShell" and command name. + pw.println("Error: task id should be provided as arguments"); + return false; + } + final int taskId = new Integer(args[2]); + final int sideStagePosition = args.length > 3 + ? new Integer(args[3]) : SPLIT_POSITION_BOTTOM_OR_RIGHT; + mSplitScreenOptional.ifPresent(split -> split.moveToSideStage(taskId, sideStagePosition)); + return true; + } + + private boolean runRemoveFromSideStage(String[] args, PrintWriter pw) { + if (args.length < 3) { + // First arguments are "WMShell" and command name. + pw.println("Error: task id should be provided as arguments"); + return false; + } + final int taskId = new Integer(args[2]); + mSplitScreenOptional.ifPresent(split -> split.removeFromSideStage(taskId)); + return true; + } + + private boolean runSetSideStagePosition(String[] args, PrintWriter pw) { + if (args.length < 3) { + // First arguments are "WMShell" and command name. + pw.println("Error: side stage position should be provided as arguments"); + return false; + } + final int position = new Integer(args[2]); + mSplitScreenOptional.ifPresent(split -> split.setSideStagePosition(position)); + return true; + } + + private boolean runHelp(PrintWriter pw) { + pw.println("Window Manager Shell commands:"); + pw.println(" help"); + pw.println(" Print this help text."); + pw.println(" "); + pw.println(" Dump Window Manager Shell internal state"); + pw.println(" pair "); + pw.println(" unpair "); + pw.println(" Pairs/unpairs tasks with given ids."); + pw.println(" moveToSideStage "); + pw.println(" Move a task with given id in split-screen mode."); + pw.println(" removeFromSideStage "); + pw.println(" Remove a task with given id in split-screen mode."); + pw.println(" setSideStageOutline "); + pw.println(" Enable/Disable outline on the side-stage."); + pw.println(" setSideStagePosition "); + pw.println(" Sets the position of the side-stage."); + return true; + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellController.java index 837acecef56f..618028c1544f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellController.java @@ -47,6 +47,9 @@ public class ShellController { private final ShellExecutor mMainExecutor; private final ShellInterfaceImpl mImpl = new ShellInterfaceImpl(); + private ShellInit mShellInit; + private ShellCommandHandler mShellCommandHandler; + private final CopyOnWriteArrayList mConfigChangeListeners = new CopyOnWriteArrayList<>(); private final CopyOnWriteArrayList mKeyguardChangeListeners = @@ -65,6 +68,24 @@ public class ShellController { return mImpl; } + /** + * Sets the init handler to call back to. + * TODO(238217847): This is only exposed this way until we can remove the dependencies from the + * init handler to other classes. + */ + public void setShellInit(ShellInit shellInit) { + mShellInit = shellInit; + } + + /** + * Sets the command handler to call back to. + * TODO(238217847): This is only exposed this way until we can remove the dependencies from the + * command handler to other classes. + */ + public void setShellCommandHandler(ShellCommandHandler shellCommandHandler) { + mShellCommandHandler = shellCommandHandler; + } + /** * Adds a new configuration listener. The configuration change callbacks are not made in any * particular order. @@ -164,6 +185,38 @@ public class ShellController { */ @ExternalThread private class ShellInterfaceImpl implements ShellInterface { + + @Override + public void onInit() { + try { + mMainExecutor.executeBlocking(() -> mShellInit.init()); + } catch (InterruptedException e) { + throw new RuntimeException("Failed to initialize the Shell in 2s", e); + } + } + + @Override + public void dump(PrintWriter pw) { + try { + mMainExecutor.executeBlocking(() -> mShellCommandHandler.dump(pw)); + } catch (InterruptedException e) { + throw new RuntimeException("Failed to dump the Shell in 2s", e); + } + } + + @Override + public boolean handleCommand(String[] args, PrintWriter pw) { + try { + boolean[] result = new boolean[1]; + mMainExecutor.executeBlocking(() -> { + result[0] = mShellCommandHandler.handleCommand(args, pw); + }); + return result[0]; + } catch (InterruptedException e) { + throw new RuntimeException("Failed to handle Shell command in 2s", e); + } + } + @Override public void onConfigurationChanged(Configuration newConfiguration) { mMainExecutor.execute(() -> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellInit.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellInit.java new file mode 100644 index 000000000000..2619b37b67d8 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellInit.java @@ -0,0 +1,211 @@ +/* + * Copyright (C) 2019 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.sysui; + +import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_FULLSCREEN; +import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_INIT; + +import android.os.Build; +import android.os.SystemClock; +import android.util.Pair; + +import androidx.annotation.VisibleForTesting; + +import com.android.internal.protolog.common.ProtoLog; +import com.android.wm.shell.ShellTaskOrganizer; +import com.android.wm.shell.activityembedding.ActivityEmbeddingController; +import com.android.wm.shell.bubbles.BubbleController; +import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.common.DisplayImeController; +import com.android.wm.shell.common.DisplayInsetsController; +import com.android.wm.shell.common.ShellExecutor; +import com.android.wm.shell.draganddrop.DragAndDropController; +import com.android.wm.shell.freeform.FreeformTaskListener; +import com.android.wm.shell.fullscreen.FullscreenTaskListener; +import com.android.wm.shell.kidsmode.KidsModeTaskOrganizer; +import com.android.wm.shell.pip.phone.PipTouchHandler; +import com.android.wm.shell.recents.RecentTasksController; +import com.android.wm.shell.splitscreen.SplitScreenController; +import com.android.wm.shell.startingsurface.StartingWindowController; +import com.android.wm.shell.transition.DefaultMixedHandler; +import com.android.wm.shell.transition.Transitions; +import com.android.wm.shell.unfold.UnfoldAnimationController; +import com.android.wm.shell.unfold.UnfoldTransitionHandler; + +import java.util.ArrayList; +import java.util.Optional; + +/** + * The entry point implementation into the shell for initializing shell internal state. Classes + * which need to setup on start should inject an instance of this class and add an init callback. + */ +public class ShellInit { + private static final String TAG = ShellInit.class.getSimpleName(); + + private final DisplayController mDisplayController; + private final DisplayImeController mDisplayImeController; + private final DisplayInsetsController mDisplayInsetsController; + private final DragAndDropController mDragAndDropController; + private final ShellTaskOrganizer mShellTaskOrganizer; + private final KidsModeTaskOrganizer mKidsModeTaskOrganizer; + private final Optional mBubblesOptional; + private final Optional mSplitScreenOptional; + private final Optional mPipTouchHandlerOptional; + private final FullscreenTaskListener mFullscreenTaskListener; + private final Optional mUnfoldController; + private final Optional mUnfoldTransitionHandler; + private final Optional> mFreeformTaskListenerOptional; + private final ShellExecutor mMainExecutor; + private final Transitions mTransitions; + private final StartingWindowController mStartingWindow; + private final Optional mRecentTasks; + private final Optional mActivityEmbeddingOptional; + + // An ordered list of init callbacks to be made once shell is first started + private final ArrayList> mInitCallbacks = new ArrayList<>(); + private boolean mHasInitialized; + + public ShellInit( + ShellController shellController, + DisplayController displayController, + DisplayImeController displayImeController, + DisplayInsetsController displayInsetsController, + DragAndDropController dragAndDropController, + ShellTaskOrganizer shellTaskOrganizer, + KidsModeTaskOrganizer kidsModeTaskOrganizer, + Optional bubblesOptional, + Optional splitScreenOptional, + Optional pipTouchHandlerOptional, + FullscreenTaskListener fullscreenTaskListener, + Optional unfoldAnimationController, + Optional unfoldTransitionHandler, + Optional> freeformTaskListenerOptional, + Optional recentTasks, + Optional activityEmbeddingOptional, + Transitions transitions, + StartingWindowController startingWindow, + ShellExecutor mainExecutor) { + mDisplayController = displayController; + mDisplayImeController = displayImeController; + mDisplayInsetsController = displayInsetsController; + mDragAndDropController = dragAndDropController; + mShellTaskOrganizer = shellTaskOrganizer; + mKidsModeTaskOrganizer = kidsModeTaskOrganizer; + mBubblesOptional = bubblesOptional; + mSplitScreenOptional = splitScreenOptional; + mFullscreenTaskListener = fullscreenTaskListener; + mPipTouchHandlerOptional = pipTouchHandlerOptional; + mUnfoldController = unfoldAnimationController; + mUnfoldTransitionHandler = unfoldTransitionHandler; + mFreeformTaskListenerOptional = freeformTaskListenerOptional; + mRecentTasks = recentTasks; + mActivityEmbeddingOptional = activityEmbeddingOptional; + mTransitions = transitions; + mMainExecutor = mainExecutor; + mStartingWindow = startingWindow; + // TODO(238217847): To be removed once the init dependencies are inverted + shellController.setShellInit(this); + } + + private void legacyInit() { + // Start listening for display and insets changes + mDisplayController.initialize(); + mDisplayInsetsController.initialize(); + mDisplayImeController.startMonitorDisplays(); + + // Setup the shell organizer + mShellTaskOrganizer.addListenerForType( + mFullscreenTaskListener, TASK_LISTENER_TYPE_FULLSCREEN); + mShellTaskOrganizer.initStartingWindow(mStartingWindow); + mShellTaskOrganizer.registerOrganizer(); + + mSplitScreenOptional.ifPresent(SplitScreenController::onOrganizerRegistered); + mBubblesOptional.ifPresent(BubbleController::initialize); + + // Bind the splitscreen impl to the drag drop controller + mDragAndDropController.initialize(mSplitScreenOptional); + + if (Transitions.ENABLE_SHELL_TRANSITIONS) { + mTransitions.register(mShellTaskOrganizer); + mActivityEmbeddingOptional.ifPresent(ActivityEmbeddingController::init); + mUnfoldTransitionHandler.ifPresent(UnfoldTransitionHandler::init); + if (mSplitScreenOptional.isPresent() && mPipTouchHandlerOptional.isPresent()) { + final DefaultMixedHandler mixedHandler = new DefaultMixedHandler(mTransitions, + mPipTouchHandlerOptional.get().getTransitionHandler(), + mSplitScreenOptional.get().getTransitionHandler()); + // Added at end so that it has highest priority. + mTransitions.addHandler(mixedHandler); + } + } + + // TODO(b/181599115): This should really be the pip controller, but until we can provide the + // 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)); + + mUnfoldController.ifPresent(UnfoldAnimationController::init); + mRecentTasks.ifPresent(RecentTasksController::init); + + // Initialize kids mode task organizer + mKidsModeTaskOrganizer.initialize(mStartingWindow); + } + + /** + * Adds a callback to the ordered list of callbacks be made when Shell is first started. This + * can be used in class constructors when dagger is used to ensure that the initialization order + * matches the dependency order. + */ + public void addInitCallback(Runnable r, T instance) { + if (mHasInitialized) { + if (Build.isDebuggable()) { + // All callbacks must be added prior to the Shell being initialized + throw new IllegalArgumentException("Can not add callback after init"); + } + return; + } + final String className = instance.getClass().getSimpleName(); + mInitCallbacks.add(new Pair<>(className, r)); + ProtoLog.v(WM_SHELL_INIT, "Adding init callback for %s", className); + } + + /** + * Calls all the init callbacks when the Shell is first starting. + */ + @VisibleForTesting + public void init() { + ProtoLog.v(WM_SHELL_INIT, "Initializing Shell Components: %d", mInitCallbacks.size()); + // Init in order of registration + for (int i = 0; i < mInitCallbacks.size(); i++) { + final Pair info = mInitCallbacks.get(i); + final long t1 = SystemClock.uptimeMillis(); + info.second.run(); + final long t2 = SystemClock.uptimeMillis(); + ProtoLog.v(WM_SHELL_INIT, "\t%s took %dms", info.first, (t2 - t1)); + } + mInitCallbacks.clear(); + + // TODO: To be removed + legacyInit(); + + mHasInitialized = true; + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellInterface.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellInterface.java index a15ce5d2b816..254c253b0042 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellInterface.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellInterface.java @@ -18,14 +18,31 @@ package com.android.wm.shell.sysui; import android.content.res.Configuration; +import java.io.PrintWriter; + /** * General interface for notifying the Shell of common SysUI events like configuration or keyguard * changes. - * - * TODO: Move ShellInit and ShellCommandHandler into this interface */ public interface ShellInterface { + /** + * Initializes the shell state. + */ + default void onInit() {} + + /** + * Dumps the shell state. + */ + default void dump(PrintWriter pw) {} + + /** + * Handles a shell command. + */ + default boolean handleCommand(final String[] args, PrintWriter pw) { + return false; + } + /** * Notifies the Shell that the configuration has changed. */ diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellInitImplTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellInitImplTest.java deleted file mode 100644 index ace8d365c7af..000000000000 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellInitImplTest.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright (C) 2022 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 static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import android.testing.AndroidTestingRunner; -import android.testing.TestableLooper; - -import androidx.test.filters.SmallTest; - -import com.android.wm.shell.activityembedding.ActivityEmbeddingController; -import com.android.wm.shell.bubbles.BubbleController; -import com.android.wm.shell.common.DisplayController; -import com.android.wm.shell.common.DisplayImeController; -import com.android.wm.shell.common.DisplayInsetsController; -import com.android.wm.shell.common.ShellExecutor; -import com.android.wm.shell.draganddrop.DragAndDropController; -import com.android.wm.shell.freeform.FreeformTaskListener; -import com.android.wm.shell.fullscreen.FullscreenTaskListener; -import com.android.wm.shell.kidsmode.KidsModeTaskOrganizer; -import com.android.wm.shell.pip.phone.PipTouchHandler; -import com.android.wm.shell.recents.RecentTasksController; -import com.android.wm.shell.splitscreen.SplitScreenController; -import com.android.wm.shell.startingsurface.StartingWindowController; -import com.android.wm.shell.transition.Transitions; -import com.android.wm.shell.unfold.UnfoldAnimationController; -import com.android.wm.shell.unfold.UnfoldTransitionHandler; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.util.ArrayList; -import java.util.Optional; - -@SmallTest -@RunWith(AndroidTestingRunner.class) -@TestableLooper.RunWithLooper(setAsMainLooper = true) -public class ShellInitImplTest extends ShellTestCase { - - @Mock private DisplayController mDisplayController; - @Mock private DisplayImeController mDisplayImeController; - @Mock private DisplayInsetsController mDisplayInsetsController; - @Mock private DragAndDropController mDragAndDropController; - @Mock private ShellTaskOrganizer mShellTaskOrganizer; - @Mock private KidsModeTaskOrganizer mKidsModeTaskOrganizer; - @Mock private Optional mBubblesOptional; - @Mock private Optional mSplitScreenOptional; - @Mock private Optional mPipTouchHandlerOptional; - @Mock private FullscreenTaskListener mFullscreenTaskListener; - @Mock private Optional mUnfoldAnimationController; - @Mock private Optional mUnfoldTransitionHandler; - @Mock private Optional> mFreeformTaskListenerOptional; - @Mock private Optional mRecentTasks; - @Mock private Optional mActivityEmbeddingController; - @Mock private Transitions mTransitions; - @Mock private StartingWindowController mStartingWindow; - @Mock private ShellExecutor mMainExecutor; - - private ShellInitImpl mImpl; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - mImpl = new ShellInitImpl(mDisplayController, mDisplayImeController, - mDisplayInsetsController, mDragAndDropController, mShellTaskOrganizer, - mKidsModeTaskOrganizer, mBubblesOptional, mSplitScreenOptional, - mPipTouchHandlerOptional, mFullscreenTaskListener, mUnfoldAnimationController, - mUnfoldTransitionHandler, mFreeformTaskListenerOptional, mRecentTasks, - mActivityEmbeddingController, mTransitions, mStartingWindow, mMainExecutor); - } - - @Test - public void testAddInitCallbacks_expectCalledInOrder() { - ArrayList results = new ArrayList<>(); - mImpl.addInitCallback(() -> { - results.add(1); - }, new Object()); - mImpl.addInitCallback(() -> { - results.add(2); - }, new Object()); - mImpl.addInitCallback(() -> { - results.add(3); - }, new Object()); - mImpl.init(); - assertTrue(results.get(0) == 1); - assertTrue(results.get(1) == 2); - assertTrue(results.get(2) == 3); - } - - @Test - public void testNoInitCallbacksAfterInit_expectException() { - mImpl.init(); - try { - mImpl.addInitCallback(() -> {}, new Object()); - fail("Expected exception when adding callback after init"); - } catch (IllegalArgumentException e) { - // Expected - } - } - - @Test - public void testDoubleInit_expectNoOp() { - ArrayList results = new ArrayList<>(); - mImpl.addInitCallback(() -> { - results.add(1); - }, new Object()); - mImpl.init(); - assertTrue(results.size() == 1); - mImpl.init(); - assertTrue(results.size() == 1); - } -} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellInitTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellInitTest.java new file mode 100644 index 000000000000..219e5ab6c651 --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellInitTest.java @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2022 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 static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; + +import androidx.test.filters.SmallTest; + +import com.android.wm.shell.activityembedding.ActivityEmbeddingController; +import com.android.wm.shell.bubbles.BubbleController; +import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.common.DisplayImeController; +import com.android.wm.shell.common.DisplayInsetsController; +import com.android.wm.shell.common.ShellExecutor; +import com.android.wm.shell.draganddrop.DragAndDropController; +import com.android.wm.shell.freeform.FreeformTaskListener; +import com.android.wm.shell.fullscreen.FullscreenTaskListener; +import com.android.wm.shell.kidsmode.KidsModeTaskOrganizer; +import com.android.wm.shell.pip.phone.PipTouchHandler; +import com.android.wm.shell.recents.RecentTasksController; +import com.android.wm.shell.splitscreen.SplitScreenController; +import com.android.wm.shell.startingsurface.StartingWindowController; +import com.android.wm.shell.sysui.ShellController; +import com.android.wm.shell.sysui.ShellInit; +import com.android.wm.shell.transition.Transitions; +import com.android.wm.shell.unfold.UnfoldAnimationController; +import com.android.wm.shell.unfold.UnfoldTransitionHandler; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.ArrayList; +import java.util.Optional; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper(setAsMainLooper = true) +public class ShellInitTest extends ShellTestCase { + + @Mock private ShellController mShellController; + @Mock private DisplayController mDisplayController; + @Mock private DisplayImeController mDisplayImeController; + @Mock private DisplayInsetsController mDisplayInsetsController; + @Mock private DragAndDropController mDragAndDropController; + @Mock private ShellTaskOrganizer mShellTaskOrganizer; + @Mock private KidsModeTaskOrganizer mKidsModeTaskOrganizer; + @Mock private Optional mBubblesOptional; + @Mock private Optional mSplitScreenOptional; + @Mock private Optional mPipTouchHandlerOptional; + @Mock private FullscreenTaskListener mFullscreenTaskListener; + @Mock private Optional mUnfoldAnimationController; + @Mock private Optional mUnfoldTransitionHandler; + @Mock private Optional> mFreeformTaskListenerOptional; + @Mock private Optional mRecentTasks; + @Mock private Optional mActivityEmbeddingController; + @Mock private Transitions mTransitions; + @Mock private StartingWindowController mStartingWindow; + @Mock private ShellExecutor mMainExecutor; + + private ShellInit mImpl; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mImpl = new ShellInit(mShellController, mDisplayController, mDisplayImeController, + mDisplayInsetsController, mDragAndDropController, mShellTaskOrganizer, + mKidsModeTaskOrganizer, mBubblesOptional, mSplitScreenOptional, + mPipTouchHandlerOptional, mFullscreenTaskListener, mUnfoldAnimationController, + mUnfoldTransitionHandler, mFreeformTaskListenerOptional, mRecentTasks, + mActivityEmbeddingController, mTransitions, mStartingWindow, mMainExecutor); + } + + @Test + public void testAddInitCallbacks_expectCalledInOrder() { + ArrayList results = new ArrayList<>(); + mImpl.addInitCallback(() -> { + results.add(1); + }, new Object()); + mImpl.addInitCallback(() -> { + results.add(2); + }, new Object()); + mImpl.addInitCallback(() -> { + results.add(3); + }, new Object()); + mImpl.init(); + assertTrue(results.get(0) == 1); + assertTrue(results.get(1) == 2); + assertTrue(results.get(2) == 3); + } + + @Test + public void testNoInitCallbacksAfterInit_expectException() { + mImpl.init(); + try { + mImpl.addInitCallback(() -> {}, new Object()); + fail("Expected exception when adding callback after init"); + } catch (IllegalArgumentException e) { + // Expected + } + } + + @Test + public void testDoubleInit_expectNoOp() { + ArrayList results = new ArrayList<>(); + mImpl.addInitCallback(() -> { + results.add(1); + }, new Object()); + mImpl.init(); + assertTrue(results.size() == 1); + mImpl.init(); + assertTrue(results.size() == 1); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java b/packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java index 24fcf15d7a11..e9ca0fdbb929 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java @@ -96,7 +96,6 @@ public abstract class SystemUIInitializer { .setSplitScreen(mWMComponent.getSplitScreen()) .setOneHanded(mWMComponent.getOneHanded()) .setBubbles(mWMComponent.getBubbles()) - .setShellCommandHandler(mWMComponent.getShellCommandHandler()) .setTaskViewFactory(mWMComponent.getTaskViewFactory()) .setTransitions(mWMComponent.getTransitions()) .setStartingSurface(mWMComponent.getStartingSurface()) @@ -112,7 +111,6 @@ public abstract class SystemUIInitializer { .setSplitScreen(Optional.ofNullable(null)) .setOneHanded(Optional.ofNullable(null)) .setBubbles(Optional.ofNullable(null)) - .setShellCommandHandler(Optional.ofNullable(null)) .setTaskViewFactory(Optional.ofNullable(null)) .setTransitions(new ShellTransitions() {}) .setDisplayAreaHelper(Optional.ofNullable(null)) diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java index fd7680f463c0..029cabb0bc0b 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java @@ -37,7 +37,6 @@ import com.android.systemui.unfold.FoldStateLoggingProvider; import com.android.systemui.unfold.SysUIUnfoldComponent; import com.android.systemui.unfold.UnfoldLatencyTracker; import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider; -import com.android.wm.shell.ShellCommandHandler; import com.android.wm.shell.TaskViewFactory; import com.android.wm.shell.back.BackAnimation; import com.android.wm.shell.bubbles.Bubbles; @@ -95,9 +94,6 @@ public interface SysUIComponent { @BindsInstance Builder setTaskViewFactory(Optional t); - @BindsInstance - Builder setShellCommandHandler(Optional shellDump); - @BindsInstance Builder setTransitions(ShellTransitions t); diff --git a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java index e4c0325d4d5a..78a45f9d3310 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java @@ -23,8 +23,8 @@ import androidx.annotation.Nullable; import com.android.systemui.SystemUIInitializerFactory; import com.android.systemui.tv.TvWMComponent; -import com.android.wm.shell.ShellCommandHandler; -import com.android.wm.shell.ShellInit; +import com.android.wm.shell.sysui.ShellCommandHandler; +import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.TaskViewFactory; import com.android.wm.shell.back.BackAnimation; import com.android.wm.shell.bubbles.Bubbles; @@ -75,17 +75,24 @@ public interface WMComponent { * Initializes all the WMShell components before starting any of the SystemUI components. */ default void init() { - getShellInit().init(); + // TODO(238217847): To be removed once the dependencies are inverted and ShellController can + // inject these classes directly, otherwise, it's currently needed to ensure that these + // classes are created and set on the controller before onInit() is called + getShellInit(); + getShellCommandHandler(); + getShell().onInit(); } @WMSingleton - ShellInit getShellInit(); + ShellInterface getShell(); + // TODO(238217847): To be removed once ShellController can inject ShellInit directly @WMSingleton - Optional getShellCommandHandler(); + ShellInit getShellInit(); + // TODO(238217847): To be removed once ShellController can inject ShellCommandHandler directly @WMSingleton - ShellInterface getShell(); + ShellCommandHandler getShellCommandHandler(); @WMSingleton Optional getOneHanded(); diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java index 83b0022b9f53..12597e0896b1 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java @@ -54,7 +54,6 @@ import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.UserInfoController; import com.android.systemui.tracing.ProtoTracer; import com.android.systemui.tracing.nano.SystemUiTraceProto; -import com.android.wm.shell.ShellCommandHandler; import com.android.wm.shell.nano.WmShellTraceProto; import com.android.wm.shell.onehanded.OneHanded; import com.android.wm.shell.onehanded.OneHandedEventCallback; @@ -107,7 +106,6 @@ public final class WMShell extends CoreStartable private final Optional mPipOptional; private final Optional mSplitScreenOptional; private final Optional mOneHandedOptional; - private final Optional mShellCommandHandler; private final CommandQueue mCommandQueue; private final ConfigurationController mConfigurationController; @@ -130,7 +128,6 @@ public final class WMShell extends CoreStartable Optional pipOptional, Optional splitScreenOptional, Optional oneHandedOptional, - Optional shellCommandHandler, CommandQueue commandQueue, ConfigurationController configurationController, KeyguardStateController keyguardStateController, @@ -154,7 +151,6 @@ public final class WMShell extends CoreStartable mOneHandedOptional = oneHandedOptional; mWakefulnessLifecycle = wakefulnessLifecycle; mProtoTracer = protoTracer; - mShellCommandHandler = shellCommandHandler; mUserInfoController = userInfoController; mSysUiMainExecutor = sysUiMainExecutor; } @@ -325,8 +321,7 @@ public final class WMShell extends CoreStartable @Override public void dump(PrintWriter pw, String[] args) { // Handle commands if provided - if (mShellCommandHandler.isPresent() - && mShellCommandHandler.get().handleCommand(args, pw)) { + if (mShell.handleCommand(args, pw)) { return; } // Handle logging commands if provided @@ -334,8 +329,7 @@ public final class WMShell extends CoreStartable return; } // Dump WMShell stuff here if no commands were handled - mShellCommandHandler.ifPresent( - shellCommandHandler -> shellCommandHandler.dump(pw)); + mShell.dump(pw); } @Override diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java index 72ade2632078..9c2136675dfa 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java @@ -33,7 +33,6 @@ import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.UserInfoController; import com.android.systemui.tracing.ProtoTracer; -import com.android.wm.shell.ShellCommandHandler; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.onehanded.OneHanded; import com.android.wm.shell.onehanded.OneHandedEventCallback; @@ -73,7 +72,6 @@ public class WMShellTest extends SysuiTestCase { @Mock OneHanded mOneHanded; @Mock WakefulnessLifecycle mWakefulnessLifecycle; @Mock ProtoTracer mProtoTracer; - @Mock ShellCommandHandler mShellCommandHandler; @Mock UserInfoController mUserInfoController; @Mock ShellExecutor mSysUiMainExecutor; @@ -82,10 +80,10 @@ public class WMShellTest extends SysuiTestCase { MockitoAnnotations.initMocks(this); mWMShell = new WMShell(mContext, mShellInterface, Optional.of(mPip), - Optional.of(mSplitScreen), Optional.of(mOneHanded), - Optional.of(mShellCommandHandler), mCommandQueue, mConfigurationController, - mKeyguardStateController, mKeyguardUpdateMonitor, mScreenLifecycle, mSysUiState, - mProtoTracer, mWakefulnessLifecycle, mUserInfoController, mSysUiMainExecutor); + Optional.of(mSplitScreen), Optional.of(mOneHanded), mCommandQueue, + mConfigurationController, mKeyguardStateController, mKeyguardUpdateMonitor, + mScreenLifecycle, mSysUiState, mProtoTracer, mWakefulnessLifecycle, + mUserInfoController, mSysUiMainExecutor); } @Test -- cgit v1.2.3-59-g8ed1b