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