Skip to content

Commit f62febb

Browse files
Add the TermuxTask class for linking a Process to an ExecutionCommand.
TermuxTask will maintain info for background Termux tasks. Each task started by TermuxService will now be linked to a ExecutionCommand that started it. - StreamGobbler class has also been imported from https://github.com/Chainfire/libsuperuser and partially modified to read stdout and stderr of background commands. This should likely be much safer and efficient. - Logging of every line has been disabled unless log level is set to verbose. This should have a performance increase and also prevent potentially private user data to be sent to logcat. - This also solves the bug where Termux:Tasker would hang indefinitely if Runtime.getRuntime().exec raised an exception, like for invalid or missing interpreter errors and Termux:Tasker wasn't notified of it. Now the errmsg will be used to send any exceptions back to Termux:Tasker and other 3rd party calls. - This also solves the bug where stdout or stderr were too large in size and TransactionTooLargeException exception was raised and result TERMUX_SERVICE.EXTRA_PENDING_INTENT pending intent failed to be sent to the caller. This would have also hung up Termux:Tasker. Now the stdout and stderr sent back in TERMUX_SERVICE.EXTRA_PLUGIN_RESULT_BUNDLE bundle will be truncated from the start to max 100KB combined. The original size of stdout and stderr will be provided in TERMUX_SERVICE.EXTRA_PLUGIN_RESULT_BUNDLE_STDOUT_ORIGINAL_LENGTH and TERMUX_SERVICE.EXTRA_PLUGIN_RESULT_BUNDLE_STDERR_ORIGINAL_LENGTH extras respectively so that the caller can check if either of them were truncated. The errmsg will also be truncated from end to max 25KB to preserve start of stacktraces. - The PluginUtils.processPluginExecutionCommandResult() has been updated to fully handle the result of plugin execution intents.
1 parent 7ca20fd commit f62febb

File tree

4 files changed

+619
-71
lines changed

4 files changed

+619
-71
lines changed

app/src/main/java/com/termux/app/TermuxService.java

