/* * 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.protolog.ShellProtoLogGroup.WM_SHELL_INIT; import com.android.internal.protolog.common.ProtoLog; import java.io.PrintWriter; import java.util.Arrays; import java.util.TreeMap; import java.util.function.BiConsumer; /** * 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(); // We're using a TreeMap to keep them sorted by command name private final TreeMap> mDumpables = new TreeMap<>(); private final TreeMap mCommands = new TreeMap<>(); public interface ShellCommandActionHandler { /** * Handles the given command. * * @param args the arguments starting with the action name, then the action arguments * @param pw the write to print output to */ boolean onShellCommand(String[] args, PrintWriter pw); /** * Prints the help for this class of commands. Implementations do not need to print the * command class. */ void printShellCommandHelp(PrintWriter pw, String prefix); } /** * Adds a callback to run when the Shell is being dumped. * * @param callback the callback to be made when Shell is dumped, takes the print writer and * a prefix * @param instance used for debugging only */ public void addDumpCallback(BiConsumer callback, T instance) { mDumpables.put(instance.getClass().getSimpleName(), callback); ProtoLog.v(WM_SHELL_INIT, "Adding dump callback for %s", instance.getClass().getSimpleName()); } /** * Adds an action callback to be invoked when the user runs that particular command from adb. * * @param commandClass the top level class of command to invoke * @param actions the interface to callback when an action of this class is invoked * @param instance used for debugging only */ public void addCommandCallback(String commandClass, ShellCommandActionHandler actions, T instance) { mCommands.put(commandClass, actions); ProtoLog.v(WM_SHELL_INIT, "Adding command class %s for %s", commandClass, instance.getClass().getSimpleName()); } /** Dumps WM Shell internal state. */ public void dump(PrintWriter pw) { for (String key : mDumpables.keySet()) { final BiConsumer r = mDumpables.get(key); r.accept(pw, ""); pw.println(); } } /** 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; } final String cmdClass = args[1]; if (cmdClass.toLowerCase().equals("help")) { return runHelp(pw); } if (!mCommands.containsKey(cmdClass)) { return false; } // Only pass the actions onwards as arguments to the callback final ShellCommandActionHandler actions = mCommands.get(args[1]); final String[] cmdClassArgs = Arrays.copyOfRange(args, 2, args.length); actions.onShellCommand(cmdClassArgs, pw); return true; } private boolean runHelp(PrintWriter pw) { pw.println("Window Manager Shell commands:"); for (String commandClass : mCommands.keySet()) { pw.println(" " + commandClass); mCommands.get(commandClass).printShellCommandHelp(pw, " "); } pw.println(" help"); pw.println(" Print this help text."); pw.println(" "); pw.println(" Dump all Window Manager Shell internal state"); return true; } }