1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.wm.shell;
18 
19 import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
20 
21 import com.android.wm.shell.apppairs.AppPairsController;
22 import com.android.wm.shell.common.ShellExecutor;
23 import com.android.wm.shell.hidedisplaycutout.HideDisplayCutoutController;
24 import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController;
25 import com.android.wm.shell.onehanded.OneHandedController;
26 import com.android.wm.shell.pip.Pip;
27 import com.android.wm.shell.recents.RecentTasksController;
28 import com.android.wm.shell.splitscreen.SplitScreenController;
29 
30 import java.io.PrintWriter;
31 import java.util.Optional;
32 
33 /**
34  * An entry point into the shell for dumping shell internal state and running adb commands.
35  *
36  * Use with {@code adb shell dumpsys activity service SystemUIService WMShell ...}.
37  */
38 public final class ShellCommandHandlerImpl {
39     private static final String TAG = ShellCommandHandlerImpl.class.getSimpleName();
40 
41     private final Optional<LegacySplitScreenController> mLegacySplitScreenOptional;
42     private final Optional<SplitScreenController> mSplitScreenOptional;
43     private final Optional<Pip> mPipOptional;
44     private final Optional<OneHandedController> mOneHandedOptional;
45     private final Optional<HideDisplayCutoutController> mHideDisplayCutout;
46     private final Optional<AppPairsController> mAppPairsOptional;
47     private final Optional<RecentTasksController> mRecentTasks;
48     private final ShellTaskOrganizer mShellTaskOrganizer;
49     private final ShellExecutor mMainExecutor;
50     private final HandlerImpl mImpl = new HandlerImpl();
51 
ShellCommandHandlerImpl( ShellTaskOrganizer shellTaskOrganizer, Optional<LegacySplitScreenController> legacySplitScreenOptional, Optional<SplitScreenController> splitScreenOptional, Optional<Pip> pipOptional, Optional<OneHandedController> oneHandedOptional, Optional<HideDisplayCutoutController> hideDisplayCutout, Optional<AppPairsController> appPairsOptional, Optional<RecentTasksController> recentTasks, ShellExecutor mainExecutor)52     public ShellCommandHandlerImpl(
53             ShellTaskOrganizer shellTaskOrganizer,
54             Optional<LegacySplitScreenController> legacySplitScreenOptional,
55             Optional<SplitScreenController> splitScreenOptional,
56             Optional<Pip> pipOptional,
57             Optional<OneHandedController> oneHandedOptional,
58             Optional<HideDisplayCutoutController> hideDisplayCutout,
59             Optional<AppPairsController> appPairsOptional,
60             Optional<RecentTasksController> recentTasks,
61             ShellExecutor mainExecutor) {
62         mShellTaskOrganizer = shellTaskOrganizer;
63         mRecentTasks = recentTasks;
64         mLegacySplitScreenOptional = legacySplitScreenOptional;
65         mSplitScreenOptional = splitScreenOptional;
66         mPipOptional = pipOptional;
67         mOneHandedOptional = oneHandedOptional;
68         mHideDisplayCutout = hideDisplayCutout;
69         mAppPairsOptional = appPairsOptional;
70         mMainExecutor = mainExecutor;
71     }
72 
asShellCommandHandler()73     public ShellCommandHandler asShellCommandHandler() {
74         return mImpl;
75     }
76 
77     /** Dumps WM Shell internal state. */
dump(PrintWriter pw)78     private void dump(PrintWriter pw) {
79         mShellTaskOrganizer.dump(pw, "");
80         pw.println();
81         pw.println();
82         mPipOptional.ifPresent(pip -> pip.dump(pw));
83         mLegacySplitScreenOptional.ifPresent(splitScreen -> splitScreen.dump(pw));
84         mOneHandedOptional.ifPresent(oneHanded -> oneHanded.dump(pw));
85         mHideDisplayCutout.ifPresent(hideDisplayCutout -> hideDisplayCutout.dump(pw));
86         pw.println();
87         pw.println();
88         mAppPairsOptional.ifPresent(appPairs -> appPairs.dump(pw, ""));
89         pw.println();
90         pw.println();
91         mSplitScreenOptional.ifPresent(splitScreen -> splitScreen.dump(pw, ""));
92         pw.println();
93         pw.println();
94         mRecentTasks.ifPresent(recentTasks -> recentTasks.dump(pw, ""));
95     }
96 
97 
98     /** Returns {@code true} if command was found and executed. */
handleCommand(final String[] args, PrintWriter pw)99     private boolean handleCommand(final String[] args, PrintWriter pw) {
100         if (args.length < 2) {
101             // Argument at position 0 is "WMShell".
102             return false;
103         }
104         switch (args[1]) {
105             case "pair":
106                 return runPair(args, pw);
107             case "unpair":
108                 return runUnpair(args, pw);
109             case "moveToSideStage":
110                 return runMoveToSideStage(args, pw);
111             case "removeFromSideStage":
112                 return runRemoveFromSideStage(args, pw);
113             case "setSideStagePosition":
114                 return runSetSideStagePosition(args, pw);
115             case "setSideStageVisibility":
116                 return runSetSideStageVisibility(args, pw);
117             case "help":
118                 return runHelp(pw);
119             default:
120                 return false;
121         }
122     }
123 
runPair(String[] args, PrintWriter pw)124     private boolean runPair(String[] args, PrintWriter pw) {
125         if (args.length < 4) {
126             // First two arguments are "WMShell" and command name.
127             pw.println("Error: two task ids should be provided as arguments");
128             return false;
129         }
130         final int taskId1 = new Integer(args[2]);
131         final int taskId2 = new Integer(args[3]);
132         mAppPairsOptional.ifPresent(appPairs -> appPairs.pair(taskId1, taskId2));
133         return true;
134     }
135 
runUnpair(String[] args, PrintWriter pw)136     private boolean runUnpair(String[] args, PrintWriter pw) {
137         if (args.length < 3) {
138             // First two arguments are "WMShell" and command name.
139             pw.println("Error: task id should be provided as an argument");
140             return false;
141         }
142         final int taskId = new Integer(args[2]);
143         mAppPairsOptional.ifPresent(appPairs -> appPairs.unpair(taskId));
144         return true;
145     }
146 
runMoveToSideStage(String[] args, PrintWriter pw)147     private boolean runMoveToSideStage(String[] args, PrintWriter pw) {
148         if (args.length < 3) {
149             // First arguments are "WMShell" and command name.
150             pw.println("Error: task id should be provided as arguments");
151             return false;
152         }
153         final int taskId = new Integer(args[2]);
154         final int sideStagePosition = args.length > 3
155                 ? new Integer(args[3]) : SPLIT_POSITION_BOTTOM_OR_RIGHT;
156         mSplitScreenOptional.ifPresent(split -> split.moveToSideStage(taskId, sideStagePosition));
157         return true;
158     }
159 
runRemoveFromSideStage(String[] args, PrintWriter pw)160     private boolean runRemoveFromSideStage(String[] args, PrintWriter pw) {
161         if (args.length < 3) {
162             // First arguments are "WMShell" and command name.
163             pw.println("Error: task id should be provided as arguments");
164             return false;
165         }
166         final int taskId = new Integer(args[2]);
167         mSplitScreenOptional.ifPresent(split -> split.removeFromSideStage(taskId));
168         return true;
169     }
170 
runSetSideStagePosition(String[] args, PrintWriter pw)171     private boolean runSetSideStagePosition(String[] args, PrintWriter pw) {
172         if (args.length < 3) {
173             // First arguments are "WMShell" and command name.
174             pw.println("Error: side stage position should be provided as arguments");
175             return false;
176         }
177         final int position = new Integer(args[2]);
178         mSplitScreenOptional.ifPresent(split -> split.setSideStagePosition(position));
179         return true;
180     }
181 
runSetSideStageVisibility(String[] args, PrintWriter pw)182     private boolean runSetSideStageVisibility(String[] args, PrintWriter pw) {
183         if (args.length < 3) {
184             // First arguments are "WMShell" and command name.
185             pw.println("Error: side stage visibility should be provided as arguments");
186             return false;
187         }
188         final Boolean visible = new Boolean(args[2]);
189 
190         mSplitScreenOptional.ifPresent(split -> split.setSideStageVisibility(visible));
191         return true;
192     }
193 
runHelp(PrintWriter pw)194     private boolean runHelp(PrintWriter pw) {
195         pw.println("Window Manager Shell commands:");
196         pw.println("  help");
197         pw.println("      Print this help text.");
198         pw.println("  <no arguments provided>");
199         pw.println("    Dump Window Manager Shell internal state");
200         pw.println("  pair <taskId1> <taskId2>");
201         pw.println("  unpair <taskId>");
202         pw.println("    Pairs/unpairs tasks with given ids.");
203         pw.println("  moveToSideStage <taskId> <SideStagePosition>");
204         pw.println("    Move a task with given id in split-screen mode.");
205         pw.println("  removeFromSideStage <taskId>");
206         pw.println("    Remove a task with given id in split-screen mode.");
207         pw.println("  setSideStageOutline <true/false>");
208         pw.println("    Enable/Disable outline on the side-stage.");
209         pw.println("  setSideStagePosition <SideStagePosition>");
210         pw.println("    Sets the position of the side-stage.");
211         pw.println("  setSideStageVisibility <true/false>");
212         pw.println("    Show/hide side-stage.");
213         return true;
214     }
215 
216     private class HandlerImpl implements ShellCommandHandler {
217         @Override
dump(PrintWriter pw)218         public void dump(PrintWriter pw) {
219             try {
220                 mMainExecutor.executeBlocking(() -> ShellCommandHandlerImpl.this.dump(pw));
221             } catch (InterruptedException e) {
222                 throw new RuntimeException("Failed to dump the Shell in 2s", e);
223             }
224         }
225 
226         @Override
handleCommand(String[] args, PrintWriter pw)227         public boolean handleCommand(String[] args, PrintWriter pw) {
228             try {
229                 boolean[] result = new boolean[1];
230                 mMainExecutor.executeBlocking(() -> {
231                     result[0] = ShellCommandHandlerImpl.this.handleCommand(args, pw);
232                 });
233                 return result[0];
234             } catch (InterruptedException e) {
235                 throw new RuntimeException("Failed to handle Shell command in 2s", e);
236             }
237         }
238     }
239 }
240