Skip to content

Commit

Permalink
Partially integrate ExectionCommand into TermuxService and BackgroundJob
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
agnostic-apollo committed Mar 23, 2021
1 parent 31371b5 commit 1b5e5b5
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 64 deletions.
50 changes: 31 additions & 19 deletions app/src/main/java/com/termux/app/BackgroundJob.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

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;
Expand All @@ -26,28 +28,33 @@
*/
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;
}

Expand Down Expand Up @@ -79,7 +86,7 @@ public void run() {
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));

Expand Down Expand Up @@ -109,16 +116,21 @@ public void run() {
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
}
Expand All @@ -133,10 +145,10 @@ private static void addToEnvIfPresent(List<String> environment, String name) {
}
}

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<String> environment = new ArrayList<>();

Expand All @@ -159,13 +171,13 @@ static String[] buildEnvironment(boolean failSafe, String cwd) {
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);
}

Expand All @@ -186,7 +198,7 @@ public static int getPid(Process p) {
}
}

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
Expand Down Expand Up @@ -236,7 +248,7 @@ static String[] setupProcessArgs(String fileToExecute, String[] args) {
List<String> 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]);
}

Expand Down
4 changes: 2 additions & 2 deletions app/src/main/java/com/termux/app/TermuxActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -263,8 +263,8 @@ public void onServiceConnected(ComponentName componentName, IBinder service) {
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());
}
Expand Down
93 changes: 52 additions & 41 deletions app/src/main/java/com/termux/app/TermuxService.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
import android.os.IBinder;
import android.os.PowerManager;
import android.provider.Settings;
import android.util.Log;
import android.widget.ArrayAdapter;

import com.termux.R;
Expand All @@ -28,16 +27,15 @@
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;

/**
Expand All @@ -55,7 +53,9 @@
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 {
Expand Down Expand Up @@ -275,36 +275,45 @@ private void actionReleaseWakeLock(boolean updateNotification) {

/** 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<String, Object>() {{
put("pendingIntentCreator", pendingIntentCreator);
}});

BackgroundJob task = new BackgroundJob(cwd, executablePath, arguments, this, pendingIntent);
BackgroundJob task = new BackgroundJob(executionCommand, this);

mBackgroundTasks.add(task);
updateNotification();
Expand All @@ -319,22 +328,20 @@ public void onBackgroundJobExited(final BackgroundJob task) {
}

/** 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<String, Object>() {{
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;
}
Expand All @@ -344,7 +351,7 @@ private void executeForegroundCommand(Intent intent, String executablePath, Stri
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) {
Expand Down Expand Up @@ -390,16 +397,16 @@ private void startTermuxActivity() {
}

/** 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()) {
Expand All @@ -426,7 +433,7 @@ public TerminalSession createTerminalSession(String executablePath, String[] arg
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();

Expand Down Expand Up @@ -569,8 +576,8 @@ private void setupNotificationChannel() {

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. */
Expand All @@ -593,4 +600,8 @@ public List<TerminalSession> getSessions() {
return mTerminalSessions;
}

synchronized public static int getNextExecutionId() {
return EXECUTION_ID++;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ public void renameSession(final TerminalSession sessionToRename) {
}, -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();
Expand All @@ -196,7 +196,7 @@ public void addNewSession(boolean failSafe, String sessionName) {
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;
}
Expand Down

0 comments on commit 1b5e5b5

Please sign in to comment.