1818import com .termux .app .settings .properties .TermuxPropertyConstants ;
1919import com .termux .app .utils .Logger ;
2020
21- import java .io .File ;
22- import java .io .FileInputStream ;
23- import java .io .InputStreamReader ;
24- import java .nio .charset .StandardCharsets ;
25- import java .util .Properties ;
26-
2721/**
2822 * Third-party apps that are not part of termux world can run commands in termux context by either
2923 * sending an intent to RunCommandService or becoming a plugin host for the termux-tasker plugin
3024 * client.
3125 *
32- * For the RunCommandService intent to work, there are 2 main requirements:
26+ * For the {@link RUN_COMMAND_SERVICE#ACTION_RUN_COMMAND} intent to work, there are 2 main requirements:
27+ *
3328 * 1. The `allow-external-apps` property must be set to "true" in ~/.termux/termux.properties in
34- * termux app, regardless of if the executable path is inside or outside the `~/.termux/tasker/`
35- * directory.
29+ * termux app, regardless of if the executable path is inside or outside the `~/.termux/tasker/`
30+ * directory.
3631 * 2. The intent sender/third-party app must request the `com.termux.permission.RUN_COMMAND`
37- * permission in its `AndroidManifest.xml` and it should be granted by user to the app through the
38- * app's App Info permissions page in android settings, likely under Additional Permissions.
32+ * permission in its `AndroidManifest.xml` and it should be granted by user to the app through the
33+ * app's App Info permissions page in android settings, likely under Additional Permissions.
34+ *
35+ *
36+ *
37+ * The {@link RUN_COMMAND_SERVICE#ACTION_RUN_COMMAND} intent expects the following extras:
38+ *
39+ * 1. The {@code String} {@link RUN_COMMAND_SERVICE#EXTRA_COMMAND_PATH} extra for absolute path of
40+ * command. This is mandatory.
41+ * 2. The {@code String[]} {@link RUN_COMMAND_SERVICE#EXTRA_ARGUMENTS} extra for any arguments to
42+ * pass to command. This is optional.
43+ * 3. The {@code String} {@link RUN_COMMAND_SERVICE#EXTRA_WORKDIR} extra for current working directory
44+ * of command. This is optional and defaults to {@link TermuxConstants#TERMUX_HOME_DIR_PATH}.
45+ * 4. The {@code boolean} {@link RUN_COMMAND_SERVICE#EXTRA_BACKGROUND} extra whether to run command
46+ * in background or foreground terminal session. This is optional and defaults to {@code false}.
47+ * 5. The {@code String} {@link RUN_COMMAND_SERVICE#EXTRA_SESSION_ACTION} extra for for session action
48+ * of foreground commands. This is optional and defaults to
49+ * {@link TERMUX_SERVICE#VALUE_EXTRA_SESSION_ACTION_SWITCH_TO_NEW_SESSION_AND_OPEN_ACTIVITY}.
50+ *
51+ *
52+ *
53+ * The {@link RUN_COMMAND_SERVICE#EXTRA_COMMAND_PATH} and {@link RUN_COMMAND_SERVICE#EXTRA_WORKDIR}
54+ * can optionally be prefixed with "$PREFIX/" or "~/" if an absolute path is not to be given.
55+ * The "$PREFIX/" will expand to {@link TermuxConstants#TERMUX_PREFIX_DIR_PATH} and
56+ * "~/" will expand to {@link TermuxConstants#TERMUX_HOME_DIR_PATH}, followed by a forward slash "/".
3957 *
40- * The absolute path of executable or script must be given in "RUN_COMMAND_PATH" extra.
41- * The "RUN_COMMAND_ARGUMENTS", "RUN_COMMAND_WORKDIR" and "RUN_COMMAND_BACKGROUND" extras are
42- * optional. The workdir defaults to termux home. The background mode defaults to "false".
43- * The command path and workdir can optionally be prefixed with "$PREFIX/" or "~/" if an absolute
44- * path is not to be given.
4558 *
4659 * To automatically bring termux session to foreground and start termux commands that were started
4760 * with background mode "false" in android >= 10 without user having to click the notification
5164 * Check https://github.com/termux/termux-tasker for more details on allow-external-apps and draw
5265 * over apps and other limitations.
5366 *
67+ *
5468 * To reduce the chance of termux being killed by android even further due to violation of not
5569 * being able to call startForeground() within ~5s of service start in android >= 8, the user
5670 * may disable battery optimizations for termux.
5771 *
72+ *
73+ * If your third-party app is targeting sdk 30 (android 11), then it needs to add `com.termux`
74+ * package to the `queries` element or request `QUERY_ALL_PACKAGES` permission in its
75+ * `AndroidManifest.xml`. Otherwise it will get `PackageSetting{...... com.termux/......} BLOCKED`
76+ * errors in logcat and `RUN_COMMAND` won't work.
77+ * https://developer.android.com/training/basics/intents/package-visibility#package-name
78+ *
79+ *
80+ *
5881 * Sample code to run command "top" with java:
5982 * Intent intent = new Intent();
6083 * intent.setClassName("com.termux", "com.termux.app.RunCommandService");
6386 * intent.putExtra("com.termux.RUN_COMMAND_ARGUMENTS", new String[]{"-n", "5"});
6487 * intent.putExtra("com.termux.RUN_COMMAND_WORKDIR", "/data/data/com.termux/files/home");
6588 * intent.putExtra("com.termux.RUN_COMMAND_BACKGROUND", false);
89+ * intent.putExtra("com.termux.RUN_COMMAND_SESSION_ACTION", "0");
6690 * startService(intent);
6791 *
6892 * Sample code to run command "top" with "am startservice" command:
7195 * --es com.termux.RUN_COMMAND_PATH '/data/data/com.termux/files/usr/bin/top' \
7296 * --esa com.termux.RUN_COMMAND_ARGUMENTS '-n,5' \
7397 * --es com.termux.RUN_COMMAND_WORKDIR '/data/data/com.termux/files/home' \
74- * --ez com.termux.RUN_COMMAND_BACKGROUND 'false'
75- *
76- * If your third-party app is targeting sdk 30 (android 11), then it needs to add `com.termux`
77- * package to the `queries` element or request `QUERY_ALL_PACKAGES` permission in its
78- * `AndroidManifest.xml`. Otherwise it will get `PackageSetting{...... com.termux/......} BLOCKED`
79- * errors in logcat and `RUN_COMMAND` won't work.
80- * https://developer.android.com/training/basics/intents/package-visibility#package-name
98+ * --ez com.termux.RUN_COMMAND_BACKGROUND 'false' \
99+ * --es com.termux.RUN_COMMAND_SESSION_ACTION '0'
81100 */
82101public class RunCommandService extends Service {
83102
@@ -99,17 +118,20 @@ public IBinder onBind(Intent intent) {
99118
100119 @ Override
101120 public void onCreate () {
121+ Logger .logVerbose (LOG_TAG , "onCreate" );
102122 runStartForeground ();
103123 }
104124
105125 @ Override
106126 public int onStartCommand (Intent intent , int flags , int startId ) {
127+ Logger .logDebug (LOG_TAG , "onStartCommand" );
128+
107129 // Run again in case service is already started and onCreate() is not called
108130 runStartForeground ();
109131
110- // If wrong action passed, then just return
132+ // If invalid action passed, then just return
111133 if (!RUN_COMMAND_SERVICE .ACTION_RUN_COMMAND .equals (intent .getAction ())) {
112- Logger .logError (LOG_TAG , "Unexpected intent action to RunCommandService: " + intent .getAction ());
134+ Logger .logError (LOG_TAG , "Invalid intent action to RunCommandService: \" " + intent .getAction () + " \" " );
113135 return Service .START_NOT_STICKY ;
114136 }
115137
@@ -119,18 +141,32 @@ public int onStartCommand(Intent intent, int flags, int startId) {
119141 return Service .START_NOT_STICKY ;
120142 }
121143
122- Uri programUri = new Uri .Builder ().scheme (TERMUX_SERVICE .URI_SCHEME_SERVICE_EXECUTE ).path (getExpandedTermuxPath (intent .getStringExtra (RUN_COMMAND_SERVICE .EXTRA_COMMAND_PATH ))).build ();
144+
145+
146+ String commandPath = intent .getStringExtra (RUN_COMMAND_SERVICE .EXTRA_COMMAND_PATH );
147+ // If invalid commandPath passed, then just return
148+ if (commandPath == null || commandPath .isEmpty ()) {
149+ Logger .logError (LOG_TAG , "Invalid coommand path to RunCommandService: \" " + commandPath + "\" " );
150+ return Service .START_NOT_STICKY ;
151+ }
152+
153+ Uri programUri = new Uri .Builder ().scheme (TERMUX_SERVICE .URI_SCHEME_SERVICE_EXECUTE ).path (getExpandedTermuxPath (commandPath )).build ();
154+
155+
123156
124157 Intent execIntent = new Intent (TERMUX_SERVICE .ACTION_SERVICE_EXECUTE , programUri );
125158 execIntent .setClass (this , TermuxService .class );
126159 execIntent .putExtra (TERMUX_SERVICE .EXTRA_ARGUMENTS , intent .getStringArrayExtra (RUN_COMMAND_SERVICE .EXTRA_ARGUMENTS ));
127160 execIntent .putExtra (TERMUX_SERVICE .EXTRA_BACKGROUND , intent .getBooleanExtra (RUN_COMMAND_SERVICE .EXTRA_BACKGROUND , false ));
161+ execIntent .putExtra (TERMUX_SERVICE .EXTRA_SESSION_ACTION , intent .getStringExtra (RUN_COMMAND_SERVICE .EXTRA_SESSION_ACTION ));
128162
129163 String workingDirectory = intent .getStringExtra (RUN_COMMAND_SERVICE .EXTRA_WORKDIR );
130164 if (workingDirectory != null && !workingDirectory .isEmpty ()) {
131165 execIntent .putExtra (TERMUX_SERVICE .EXTRA_WORKDIR , getExpandedTermuxPath (workingDirectory ));
132166 }
133167
168+
169+
134170 if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .O ) {
135171 this .startForegroundService (execIntent );
136172 } else {
0 commit comments