18
18
import com .termux .app .settings .properties .TermuxPropertyConstants ;
19
19
import com .termux .app .utils .Logger ;
20
20
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
-
27
21
/**
28
22
* Third-party apps that are not part of termux world can run commands in termux context by either
29
23
* sending an intent to RunCommandService or becoming a plugin host for the termux-tasker plugin
30
24
* client.
31
25
*
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
+ *
33
28
* 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.
36
31
* 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 "/".
39
57
*
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.
45
58
*
46
59
* To automatically bring termux session to foreground and start termux commands that were started
47
60
* with background mode "false" in android >= 10 without user having to click the notification
51
64
* Check https://github.com/termux/termux-tasker for more details on allow-external-apps and draw
52
65
* over apps and other limitations.
53
66
*
67
+ *
54
68
* To reduce the chance of termux being killed by android even further due to violation of not
55
69
* being able to call startForeground() within ~5s of service start in android >= 8, the user
56
70
* may disable battery optimizations for termux.
57
71
*
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
+ *
58
81
* Sample code to run command "top" with java:
59
82
* Intent intent = new Intent();
60
83
* intent.setClassName("com.termux", "com.termux.app.RunCommandService");
63
86
* intent.putExtra("com.termux.RUN_COMMAND_ARGUMENTS", new String[]{"-n", "5"});
64
87
* intent.putExtra("com.termux.RUN_COMMAND_WORKDIR", "/data/data/com.termux/files/home");
65
88
* intent.putExtra("com.termux.RUN_COMMAND_BACKGROUND", false);
89
+ * intent.putExtra("com.termux.RUN_COMMAND_SESSION_ACTION", "0");
66
90
* startService(intent);
67
91
*
68
92
* Sample code to run command "top" with "am startservice" command:
71
95
* --es com.termux.RUN_COMMAND_PATH '/data/data/com.termux/files/usr/bin/top' \
72
96
* --esa com.termux.RUN_COMMAND_ARGUMENTS '-n,5' \
73
97
* --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'
81
100
*/
82
101
public class RunCommandService extends Service {
83
102
@@ -99,17 +118,20 @@ public IBinder onBind(Intent intent) {
99
118
100
119
@ Override
101
120
public void onCreate () {
121
+ Logger .logVerbose (LOG_TAG , "onCreate" );
102
122
runStartForeground ();
103
123
}
104
124
105
125
@ Override
106
126
public int onStartCommand (Intent intent , int flags , int startId ) {
127
+ Logger .logDebug (LOG_TAG , "onStartCommand" );
128
+
107
129
// Run again in case service is already started and onCreate() is not called
108
130
runStartForeground ();
109
131
110
- // If wrong action passed, then just return
132
+ // If invalid action passed, then just return
111
133
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 () + " \" " );
113
135
return Service .START_NOT_STICKY ;
114
136
}
115
137
@@ -119,18 +141,32 @@ public int onStartCommand(Intent intent, int flags, int startId) {
119
141
return Service .START_NOT_STICKY ;
120
142
}
121
143
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
+
123
156
124
157
Intent execIntent = new Intent (TERMUX_SERVICE .ACTION_SERVICE_EXECUTE , programUri );
125
158
execIntent .setClass (this , TermuxService .class );
126
159
execIntent .putExtra (TERMUX_SERVICE .EXTRA_ARGUMENTS , intent .getStringArrayExtra (RUN_COMMAND_SERVICE .EXTRA_ARGUMENTS ));
127
160
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 ));
128
162
129
163
String workingDirectory = intent .getStringExtra (RUN_COMMAND_SERVICE .EXTRA_WORKDIR );
130
164
if (workingDirectory != null && !workingDirectory .isEmpty ()) {
131
165
execIntent .putExtra (TERMUX_SERVICE .EXTRA_WORKDIR , getExpandedTermuxPath (workingDirectory ));
132
166
}
133
167
168
+
169
+
134
170
if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .O ) {
135
171
this .startForegroundService (execIntent );
136
172
} else {
0 commit comments