From 1b5e5b56cbf19809df09b4e90170d2962b4ca0cc Mon Sep 17 00:00:00 2001 From: agnostic-apollo Date: Wed, 24 Mar 2021 03:59:18 +0500 Subject: [PATCH] Partially integrate ExectionCommand into TermuxService and BackgroundJob The TERMUX_SERVICE.ACTION_SERVICE_EXECUTE intent received will be managed by the ExectionCommand now. The cwd and failsafe have been renamed to workingDirectory and isFailsafe. --- .../java/com/termux/app/BackgroundJob.java | 50 ++++++---- .../java/com/termux/app/TermuxActivity.java | 4 +- .../java/com/termux/app/TermuxService.java | 93 +++++++++++-------- .../app/terminal/TermuxSessionClient.java | 4 +- 4 files changed, 87 insertions(+), 64 deletions(-) diff --git a/app/src/main/java/com/termux/app/BackgroundJob.java b/app/src/main/java/com/termux/app/BackgroundJob.java index 61e32317..171fedff 100644 --- a/app/src/main/java/com/termux/app/BackgroundJob.java +++ b/app/src/main/java/com/termux/app/BackgroundJob.java @@ -7,6 +7,8 @@ import android.os.Bundle; import com.termux.BuildConfig; import com.termux.app.utils.Logger; +import com.termux.models.ExecutionCommand; +import com.termux.models.ExecutionCommand.ExecutionState; import java.io.BufferedReader; import java.io.File; @@ -26,28 +28,33 @@ import java.util.List; */ public final class BackgroundJob { - final Process mProcess; + Process mProcess; private static final String LOG_TAG = "BackgroundJob"; - public BackgroundJob(String cwd, String fileToExecute, final String[] args, final TermuxService service){ - this(cwd, fileToExecute, args, service, null); + public BackgroundJob(String executable, final String[] arguments, String workingDirectory, final TermuxService service){ + this(new ExecutionCommand(TermuxService.getNextExecutionId(), executable, arguments, workingDirectory, false, false), service); } - public BackgroundJob(String cwd, String fileToExecute, final String[] args, final TermuxService service, PendingIntent pendingIntent) { - String[] env = buildEnvironment(false, cwd); - if (cwd == null || cwd.isEmpty()) cwd = TermuxConstants.TERMUX_HOME_DIR_PATH; + public BackgroundJob(ExecutionCommand executionCommand, final TermuxService service) { + String[] env = buildEnvironment(false, executionCommand.workingDirectory); - final String[] progArray = setupProcessArgs(fileToExecute, args); - final String processDescription = Arrays.toString(progArray); + if (executionCommand.workingDirectory == null || executionCommand.workingDirectory.isEmpty()) + executionCommand.workingDirectory = TermuxConstants.TERMUX_HOME_DIR_PATH; + + final String[] commandArray = setupProcessArgs(executionCommand.executable, executionCommand.arguments); + final String commandDescription = Arrays.toString(commandArray); + + if(!executionCommand.setState(ExecutionState.EXECUTING)) + return; Process process; try { - process = Runtime.getRuntime().exec(progArray, env, new File(cwd)); + process = Runtime.getRuntime().exec(commandArray, env, new File(executionCommand.workingDirectory)); } catch (IOException e) { mProcess = null; // TODO: Visible error message? - Logger.logStackTraceWithMessage(LOG_TAG, "Failed running background job: " + processDescription, e); + Logger.logStackTraceWithMessage(LOG_TAG, "Failed running background job: " + commandDescription, e); return; } @@ -79,7 +86,7 @@ public final class BackgroundJob { new Thread() { @Override public void run() { - Logger.logDebug(LOG_TAG, "[" + pid + "] starting: " + processDescription); + Logger.logDebug(LOG_TAG, "[" + pid + "] starting: " + commandDescription); InputStream stdout = mProcess.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(stdout, StandardCharsets.UTF_8)); @@ -109,16 +116,21 @@ public final class BackgroundJob { errThread.join(); result.putString("stderr", errResult.toString()); + if(!executionCommand.setState(ExecutionState.EXECUTED)) + return; + Intent data = new Intent(); data.putExtra("result", result); - if(pendingIntent != null) { + if(executionCommand.pluginPendingIntent != null) { try { - pendingIntent.send(service.getApplicationContext(), Activity.RESULT_OK, data); + executionCommand.pluginPendingIntent.send(service.getApplicationContext(), Activity.RESULT_OK, data); } catch (PendingIntent.CanceledException e) { // The caller doesn't want the result? That's fine, just ignore } } + + executionCommand.setState(ExecutionState.SUCCESS); } catch (InterruptedException e) { // Ignore } @@ -133,10 +145,10 @@ public final class BackgroundJob { } } - static String[] buildEnvironment(boolean failSafe, String cwd) { + static String[] buildEnvironment(boolean isFailSafe, String workingDirectory) { TermuxConstants.TERMUX_HOME_DIR.mkdirs(); - if (cwd == null || cwd.isEmpty()) cwd = TermuxConstants.TERMUX_HOME_DIR_PATH; + if (workingDirectory == null || workingDirectory.isEmpty()) workingDirectory = TermuxConstants.TERMUX_HOME_DIR_PATH; List environment = new ArrayList<>(); @@ -159,13 +171,13 @@ public final class BackgroundJob { addToEnvIfPresent(environment, "ANDROID_RUNTIME_ROOT"); addToEnvIfPresent(environment, "ANDROID_TZDATA_ROOT"); - if (failSafe) { + if (isFailSafe) { // Keep the default path so that system binaries can be used in the failsafe session. environment.add("PATH= " + System.getenv("PATH")); } else { environment.add("LANG=en_US.UTF-8"); environment.add("PATH=" + TermuxConstants.TERMUX_BIN_PREFIX_DIR_PATH); - environment.add("PWD=" + cwd); + environment.add("PWD=" + workingDirectory); environment.add("TMPDIR=" + TermuxConstants.TERMUX_TMP_PREFIX_DIR_PATH); } @@ -186,7 +198,7 @@ public final class BackgroundJob { } } - static String[] setupProcessArgs(String fileToExecute, String[] args) { + static String[] setupProcessArgs(String fileToExecute, String[] arguments) { // The file to execute may either be: // - An elf file, in which we execute it directly. // - A script file without shebang, which we execute with our standard shell $PREFIX/bin/sh instead of the @@ -236,7 +248,7 @@ public final class BackgroundJob { List result = new ArrayList<>(); if (interpreter != null) result.add(interpreter); result.add(fileToExecute); - if (args != null) Collections.addAll(result, args); + if (arguments != null) Collections.addAll(result, arguments); return result.toArray(new String[0]); } diff --git a/app/src/main/java/com/termux/app/TermuxActivity.java b/app/src/main/java/com/termux/app/TermuxActivity.java index da66bddd..b86db958 100644 --- a/app/src/main/java/com/termux/app/TermuxActivity.java +++ b/app/src/main/java/com/termux/app/TermuxActivity.java @@ -263,8 +263,8 @@ public final class TermuxActivity extends Activity implements ServiceConnection Intent i = getIntent(); if (i != null && Intent.ACTION_RUN.equals(i.getAction())) { // Android 7.1 app shortcut from res/xml/shortcuts.xml. - boolean failSafe = i.getBooleanExtra(TERMUX_ACTIVITY.ACTION_FAILSAFE_SESSION, false); - mTermuxSessionClient.addNewSession(failSafe, null); + boolean isFailSafe = i.getBooleanExtra(TERMUX_ACTIVITY.ACTION_FAILSAFE_SESSION, false); + mTermuxSessionClient.addNewSession(isFailSafe, null); } else { mTermuxSessionClient.setCurrentSession(mTermuxSessionClient.getCurrentStoredSessionOrLast()); } diff --git a/app/src/main/java/com/termux/app/TermuxService.java b/app/src/main/java/com/termux/app/TermuxService.java index 8c8f8f3a..ba1e56dc 100644 --- a/app/src/main/java/com/termux/app/TermuxService.java +++ b/app/src/main/java/com/termux/app/TermuxService.java @@ -18,7 +18,6 @@ import android.os.Handler; import android.os.IBinder; import android.os.PowerManager; import android.provider.Settings; -import android.util.Log; import android.widget.ArrayAdapter; import com.termux.R; @@ -28,16 +27,15 @@ import com.termux.app.settings.preferences.TermuxAppSharedPreferences; import com.termux.app.terminal.TermuxSessionClient; import com.termux.app.terminal.TermuxSessionClientBase; import com.termux.app.utils.Logger; -import com.termux.app.utils.PluginUtils; import com.termux.app.utils.TextDataUtils; +import com.termux.models.ExecutionCommand; +import com.termux.models.ExecutionCommand.ExecutionState; import com.termux.terminal.TerminalEmulator; import com.termux.terminal.TerminalSession; import com.termux.terminal.TerminalSessionClient; import java.io.File; import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; import java.util.List; /** @@ -55,7 +53,9 @@ import java.util.List; public final class TermuxService extends Service { private static final String NOTIFICATION_CHANNEL_ID = "termux_notification_channel"; - private static final int NOTIFICATION_ID = 1337; + public static final int NOTIFICATION_ID = 1337; + + private static int EXECUTION_ID = 1000; /** This service is only bound from inside the same process and never uses IPC. */ class LocalBinder extends Binder { @@ -275,36 +275,45 @@ public final class TermuxService extends Service { /** Process action to execute a shell command in a foreground session or in background. */ private void actionServiceExecute(Intent intent) { - Uri executableUri = intent.getData(); - String executablePath = (executableUri == null ? null : executableUri.getPath()); + if (intent == null){ + Logger.logError(LOG_TAG, "Ignoring null intent to actionServiceExecute"); + return; + } - String[] arguments = (executableUri == null ? null : intent.getStringArrayExtra(TERMUX_SERVICE.EXTRA_ARGUMENTS)); - String cwd = intent.getStringExtra(TERMUX_SERVICE.EXTRA_WORKDIR); + ExecutionCommand executionCommand = new ExecutionCommand(getNextExecutionId()); - PendingIntent pendingIntent = intent.getParcelableExtra(TERMUX_SERVICE.EXTRA_PENDING_INTENT); + executionCommand.executableUri = intent.getData(); - int sessionAction = TextDataUtils.getIntStoredAsStringFromBundle(intent.getExtras(), - TERMUX_SERVICE.EXTRA_SESSION_ACTION, TERMUX_SERVICE.VALUE_EXTRA_SESSION_ACTION_SWITCH_TO_NEW_SESSION_AND_OPEN_ACTIVITY); + if(executionCommand.executableUri != null) { + executionCommand.executable = executionCommand.executableUri.getPath(); + executionCommand.arguments = intent.getStringArrayExtra(TERMUX_SERVICE.EXTRA_ARGUMENTS); + } - if (intent.getBooleanExtra(TERMUX_SERVICE.EXTRA_BACKGROUND, false)) { - executeBackgroundCommand(executablePath, arguments, cwd, pendingIntent); + executionCommand.workingDirectory = intent.getStringExtra(TERMUX_SERVICE.EXTRA_WORKDIR); + executionCommand.inBackground = intent.getBooleanExtra(TERMUX_SERVICE.EXTRA_BACKGROUND, false); + executionCommand.isFailsafe = intent.getBooleanExtra(TERMUX_ACTIVITY.ACTION_FAILSAFE_SESSION, false); + executionCommand.sessionAction = intent.getStringExtra(TERMUX_SERVICE.EXTRA_SESSION_ACTION); + executionCommand.commandLabel = TextDataUtils.getDefaultIfNull(intent.getStringExtra(TERMUX_SERVICE.EXTRA_COMMAND_LABEL), "Execution Intent Command"); + executionCommand.commandDescription = intent.getStringExtra(TERMUX_SERVICE.EXTRA_COMMAND_DESCRIPTION); + executionCommand.commandHelp = intent.getStringExtra(TERMUX_SERVICE.EXTRA_COMMAND_HELP); + executionCommand.pluginAPIHelp = intent.getStringExtra(TERMUX_SERVICE.EXTRA_PLUGIN_API_HELP); + executionCommand.isPluginExecutionCommand = true; + executionCommand.pluginPendingIntent = intent.getParcelableExtra(TERMUX_SERVICE.EXTRA_PENDING_INTENT); + + if (executionCommand.inBackground) { + executeBackgroundCommand(executionCommand); } else { - executeForegroundCommand(intent, executablePath, arguments, cwd, sessionAction); + executeForegroundCommand(executionCommand); } } /** Execute a shell command in background with {@link BackgroundJob}. */ - private void executeBackgroundCommand(String executablePath, String[] arguments, String cwd, PendingIntent pendingIntent) { + private void executeBackgroundCommand(ExecutionCommand executionCommand) { Logger.logDebug(LOG_TAG, "Starting background command"); - final String pendingIntentCreator; - if(pendingIntent != null) pendingIntentCreator = pendingIntent.getCreatorPackage(); else pendingIntentCreator = null; + Logger.logDebug(LOG_TAG, executionCommand.toString()); - PluginUtils.dumpExecutionIntentToLog(Log.DEBUG, LOG_TAG, null, executablePath, Arrays.asList(arguments), cwd, true, new HashMap() {{ - put("pendingIntentCreator", pendingIntentCreator); - }}); - - BackgroundJob task = new BackgroundJob(cwd, executablePath, arguments, this, pendingIntent); + BackgroundJob task = new BackgroundJob(executionCommand, this); mBackgroundTasks.add(task); updateNotification(); @@ -319,22 +328,20 @@ public final class TermuxService extends Service { } /** Execute a shell command in a foreground terminal session. */ - private void executeForegroundCommand(Intent intent, String executablePath, String[] arguments, String cwd, int sessionAction) { + private void executeForegroundCommand(ExecutionCommand executionCommand) { Logger.logDebug(LOG_TAG, "Starting foreground command"); - boolean failsafe = intent.getBooleanExtra(TERMUX_ACTIVITY.ACTION_FAILSAFE_SESSION, false); + if(!executionCommand.setState(ExecutionState.EXECUTING)) + return; - PluginUtils.dumpExecutionIntentToLog(Log.DEBUG, LOG_TAG, null, executablePath, Arrays.asList(arguments), cwd, false, new HashMap() {{ - put("sessionAction", sessionAction); - put("failsafe", failsafe); - }}); + Logger.logDebug(LOG_TAG, executionCommand.toString()); - TerminalSession newSession = createTerminalSession(executablePath, arguments, cwd, failsafe); + TerminalSession newSession = createTerminalSession(executionCommand.executable, executionCommand.arguments, executionCommand.workingDirectory, executionCommand.isFailsafe); // Transform executable path to session name, e.g. "/bin/do-something.sh" => "do something.sh". - if (executablePath != null) { - int lastSlash = executablePath.lastIndexOf('/'); - String name = (lastSlash == -1) ? executablePath : executablePath.substring(lastSlash + 1); + if (executionCommand.executable != null) { + int lastSlash = executionCommand.executable.lastIndexOf('/'); + String name = (lastSlash == -1) ? executionCommand.executable : executionCommand.executable.substring(lastSlash + 1); name = name.replace('-', ' '); newSession.mSessionName = name; } @@ -344,7 +351,7 @@ public final class TermuxService extends Service { if(mTermuxSessionClient != null) mTermuxSessionClient.terminalSessionListNotifyUpdated(); - handleSessionAction(sessionAction, newSession); + handleSessionAction(TextDataUtils.getIntFromString(executionCommand.sessionAction, TERMUX_SERVICE.VALUE_EXTRA_SESSION_ACTION_SWITCH_TO_NEW_SESSION_AND_OPEN_ACTIVITY), newSession); } private void setCurrentStoredSession(TerminalSession newSession) { @@ -390,16 +397,16 @@ public final class TermuxService extends Service { } /** Create a terminal session. */ - public TerminalSession createTerminalSession(String executablePath, String[] arguments, String cwd, boolean failSafe) { + public TerminalSession createTerminalSession(String executablePath, String[] arguments, String workingDirectory, boolean isFailSafe) { TermuxConstants.TERMUX_HOME_DIR.mkdirs(); - if (cwd == null || cwd.isEmpty()) cwd = TermuxConstants.TERMUX_HOME_DIR_PATH; + if (workingDirectory == null || workingDirectory.isEmpty()) workingDirectory = TermuxConstants.TERMUX_HOME_DIR_PATH; - String[] env = BackgroundJob.buildEnvironment(failSafe, cwd); + String[] env = BackgroundJob.buildEnvironment(isFailSafe, workingDirectory); boolean isLoginShell = false; if (executablePath == null) { - if (!failSafe) { + if (!isFailSafe) { for (String shellBinary : new String[]{"login", "bash", "zsh"}) { File shellFile = new File(TermuxConstants.TERMUX_BIN_PREFIX_DIR_PATH, shellBinary); if (shellFile.canExecute()) { @@ -426,7 +433,7 @@ public final class TermuxService extends Service { args[0] = processName; if (processArgs.length > 1) System.arraycopy(processArgs, 1, args, 1, processArgs.length - 1); - TerminalSession session = new TerminalSession(executablePath, cwd, args, env, getTermuxSessionClient()); + TerminalSession session = new TerminalSession(executablePath, workingDirectory, args, env, getTermuxSessionClient()); mTerminalSessions.add(session); updateNotification(); @@ -569,8 +576,8 @@ public final class TermuxService extends Service { NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, channelName,importance); channel.setDescription(channelDescription); - NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); - manager.createNotificationChannel(channel); + NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + notificationManager.createNotificationChannel(channel); } /** Update the shown foreground service notification after making any changes that affect it. */ @@ -593,4 +600,8 @@ public final class TermuxService extends Service { return mTerminalSessions; } + synchronized public static int getNextExecutionId() { + return EXECUTION_ID++; + } + } diff --git a/app/src/main/java/com/termux/app/terminal/TermuxSessionClient.java b/app/src/main/java/com/termux/app/terminal/TermuxSessionClient.java index 467b778b..4229cc91 100644 --- a/app/src/main/java/com/termux/app/terminal/TermuxSessionClient.java +++ b/app/src/main/java/com/termux/app/terminal/TermuxSessionClient.java @@ -182,7 +182,7 @@ public class TermuxSessionClient extends TermuxSessionClientBase { }, -1, null, -1, null, null); } - public void addNewSession(boolean failSafe, String sessionName) { + public void addNewSession(boolean isFailSafe, String sessionName) { if (mActivity.getTermuxService().getSessions().size() >= MAX_SESSIONS) { new AlertDialog.Builder(mActivity).setTitle(R.string.max_terminals_reached_title).setMessage(R.string.max_terminals_reached_message) .setPositiveButton(android.R.string.ok, null).show(); @@ -196,7 +196,7 @@ public class TermuxSessionClient extends TermuxSessionClientBase { workingDirectory = currentSession.getCwd(); } - TerminalSession newSession = mActivity.getTermuxService().createTerminalSession(null, null, workingDirectory, failSafe); + TerminalSession newSession = mActivity.getTermuxService().createTerminalSession(null, null, workingDirectory, isFailSafe); if (sessionName != null) { newSession.mSessionName = sessionName; }