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.sysui; 18 19 import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_INIT; 20 21 import com.android.internal.protolog.common.ProtoLog; 22 23 import java.io.PrintWriter; 24 import java.util.Arrays; 25 import java.util.TreeMap; 26 import java.util.function.BiConsumer; 27 28 /** 29 * An entry point into the shell for dumping shell internal state and running adb commands. 30 * 31 * Use with {@code adb shell dumpsys activity service SystemUIService WMShell ...}. 32 */ 33 public final class ShellCommandHandler { 34 private static final String TAG = ShellCommandHandler.class.getSimpleName(); 35 36 // We're using a TreeMap to keep them sorted by command name 37 private final TreeMap<String, BiConsumer<PrintWriter, String>> mDumpables = new TreeMap<>(); 38 private final TreeMap<String, ShellCommandActionHandler> mCommands = new TreeMap<>(); 39 40 public interface ShellCommandActionHandler { 41 /** 42 * Handles the given command. 43 * 44 * @param args the arguments starting with the action name, then the action arguments 45 * @param pw the write to print output to 46 */ onShellCommand(String[] args, PrintWriter pw)47 boolean onShellCommand(String[] args, PrintWriter pw); 48 49 /** 50 * Prints the help for this class of commands. Implementations do not need to print the 51 * command class. 52 */ printShellCommandHelp(PrintWriter pw, String prefix)53 void printShellCommandHelp(PrintWriter pw, String prefix); 54 } 55 56 57 /** 58 * Adds a callback to run when the Shell is being dumped. 59 * 60 * @param callback the callback to be made when Shell is dumped, takes the print writer and 61 * a prefix 62 * @param instance used for debugging only 63 */ addDumpCallback(BiConsumer<PrintWriter, String> callback, T instance)64 public <T> void addDumpCallback(BiConsumer<PrintWriter, String> callback, T instance) { 65 mDumpables.put(instance.getClass().getSimpleName(), callback); 66 ProtoLog.v(WM_SHELL_INIT, "Adding dump callback for %s", 67 instance.getClass().getSimpleName()); 68 } 69 70 /** 71 * Adds an action callback to be invoked when the user runs that particular command from adb. 72 * 73 * @param commandClass the top level class of command to invoke 74 * @param actions the interface to callback when an action of this class is invoked 75 * @param instance used for debugging only 76 */ addCommandCallback(String commandClass, ShellCommandActionHandler actions, T instance)77 public <T> void addCommandCallback(String commandClass, ShellCommandActionHandler actions, 78 T instance) { 79 mCommands.put(commandClass, actions); 80 ProtoLog.v(WM_SHELL_INIT, "Adding command class %s for %s", commandClass, 81 instance.getClass().getSimpleName()); 82 } 83 84 /** Dumps WM Shell internal state. */ dump(PrintWriter pw)85 public void dump(PrintWriter pw) { 86 for (String key : mDumpables.keySet()) { 87 final BiConsumer<PrintWriter, String> r = mDumpables.get(key); 88 r.accept(pw, ""); 89 pw.println(); 90 } 91 } 92 93 94 /** Returns {@code true} if command was found and executed. */ handleCommand(final String[] args, PrintWriter pw)95 public boolean handleCommand(final String[] args, PrintWriter pw) { 96 if (args.length < 2) { 97 // Argument at position 0 is "WMShell". 98 return false; 99 } 100 101 final String cmdClass = args[1]; 102 if (cmdClass.toLowerCase().equals("help")) { 103 return runHelp(pw); 104 } 105 if (!mCommands.containsKey(cmdClass)) { 106 return false; 107 } 108 109 // Only pass the actions onwards as arguments to the callback 110 final ShellCommandActionHandler actions = mCommands.get(args[1]); 111 final String[] cmdClassArgs = Arrays.copyOfRange(args, 2, args.length); 112 actions.onShellCommand(cmdClassArgs, pw); 113 return true; 114 } 115 runHelp(PrintWriter pw)116 private boolean runHelp(PrintWriter pw) { 117 pw.println("Window Manager Shell commands:"); 118 for (String commandClass : mCommands.keySet()) { 119 pw.println(" " + commandClass); 120 mCommands.get(commandClass).printShellCommandHelp(pw, " "); 121 } 122 pw.println(" help"); 123 pw.println(" Print this help text."); 124 pw.println(" <no arguments provided>"); 125 pw.println(" Dump all Window Manager Shell internal state"); 126 return true; 127 } 128 } 129