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.SplitLayout.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.splitscreen.SplitScreenController; 28 29 import java.io.PrintWriter; 30 import java.util.Optional; 31 32 /** 33 * An entry point into the shell for dumping shell internal state and running adb commands. 34 * 35 * Use with {@code adb shell dumpsys activity service SystemUIService WMShell ...}. 36 */ 37 public final class ShellCommandHandlerImpl { 38 private static final String TAG = ShellCommandHandlerImpl.class.getSimpleName(); 39 40 private final Optional<LegacySplitScreenController> mLegacySplitScreenOptional; 41 private final Optional<SplitScreenController> mSplitScreenOptional; 42 private final Optional<Pip> mPipOptional; 43 private final Optional<OneHandedController> mOneHandedOptional; 44 private final Optional<HideDisplayCutoutController> mHideDisplayCutout; 45 private final Optional<AppPairsController> mAppPairsOptional; 46 private final ShellTaskOrganizer mShellTaskOrganizer; 47 private final ShellExecutor mMainExecutor; 48 private final HandlerImpl mImpl = new HandlerImpl(); 49 ShellCommandHandlerImpl( ShellTaskOrganizer shellTaskOrganizer, Optional<LegacySplitScreenController> legacySplitScreenOptional, Optional<SplitScreenController> splitScreenOptional, Optional<Pip> pipOptional, Optional<OneHandedController> oneHandedOptional, Optional<HideDisplayCutoutController> hideDisplayCutout, Optional<AppPairsController> appPairsOptional, ShellExecutor mainExecutor)50 public ShellCommandHandlerImpl( 51 ShellTaskOrganizer shellTaskOrganizer, 52 Optional<LegacySplitScreenController> legacySplitScreenOptional, 53 Optional<SplitScreenController> splitScreenOptional, 54 Optional<Pip> pipOptional, 55 Optional<OneHandedController> oneHandedOptional, 56 Optional<HideDisplayCutoutController> hideDisplayCutout, 57 Optional<AppPairsController> appPairsOptional, 58 ShellExecutor mainExecutor) { 59 mShellTaskOrganizer = shellTaskOrganizer; 60 mLegacySplitScreenOptional = legacySplitScreenOptional; 61 mSplitScreenOptional = splitScreenOptional; 62 mPipOptional = pipOptional; 63 mOneHandedOptional = oneHandedOptional; 64 mHideDisplayCutout = hideDisplayCutout; 65 mAppPairsOptional = appPairsOptional; 66 mMainExecutor = mainExecutor; 67 } 68 asShellCommandHandler()69 public ShellCommandHandler asShellCommandHandler() { 70 return mImpl; 71 } 72 73 /** Dumps WM Shell internal state. */ dump(PrintWriter pw)74 private void dump(PrintWriter pw) { 75 mShellTaskOrganizer.dump(pw, ""); 76 pw.println(); 77 pw.println(); 78 mPipOptional.ifPresent(pip -> pip.dump(pw)); 79 mLegacySplitScreenOptional.ifPresent(splitScreen -> splitScreen.dump(pw)); 80 mOneHandedOptional.ifPresent(oneHanded -> oneHanded.dump(pw)); 81 mHideDisplayCutout.ifPresent(hideDisplayCutout -> hideDisplayCutout.dump(pw)); 82 pw.println(); 83 pw.println(); 84 mAppPairsOptional.ifPresent(appPairs -> appPairs.dump(pw, "")); 85 pw.println(); 86 pw.println(); 87 mSplitScreenOptional.ifPresent(splitScreen -> splitScreen.dump(pw, "")); 88 } 89 90 91 /** Returns {@code true} if command was found and executed. */ handleCommand(final String[] args, PrintWriter pw)92 private boolean handleCommand(final String[] args, PrintWriter pw) { 93 if (args.length < 2) { 94 // Argument at position 0 is "WMShell". 95 return false; 96 } 97 switch (args[1]) { 98 case "pair": 99 return runPair(args, pw); 100 case "unpair": 101 return runUnpair(args, pw); 102 case "moveToSideStage": 103 return runMoveToSideStage(args, pw); 104 case "removeFromSideStage": 105 return runRemoveFromSideStage(args, pw); 106 case "setSideStagePosition": 107 return runSetSideStagePosition(args, pw); 108 case "setSideStageVisibility": 109 return runSetSideStageVisibility(args, pw); 110 case "help": 111 return runHelp(pw); 112 default: 113 return false; 114 } 115 } 116 runPair(String[] args, PrintWriter pw)117 private boolean runPair(String[] args, PrintWriter pw) { 118 if (args.length < 4) { 119 // First two arguments are "WMShell" and command name. 120 pw.println("Error: two task ids should be provided as arguments"); 121 return false; 122 } 123 final int taskId1 = new Integer(args[2]); 124 final int taskId2 = new Integer(args[3]); 125 mAppPairsOptional.ifPresent(appPairs -> appPairs.pair(taskId1, taskId2)); 126 return true; 127 } 128 runUnpair(String[] args, PrintWriter pw)129 private boolean runUnpair(String[] args, PrintWriter pw) { 130 if (args.length < 3) { 131 // First two arguments are "WMShell" and command name. 132 pw.println("Error: task id should be provided as an argument"); 133 return false; 134 } 135 final int taskId = new Integer(args[2]); 136 mAppPairsOptional.ifPresent(appPairs -> appPairs.unpair(taskId)); 137 return true; 138 } 139 runMoveToSideStage(String[] args, PrintWriter pw)140 private boolean runMoveToSideStage(String[] args, PrintWriter pw) { 141 if (args.length < 3) { 142 // First arguments are "WMShell" and command name. 143 pw.println("Error: task id should be provided as arguments"); 144 return false; 145 } 146 final int taskId = new Integer(args[2]); 147 final int sideStagePosition = args.length > 3 148 ? new Integer(args[3]) : SPLIT_POSITION_BOTTOM_OR_RIGHT; 149 mSplitScreenOptional.ifPresent(split -> split.moveToSideStage(taskId, sideStagePosition)); 150 return true; 151 } 152 runRemoveFromSideStage(String[] args, PrintWriter pw)153 private boolean runRemoveFromSideStage(String[] args, PrintWriter pw) { 154 if (args.length < 3) { 155 // First arguments are "WMShell" and command name. 156 pw.println("Error: task id should be provided as arguments"); 157 return false; 158 } 159 final int taskId = new Integer(args[2]); 160 mSplitScreenOptional.ifPresent(split -> split.removeFromSideStage(taskId)); 161 return true; 162 } 163 runSetSideStagePosition(String[] args, PrintWriter pw)164 private boolean runSetSideStagePosition(String[] args, PrintWriter pw) { 165 if (args.length < 3) { 166 // First arguments are "WMShell" and command name. 167 pw.println("Error: side stage position should be provided as arguments"); 168 return false; 169 } 170 final int position = new Integer(args[2]); 171 mSplitScreenOptional.ifPresent(split -> split.setSideStagePosition(position)); 172 return true; 173 } 174 runSetSideStageVisibility(String[] args, PrintWriter pw)175 private boolean runSetSideStageVisibility(String[] args, PrintWriter pw) { 176 if (args.length < 3) { 177 // First arguments are "WMShell" and command name. 178 pw.println("Error: side stage position should be provided as arguments"); 179 return false; 180 } 181 final Boolean visible = new Boolean(args[2]); 182 183 mSplitScreenOptional.ifPresent(split -> split.setSideStageVisibility(visible)); 184 return true; 185 } 186 runHelp(PrintWriter pw)187 private boolean runHelp(PrintWriter pw) { 188 pw.println("Window Manager Shell commands:"); 189 pw.println(" help"); 190 pw.println(" Print this help text."); 191 pw.println(" <no arguments provided>"); 192 pw.println(" Dump Window Manager Shell internal state"); 193 pw.println(" pair <taskId1> <taskId2>"); 194 pw.println(" unpair <taskId>"); 195 pw.println(" Pairs/unpairs tasks with given ids."); 196 pw.println(" moveToSideStage <taskId> <SideStagePosition>"); 197 pw.println(" Move a task with given id in split-screen mode."); 198 pw.println(" removeFromSideStage <taskId>"); 199 pw.println(" Remove a task with given id in split-screen mode."); 200 pw.println(" setSideStagePosition <SideStagePosition>"); 201 pw.println(" Sets the position of the side-stage."); 202 pw.println(" setSideStageVisibility <true/false>"); 203 pw.println(" Show/hide side-stage."); 204 return true; 205 } 206 207 private class HandlerImpl implements ShellCommandHandler { 208 @Override dump(PrintWriter pw)209 public void dump(PrintWriter pw) { 210 try { 211 mMainExecutor.executeBlocking(() -> ShellCommandHandlerImpl.this.dump(pw)); 212 } catch (InterruptedException e) { 213 throw new RuntimeException("Failed to dump the Shell in 2s", e); 214 } 215 } 216 217 @Override handleCommand(String[] args, PrintWriter pw)218 public boolean handleCommand(String[] args, PrintWriter pw) { 219 try { 220 boolean[] result = new boolean[1]; 221 mMainExecutor.executeBlocking(() -> { 222 result[0] = ShellCommandHandlerImpl.this.handleCommand(args, pw); 223 }); 224 return result[0]; 225 } catch (InterruptedException e) { 226 throw new RuntimeException("Failed to handle Shell command in 2s", e); 227 } 228 } 229 } 230 } 231