+75-25
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import com.termux.app.utils.TextDataUtils;
3333
import com.termux.models.ExecutionCommand;
3434
import com.termux.models.ExecutionCommand.ExecutionState;
35+
import com.termux.app.terminal.TermuxTask;
3536
import com.termux.terminal.TerminalEmulator;
3637
import com.termux.terminal.TerminalSession;
3738
import com.termux.terminal.TerminalSessionClient;
@@ -71,17 +72,17 @@ class LocalBinder extends Binder {
7172
private final Handler mHandler = new Handler();
7273

7374
/**
74-
* The termux sessions which this service manages.
75+
* The foreground termux sessions which this service manages.
7576
* Note that this list is observed by {@link TermuxActivity#mTermuxSessionListViewController},
7677
* so any changes must be made on the UI thread and followed by a call to
7778
* {@link ArrayAdapter#notifyDataSetChanged()} }.
7879
*/
7980
final List<TermuxSession> mTermuxSessions = new ArrayList<>();
8081

8182
/**
82-
* The background jobs which this service manages.
83+
* The background termux tasks which this service manages.
8384
*/
84-
final List<BackgroundJob> mBackgroundTasks = new ArrayList<>();
85+
final List<TermuxTask> mTermuxTasks = new ArrayList<>();
8586

8687
/** The full implementation of the {@link TerminalSessionClient} interface to be used by {@link TerminalSession}
8788
* that holds activity references for activity related functions.
@@ -204,15 +205,24 @@ private void requestStopService() {
204205
stopSelf();
205206
}
206207

207-
208-
209208
/** Process action to stop service. */
210209
private void actionStopService() {
211210
mWantsToStop = true;
212211
finishAllTermuxSessions();
213212
requestStopService();
214213
}
215214

215+
/** Finish all termux sessions by sending SIGKILL to their shells. */
216+
private synchronized void finishAllTermuxSessions() {
217+
// TODO: Should SIGKILL also be send to background processes maintained by mTermuxTasks?
218+
for (int i = 0; i < mTermuxSessions.size(); i++)
219+
mTermuxSessions.get(i).getTerminalSession().finishIfRunning();
220+
}
221+
222+
223+
224+
225+
216226
/** Process action to acquire Power and Wi-Fi WakeLocks. */
217227
@SuppressLint({"WakelockTimeout", "BatteryLife"})
218228
private void actionAcquireWakeLock() {
@@ -306,36 +316,72 @@ private void actionServiceExecute(Intent intent) {
306316
executionCommand.pluginPendingIntent = intent.getParcelableExtra(TERMUX_SERVICE.EXTRA_PENDING_INTENT);
307317

308318
if (executionCommand.inBackground) {
309-
executeBackgroundCommand(executionCommand);
319+
executeTermuxTaskCommand(executionCommand);
310320
} else {
311321
executeTermuxSessionCommand(executionCommand);
312322
}
313323
}
314324

315-
/** Execute a shell command in background with {@link BackgroundJob}. */
316-
private void executeBackgroundCommand(ExecutionCommand executionCommand) {
325+
326+
327+
328+
329+
/** Execute a shell command in background {@link TermuxTask}. */
330+
private void executeTermuxTaskCommand(ExecutionCommand executionCommand) {
317331
if (executionCommand == null) return;
318-
319-
Logger.logDebug(LOG_TAG, "Starting background command");
332+
333+
Logger.logDebug(LOG_TAG, "Starting background termux task command");
334+
335+
TermuxTask newTermuxTask = createTermuxTask(executionCommand);
336+
}
337+
338+
/** Create a {@link TermuxTask}. */
339+
@Nullable
340+
public TermuxTask createTermuxTask(String executablePath, String[] arguments, String workingDirectory) {
341+
return createTermuxTask(new ExecutionCommand(getNextExecutionId(), executablePath, arguments, workingDirectory, true, false));
342+
}
343+
344+
/** Create a {@link TermuxTask}. */
345+
@Nullable
346+
public synchronized TermuxTask createTermuxTask(ExecutionCommand executionCommand) {
347+
if (executionCommand == null) return null;
348+
349+
Logger.logDebug(LOG_TAG, "Creating termux task");
350+
351+
if (!executionCommand.inBackground) {
352+
Logger.logDebug(LOG_TAG, "Ignoring a foreground execution command passed to createTermuxTask()");
353+
return null;
354+
}
320355

321356
if(Logger.getLogLevel() >= Logger.LOG_LEVEL_VERBOSE)
322357
Logger.logVerbose(LOG_TAG, executionCommand.toString());
323358

324-
BackgroundJob task = new BackgroundJob(executionCommand, this);
359+
TermuxTask newTermuxTask = TermuxTask.create(this, executionCommand);
360+
if (newTermuxTask == null) {
361+
// Logger.logError(LOG_TAG, "Failed to execute new termux task command for:\n" + executionCommand.toString());
362+
return null;
363+
};
364+
365+
mTermuxTasks.add(newTermuxTask);
325366

326-
mBackgroundTasks.add(task);
327367
updateNotification();
368+
369+
return newTermuxTask;
328370
}
329371

330-
/** Callback received when a {@link BackgroundJob} finishes. */
331-
public void onBackgroundJobExited(final BackgroundJob task) {
372+
/** Callback received when a {@link TermuxTask} finishes. */
373+
public synchronized void onTermuxTaskExited(final TermuxTask task) {
332374
mHandler.post(() -> {
333-
mBackgroundTasks.remove(task);
375+
mTermuxTasks.remove(task);
334376
updateNotification();
335377
});
336378
}
337379

338-
/** Execute a shell command in a foreground terminal session. */
380+
381+
382+
383+
384+
/** Execute a shell command in a foreground {@link TermuxSession}. */
339385
private void executeTermuxSessionCommand(ExecutionCommand executionCommand) {
340386
if (executionCommand == null) return;
341387

@@ -357,15 +403,15 @@ private void executeTermuxSessionCommand(ExecutionCommand executionCommand) {
357403
}
358404

359405
/**
360-
* Create a termux session.
406+
* Create a {@link TermuxSession}.
361407
* Currently called by {@link TermuxSessionClient#addNewSession(boolean, String)} to add a new termux session.
362408
*/
363409
@Nullable
364410
public TermuxSession createTermuxSession(String executablePath, String[] arguments, String workingDirectory, boolean isFailSafe, String sessionName) {
365411
return createTermuxSession(new ExecutionCommand(getNextExecutionId(), executablePath, arguments, workingDirectory, false, isFailSafe), sessionName);
366412
}
367413

368-
/** Create a termux session. */
414+
/** Create a {@link TermuxSession}. */
369415
@Nullable
370416
public synchronized TermuxSession createTermuxSession(ExecutionCommand executionCommand, String sessionName) {
371417
if (executionCommand == null) return null;
@@ -428,11 +474,7 @@ public synchronized int removeTermuxSession(TerminalSession sessionToRemove) {
428474
return index;
429475
}
430476

431-
/** Finish all termux sessions by sending SIGKILL to their shells. */
432-
private synchronized void finishAllTermuxSessions() {
433-
for (int i = 0; i < mTermuxSessions.size(); i++)
434-
mTermuxSessions.get(i).getTerminalSession().finishIfRunning();
435-
}
477+
436478

437479

438480

@@ -473,6 +515,10 @@ private void startTermuxActivity() {
473515
startActivity(new Intent(this, TermuxActivity.class).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
474516
}
475517

518+
519+
520+
521+
476522
/** If {@link TermuxActivity} has not bound to the {@link TermuxService} yet or is destroyed, then
477523
* interface functions requiring the activity should not be available to the terminal sessions,
478524
* so we just return the {@link #mTermuxSessionClientBase}. Once {@link TermuxActivity} bind
@@ -519,6 +565,8 @@ public synchronized void unsetTermuxSessionClient() {
519565

520566

521567

568+
569+
522570
private Notification buildNotification() {
523571
Intent notifyIntent = new Intent(this, TermuxActivity.class);
524572
// PendingIntent#getActivity(): "Note that the activity will be started outside of the context of an existing
@@ -527,7 +575,7 @@ private Notification buildNotification() {
527575
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notifyIntent, 0);
528576

529577
int sessionCount = getTermuxSessionsSize();
530-
int taskCount = mBackgroundTasks.size();
578+
int taskCount = mTermuxTasks.size();
531579
String contentText = sessionCount + " session" + (sessionCount == 1 ? "" : "s");
532580
if (taskCount > 0) {
533581
contentText += ", " + taskCount + " task" + (taskCount == 1 ? "" : "s");
@@ -587,7 +635,7 @@ private void setupNotificationChannel() {
587635

588636
/** Update the shown foreground service notification after making any changes that affect it. */
589637
void updateNotification() {
590-
if (mWakeLock == null && mTermuxSessions.isEmpty() && mBackgroundTasks.isEmpty()) {
638+
if (mWakeLock == null && mTermuxSessions.isEmpty() && mTermuxTasks.isEmpty()) {
591639
// Exit if we are updating after the user disabled all locks with no sessions or tasks running.
592640
requestStopService();
593641
} else {
@@ -597,6 +645,8 @@ void updateNotification() {
597645

598646

599647

648+
649+
600650
private void setCurrentStoredTerminalSession(TerminalSession session) {
601651
if(session == null) return;
602652
// Make the newly created session the current one to be displayed:

0 commit comments

Comments
 (0)