Changed|Deprecated: Move from shell command background mode to command runner

This starts the support for adb, root and other custom runners for shell commands. Previously only terminal and background tasks in app shells were supported.

`TERMUX_SERVICE.EXTRA_BACKGROUND` and `RUN_COMMAND_SERVICE.EXTRA_BACKGROUND` extras have been deprecated and instead respective `EXTRA_RUNNER` extra keys should be used. Currently supported extra values are `terminal-session` and `app-shell`. In future, `adb-shell` and `root-shell` are planned to be supported as well.
This commit is contained in:
agnostic-apollo
2022-03-10 02:51:56 +05:00
parent 05283bd774
commit b94dc7eea9
7 changed files with 121 additions and 29 deletions

View File

@@ -24,6 +24,7 @@ import com.termux.shared.logger.Logger;
import com.termux.shared.notification.NotificationUtils;
import com.termux.app.utils.PluginUtils;
import com.termux.shared.shell.command.ExecutionCommand;
import com.termux.shared.shell.command.ExecutionCommand.Runner;
/**
* A service that receives {@link RUN_COMMAND_SERVICE#ACTION_RUN_COMMAND} intent from third party apps and
@@ -100,7 +101,17 @@ public class RunCommandService extends Service {
executionCommand.stdin = IntentUtils.getStringExtraIfSet(intent, RUN_COMMAND_SERVICE.EXTRA_STDIN, null);
executionCommand.workingDirectory = IntentUtils.getStringExtraIfSet(intent, RUN_COMMAND_SERVICE.EXTRA_WORKDIR, null);
executionCommand.inBackground = intent.getBooleanExtra(RUN_COMMAND_SERVICE.EXTRA_BACKGROUND, false);
// If EXTRA_RUNNER is passed, use that, otherwise check EXTRA_BACKGROUND and default to Runner.TERMINAL_SESSION
executionCommand.runner = IntentUtils.getStringExtraIfSet(intent, RUN_COMMAND_SERVICE.EXTRA_RUNNER,
(intent.getBooleanExtra(RUN_COMMAND_SERVICE.EXTRA_BACKGROUND, false) ? Runner.APP_SHELL.getName() : Runner.TERMINAL_SESSION.getName()));
if (Runner.runnerOf(executionCommand.runner) == null) {
errmsg = this.getString(R.string.error_run_command_service_invalid_execution_command_runner, executionCommand.runner);
executionCommand.setStateFailed(Errno.ERRNO_FAILED.getCode(), errmsg);
PluginUtils.processPluginExecutionCommandError(this, LOG_TAG, executionCommand, false);
return stopService();
}
executionCommand.backgroundCustomLogLevel = IntentUtils.getIntegerExtraIfSet(intent, RUN_COMMAND_SERVICE.EXTRA_BACKGROUND_CUSTOM_LOG_LEVEL, null);
executionCommand.sessionAction = intent.getStringExtra(RUN_COMMAND_SERVICE.EXTRA_SESSION_ACTION);
executionCommand.commandLabel = IntentUtils.getStringExtraIfSet(intent, RUN_COMMAND_SERVICE.EXTRA_COMMAND_LABEL, "RUN_COMMAND Execution Intent Command");
@@ -195,7 +206,7 @@ public class RunCommandService extends Service {
execIntent.putExtra(TERMUX_SERVICE.EXTRA_ARGUMENTS, executionCommand.arguments);
execIntent.putExtra(TERMUX_SERVICE.EXTRA_STDIN, executionCommand.stdin);
if (executionCommand.workingDirectory != null && !executionCommand.workingDirectory.isEmpty()) execIntent.putExtra(TERMUX_SERVICE.EXTRA_WORKDIR, executionCommand.workingDirectory);
execIntent.putExtra(TERMUX_SERVICE.EXTRA_BACKGROUND, executionCommand.inBackground);
execIntent.putExtra(TERMUX_SERVICE.EXTRA_RUNNER, executionCommand.runner);
execIntent.putExtra(TERMUX_SERVICE.EXTRA_BACKGROUND_CUSTOM_LOG_LEVEL, DataUtils.getStringFromInteger(executionCommand.backgroundCustomLogLevel, null));
execIntent.putExtra(TERMUX_SERVICE.EXTRA_SESSION_ACTION, executionCommand.sessionAction);
execIntent.putExtra(TERMUX_SERVICE.EXTRA_COMMAND_LABEL, executionCommand.commandLabel);

View File

@@ -40,6 +40,7 @@ import com.termux.shared.notification.NotificationUtils;
import com.termux.shared.android.PermissionUtils;
import com.termux.shared.data.DataUtils;
import com.termux.shared.shell.command.ExecutionCommand;
import com.termux.shared.shell.command.ExecutionCommand.Runner;
import com.termux.terminal.TerminalEmulator;
import com.termux.terminal.TerminalSession;
import com.termux.terminal.TerminalSessionClient;
@@ -353,7 +354,17 @@ public final class TermuxService extends Service implements AppShell.AppShellCli
ExecutionCommand executionCommand = new ExecutionCommand(getNextExecutionId());
executionCommand.executableUri = intent.getData();
executionCommand.inBackground = intent.getBooleanExtra(TERMUX_SERVICE.EXTRA_BACKGROUND, false);
executionCommand.isPluginExecutionCommand = true;
// If EXTRA_RUNNER is passed, use that, otherwise check EXTRA_BACKGROUND and default to Runner.TERMINAL_SESSION
executionCommand.runner = IntentUtils.getStringExtraIfSet(intent, TERMUX_SERVICE.EXTRA_RUNNER,
(intent.getBooleanExtra(TERMUX_SERVICE.EXTRA_BACKGROUND, false) ? Runner.APP_SHELL.getName() : Runner.TERMINAL_SESSION.getName()));
if (Runner.runnerOf(executionCommand.runner) == null) {
String errmsg = this.getString(R.string.error_termux_service_invalid_execution_command_runner, executionCommand.runner);
executionCommand.setStateFailed(Errno.ERRNO_FAILED.getCode(), errmsg);
PluginUtils.processPluginExecutionCommandError(this, LOG_TAG, executionCommand, false);
return;
}
if (executionCommand.executableUri != null) {
Logger.logVerbose(LOG_TAG, "uri: \"" + executionCommand.executableUri + "\", path: \"" + executionCommand.executableUri.getPath() + "\", fragment: \"" + executionCommand.executableUri.getFragment() + "\"");
@@ -361,7 +372,7 @@ public final class TermuxService extends Service implements AppShell.AppShellCli
// Get full path including fragment (anything after last "#")
executionCommand.executable = UriUtils.getUriFilePathWithFragment(executionCommand.executableUri);
executionCommand.arguments = IntentUtils.getStringArrayExtraIfSet(intent, TERMUX_SERVICE.EXTRA_ARGUMENTS, null);
if (executionCommand.inBackground)
if (Runner.APP_SHELL.equalsRunner(executionCommand.runner))
executionCommand.stdin = IntentUtils.getStringExtraIfSet(intent, TERMUX_SERVICE.EXTRA_STDIN, null);
executionCommand.backgroundCustomLogLevel = IntentUtils.getIntegerExtraIfSet(intent, TERMUX_SERVICE.EXTRA_BACKGROUND_CUSTOM_LOG_LEVEL, null);
}
@@ -373,7 +384,6 @@ public final class TermuxService extends Service implements AppShell.AppShellCli
executionCommand.commandDescription = IntentUtils.getStringExtraIfSet(intent, TERMUX_SERVICE.EXTRA_COMMAND_DESCRIPTION, null);
executionCommand.commandHelp = IntentUtils.getStringExtraIfSet(intent, TERMUX_SERVICE.EXTRA_COMMAND_HELP, null);
executionCommand.pluginAPIHelp = IntentUtils.getStringExtraIfSet(intent, TERMUX_SERVICE.EXTRA_PLUGIN_API_HELP, null);
executionCommand.isPluginExecutionCommand = true;
executionCommand.resultConfig.resultPendingIntent = intent.getParcelableExtra(TERMUX_SERVICE.EXTRA_PENDING_INTENT);
executionCommand.resultConfig.resultDirectoryPath = IntentUtils.getStringExtraIfSet(intent, TERMUX_SERVICE.EXTRA_RESULT_DIRECTORY, null);
if (executionCommand.resultConfig.resultDirectoryPath != null) {
@@ -387,10 +397,14 @@ public final class TermuxService extends Service implements AppShell.AppShellCli
// Add the execution command to pending plugin execution commands list
mPendingPluginExecutionCommands.add(executionCommand);
if (executionCommand.inBackground) {
executeTermuxTaskCommand(executionCommand);
} else {
executeTermuxSessionCommand(executionCommand);
if (Runner.APP_SHELL.equalsRunner(executionCommand.runner))
executeTermuxTaskCommand(executionCommand);
else if (Runner.TERMINAL_SESSION.equalsRunner(executionCommand.runner))
executeTermuxSessionCommand(executionCommand);
else {
String errmsg = this.getString(R.string.error_termux_service_unsupported_execution_command_runner, executionCommand.runner);
executionCommand.setStateFailed(Errno.ERRNO_FAILED.getCode(), errmsg);
PluginUtils.processPluginExecutionCommandError(this, LOG_TAG, executionCommand, false);
}
}
@@ -410,7 +424,7 @@ public final class TermuxService extends Service implements AppShell.AppShellCli
/** Create a TermuxTask. */
@Nullable
public AppShell createTermuxTask(String executablePath, String[] arguments, String stdin, String workingDirectory) {
return createTermuxTask(new ExecutionCommand(getNextExecutionId(), executablePath, arguments, stdin, workingDirectory, true, false));
return createTermuxTask(new ExecutionCommand(getNextExecutionId(), executablePath, arguments, stdin, workingDirectory, Runner.APP_SHELL.getName(), false));
}
/** Create a TermuxTask. */
@@ -420,8 +434,8 @@ public final class TermuxService extends Service implements AppShell.AppShellCli
Logger.logDebug(LOG_TAG, "Creating \"" + executionCommand.getCommandIdAndLabelLogString() + "\" TermuxTask");
if (!executionCommand.inBackground) {
Logger.logDebug(LOG_TAG, "Ignoring a foreground execution command passed to createTermuxTask()");
if (!Runner.APP_SHELL.equalsRunner(executionCommand.runner)) {
Logger.logDebug(LOG_TAG, "Ignoring wrong runner \"" + executionCommand.runner + "\" command passed to createTermuxTask()");
return null;
}
@@ -502,7 +516,7 @@ public final class TermuxService extends Service implements AppShell.AppShellCli
*/
@Nullable
public TermuxSession createTermuxSession(String executablePath, String[] arguments, String stdin, String workingDirectory, boolean isFailSafe, String sessionName) {
return createTermuxSession(new ExecutionCommand(getNextExecutionId(), executablePath, arguments, stdin, workingDirectory, false, isFailSafe), sessionName);
return createTermuxSession(new ExecutionCommand(getNextExecutionId(), executablePath, arguments, stdin, workingDirectory, Runner.TERMINAL_SESSION.getName(), isFailSafe), sessionName);
}
/** Create a {@link TermuxSession}. */
@@ -512,8 +526,8 @@ public final class TermuxService extends Service implements AppShell.AppShellCli
Logger.logDebug(LOG_TAG, "Creating \"" + executionCommand.getCommandIdAndLabelLogString() + "\" TermuxSession");
if (executionCommand.inBackground) {
Logger.logDebug(LOG_TAG, "Ignoring a background execution command passed to createTermuxSession()");
if (!Runner.TERMINAL_SESSION.equalsRunner(executionCommand.runner)) {
Logger.logDebug(LOG_TAG, "Ignoring wrong runner \"" + executionCommand.runner + "\" command passed to createTermuxSession()");
return null;
}

View File

@@ -98,11 +98,14 @@
<string name="error_display_over_other_apps_permission_not_granted">&TERMUX_APP_NAME; requires
\"Display over other apps\" permission to start terminal sessions from background on Android >= 10.
Grants it from Settings -> Apps -> &TERMUX_APP_NAME; -> Advanced</string>
<string name="error_termux_service_invalid_execution_command_runner">Invalid execution command runner to TermuxService: `%1$s`</string>
<string name="error_termux_service_unsupported_execution_command_runner">Unsupported execution command runner to TermuxService: `%1$s`</string>
<!-- Termux RunCommandService -->
<string name="error_run_command_service_invalid_intent_action">Invalid intent action to RunCommandService: `%1$s`</string>
<string name="error_run_command_service_invalid_execution_command_runner">Invalid execution command runner to RunCommandService: `%1$s`</string>
<string name="error_run_command_service_mandatory_extra_missing">Mandatory extra missing to RunCommandService: \"%1$s\"</string>
<string name="error_run_command_service_api_help">Visit %1$s for more info on RUN_COMMAND Intent usage.</string>

View File

@@ -4,6 +4,7 @@ import android.content.Intent;
import android.net.Uri;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.termux.shared.data.IntentUtils;
import com.termux.shared.shell.command.result.ResultConfig;
@@ -13,6 +14,7 @@ import com.termux.shared.logger.Logger;
import com.termux.shared.markdown.MarkdownUtils;
import com.termux.shared.data.DataUtils;
import com.termux.shared.shell.command.runner.app.AppShell;
import com.termux.terminal.TerminalSession;
import java.util.Collections;
import java.util.List;
@@ -52,8 +54,56 @@ public class ExecutionCommand {
return value;
}
}
public enum Runner {
/** Run command in {@link TerminalSession}. */
TERMINAL_SESSION("terminal-session"),
/** Run command in {@link AppShell}. */
APP_SHELL("app-shell");
///** Run command in {@link AdbShell}. */
//ADB_SHELL("adb-shell"),
///** Run command in {@link RootShell}. */
//ROOT_SHELL("root-shell");
private final String name;
Runner(final String name) {
this.name = name;
}
public String getName() {
return name;
}
public boolean equalsRunner(String runner) {
return runner != null && runner.equals(this.name);
}
/** Get {@link Runner} for {@code name} if found, otherwise {@code null}. */
@Nullable
public static Runner runnerOf(String name) {
for (Runner v : Runner.values()) {
if (v.name.equals(name)) {
return v;
}
}
return null;
}
/** Get {@link Runner} for {@code name} if found, otherwise {@code def}. */
@NonNull
public static Runner runnerOf(@Nullable String name, @NonNull Runner def) {
Runner runner = runnerOf(name);
return runner != null ? runner : def;
}
}
/** The optional unique id for the {@link ExecutionCommand}. */
public Integer id;
@@ -83,8 +133,9 @@ public class ExecutionCommand {
public Integer terminalTranscriptRows;
/** If the {@link ExecutionCommand} is a background or a foreground terminal session command. */
public boolean inBackground;
/** The {@link Runner} for the {@link ExecutionCommand}. */
public String runner;
/** If the {@link ExecutionCommand} is meant to start a failsafe terminal session. */
public boolean isFailsafe;
@@ -145,13 +196,13 @@ public class ExecutionCommand {
this.id = id;
}
public ExecutionCommand(Integer id, String executable, String[] arguments, String stdin, String workingDirectory, boolean inBackground, boolean isFailsafe) {
public ExecutionCommand(Integer id, String executable, String[] arguments, String stdin, String workingDirectory, String runner, boolean isFailsafe) {
this.id = id;
this.executable = executable;
this.arguments = arguments;
this.stdin = stdin;
this.workingDirectory = workingDirectory;
this.inBackground = inBackground;
this.runner = runner;
this.isFailsafe = isFailsafe;
}
@@ -278,10 +329,10 @@ public class ExecutionCommand {
logString.append("\n").append(executionCommand.getExecutableLogString());
logString.append("\n").append(executionCommand.getArgumentsLogString());
logString.append("\n").append(executionCommand.getWorkingDirectoryLogString());
logString.append("\n").append(executionCommand.getInBackgroundLogString());
logString.append("\n").append(executionCommand.getRunnerLogString());
logString.append("\n").append(executionCommand.getIsFailsafeLogString());
if (executionCommand.inBackground) {
if (Runner.APP_SHELL.equalsRunner(executionCommand.runner)) {
if (logStdin && (!ignoreNull || !DataUtils.isNullOrEmpty(executionCommand.stdin)))
logString.append("\n").append(executionCommand.getStdinLogString());
@@ -372,10 +423,10 @@ public class ExecutionCommand {
markdownString.append("\n").append(MarkdownUtils.getSingleLineMarkdownStringEntry("Executable", executionCommand.executable, "-"));
markdownString.append("\n").append(getArgumentsMarkdownString(executionCommand.arguments));
markdownString.append("\n").append(MarkdownUtils.getSingleLineMarkdownStringEntry("Working Directory", executionCommand.workingDirectory, "-"));
markdownString.append("\n").append(MarkdownUtils.getSingleLineMarkdownStringEntry("inBackground", executionCommand.inBackground, "-"));
markdownString.append("\n").append(MarkdownUtils.getSingleLineMarkdownStringEntry("Runner", executionCommand.runner, "-"));
markdownString.append("\n").append(MarkdownUtils.getSingleLineMarkdownStringEntry("isFailsafe", executionCommand.isFailsafe, "-"));
if (executionCommand.inBackground) {
if (Runner.APP_SHELL.equalsRunner(executionCommand.runner)) {
if (!DataUtils.isNullOrEmpty(executionCommand.stdin))
markdownString.append("\n").append(MarkdownUtils.getMultiLineMarkdownStringEntry("Stdin", executionCommand.stdin, "-"));
if (executionCommand.backgroundCustomLogLevel != null)
@@ -450,8 +501,8 @@ public class ExecutionCommand {
return "Working Directory: `" + workingDirectory + "`";
}
public String getInBackgroundLogString() {
return "inBackground: `" + inBackground + "`";
public String getRunnerLogString() {
return Logger.getSingleLineLogStringEntry("Runner", runner, "-");
}
public String getIsFailsafeLogString() {

View File

@@ -8,7 +8,7 @@ import java.util.Formatter;
import java.util.List;
/*
* Version: v0.35.0
* Version: v0.36.0
* SPDX-License-Identifier: MIT
*
* Changelog
@@ -217,6 +217,9 @@ import java.util.List;
*
* - 0.35.0 (2022-01-28)
* - Add `TERMUX_APP.TERMUX_ACTIVITY.EXTRA_RECREATE_ACTIVITY`.
*
* - 0.36.0 (2022-03-10)
* - Added `TERMUX_APP.TERMUX_SERVICE.EXTRA_RUNNER` and `TERMUX_APP.RUN_COMMAND_SERVICE.EXTRA_RUNNER`
*/
/**
@@ -915,7 +918,10 @@ public final class TermuxConstants {
/** Intent {@code String} extra for command current working directory for the TERMUX_SERVICE.ACTION_SERVICE_EXECUTE intent */
public static final String EXTRA_WORKDIR = TERMUX_PACKAGE_NAME + ".execute.cwd"; // Default: "com.termux.execute.cwd"
/** Intent {@code boolean} extra for command background mode for the TERMUX_SERVICE.ACTION_SERVICE_EXECUTE intent */
@Deprecated
public static final String EXTRA_BACKGROUND = TERMUX_PACKAGE_NAME + ".execute.background"; // Default: "com.termux.execute.background"
/** Intent {@code boolean} extra for command the {@link com.termux.shared.shell.command.ExecutionCommand.Runner} for the TERMUX_SERVICE.ACTION_SERVICE_EXECUTE intent */
public static final String EXTRA_RUNNER = TERMUX_PACKAGE_NAME + ".execute.runner"; // Default: "com.termux.execute.runner"
/** Intent {@code String} extra for custom log level for background commands defined by {@link com.termux.shared.logger.Logger} for the TERMUX_SERVICE.ACTION_SERVICE_EXECUTE intent */
public static final String EXTRA_BACKGROUND_CUSTOM_LOG_LEVEL = TERMUX_PACKAGE_NAME + ".execute.background_custom_log_level"; // Default: "com.termux.execute.background_custom_log_level"
/** Intent {@code String} extra for session action for foreground commands for the TERMUX_SERVICE.ACTION_SERVICE_EXECUTE intent */
@@ -1045,7 +1051,10 @@ public final class TermuxConstants {
/** Intent {@code String} extra for current working directory of command for the RUN_COMMAND_SERVICE.ACTION_RUN_COMMAND intent */
public static final String EXTRA_WORKDIR = TERMUX_PACKAGE_NAME + ".RUN_COMMAND_WORKDIR"; // Default: "com.termux.RUN_COMMAND_WORKDIR"
/** Intent {@code boolean} extra for whether to run command in background or foreground terminal session for the RUN_COMMAND_SERVICE.ACTION_RUN_COMMAND intent */
@Deprecated
public static final String EXTRA_BACKGROUND = TERMUX_PACKAGE_NAME + ".RUN_COMMAND_BACKGROUND"; // Default: "com.termux.RUN_COMMAND_BACKGROUND"
/** Intent {@code boolean} extra for command the {@link com.termux.shared.shell.command.ExecutionCommand.Runner} for the RUN_COMMAND_SERVICE.ACTION_RUN_COMMAND intent */
public static final String EXTRA_RUNNER = TERMUX_PACKAGE_NAME + ".RUN_COMMAND_RUNNER"; // Default: "com.termux.RUN_COMMAND_RUNNER"
/** Intent {@code String} extra for custom log level for background commands defined by {@link com.termux.shared.logger.Logger} for the RUN_COMMAND_SERVICE.ACTION_RUN_COMMAND intent */
public static final String EXTRA_BACKGROUND_CUSTOM_LOG_LEVEL = TERMUX_PACKAGE_NAME + ".RUN_COMMAND_BACKGROUND_CUSTOM_LOG_LEVEL"; // Default: "com.termux.RUN_COMMAND_BACKGROUND_CUSTOM_LOG_LEVEL"
/** Intent {@code String} extra for session action of foreground commands for the RUN_COMMAND_SERVICE.ACTION_RUN_COMMAND intent */

View File

@@ -520,7 +520,9 @@ public class TermuxUtils {
aptInfoScript = aptInfoScript.replaceAll(Pattern.quote("@TERMUX_PREFIX@"), TermuxConstants.TERMUX_PREFIX_DIR_PATH);
ExecutionCommand executionCommand = new ExecutionCommand(1, TermuxConstants.TERMUX_BIN_PREFIX_DIR_PATH + "/bash", null, aptInfoScript, null, true, false);
ExecutionCommand executionCommand = new ExecutionCommand(1,
TermuxConstants.TERMUX_BIN_PREFIX_DIR_PATH + "/bash", null, aptInfoScript,
null, ExecutionCommand.Runner.APP_SHELL.getName(), false);
executionCommand.commandLabel = "APT Info Command";
executionCommand.backgroundCustomLogLevel = Logger.LOG_LEVEL_OFF;
AppShell appShell = AppShell.execute(context, executionCommand, null, new TermuxShellEnvironmentClient(), true);
@@ -578,7 +580,8 @@ public class TermuxUtils {
// Run script
// Logging must be disabled for output of logcat command itself in StreamGobbler
ExecutionCommand executionCommand = new ExecutionCommand(1, "/system/bin/sh", null, logcatScript + "\n", "/", true, true);
ExecutionCommand executionCommand = new ExecutionCommand(1, "/system/bin/sh",
null, logcatScript + "\n", "/", ExecutionCommand.Runner.APP_SHELL.getName(), true);
executionCommand.commandLabel = "Logcat dump command";
executionCommand.backgroundCustomLogLevel = Logger.LOG_LEVEL_OFF;
AppShell appShell = AppShell.execute(context, executionCommand, null, new TermuxShellEnvironmentClient(), true);

View File

@@ -342,7 +342,8 @@ public class TermuxFileUtils {
.append("/system/bin/grep -E '( /data )|( /data/data )|( /data/user/[0-9]+ )' /proc/self/mountinfo 2>&1 | /system/bin/grep -v '/data_mirror' 2>&1");
// Run script
ExecutionCommand executionCommand = new ExecutionCommand(1, "/system/bin/sh", null, statScript.toString() + "\n", "/", true, true);
ExecutionCommand executionCommand = new ExecutionCommand(1, "/system/bin/sh", null,
statScript.toString() + "\n", "/", ExecutionCommand.Runner.APP_SHELL.getName(), true);
executionCommand.commandLabel = TermuxConstants.TERMUX_APP_NAME + " Files Stat Command";
executionCommand.backgroundCustomLogLevel = Logger.LOG_LEVEL_OFF;
AppShell appShell = AppShell.execute(context, executionCommand, null, new TermuxShellEnvironmentClient(), true);