Provide a service for executing commands by third-party applications

Re-implementation of https://github.com/termux/termux-app/pull/1029.

If Termux has property "allow-external-apps" set to "true", a third-party
program will be able to send intents for executing custom commands
within Termux environment.

Third-party program must declare permission "com.termux.permission.RUN_COMMAND".
This commit is contained in:
Leonid Pliushch
2020-06-09 15:32:22 +03:00
parent 5f71e3e73a
commit db3ff7b24a
3 changed files with 105 additions and 0 deletions

View File

@@ -8,6 +8,12 @@
<uses-feature android:name="android.hardware.touchscreen" android:required="false" />
<uses-feature android:name="android.software.leanback" android:required="false" />
<permission android:name="com.termux.permission.RUN_COMMAND"
android:label="@string/run_command_permission_label"
android:description="@string/run_command_permission_description"
android:icon="@drawable/ic_launcher"
android:protectionLevel="dangerous" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
@@ -114,6 +120,15 @@
android:name="com.termux.app.TermuxService"
android:exported="false" />
<service
android:name=".app.RunCommand"
android:exported="true"
android:permission="com.termux.permission.RUN_COMMAND" >
<intent-filter>
<action android:name="com.termux.RUN_COMMAND" />
</intent-filter>
</service>
<receiver android:name=".app.TermuxOpenReceiver" />
<provider android:authorities="com.termux.files"

View File

@@ -0,0 +1,88 @@
package com.termux.app;
import android.app.Service;
import android.content.Intent;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
import android.util.Log;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.Properties;
/**
* When allow-external-apps property is set to "true", Termux is able to process execute intents
* sent by third-party applications.
*
* Third-party program must declare com.termux.permission.RUN_COMMAND permission.
*
* Intent expects following configuration:
*
* Action: com.termux.RUN_COMMAND
* Program/script path (string): com.termux.RUN_COMMAND_PATH
* Program/script arguments (string): com.termux.RUN_COMMAND_ARGUMENTS
* Session working directory (string): com.termux.RUN_COMMAND_WORKDIR
*
* Sample intent for launching program "top" in equivalent of ADB command:
* am startservice -a com.termux.RUN_COMMAND --es com.termux.RUN_COMMAND_PATH /data/data/com.termux/files/usr/bin/top
*/
public class RunCommand extends Service {
public static final String RUN_COMMAND_ACTION = "com.termux.RUN_COMMAND";
public static final String RUN_COMMAND_PATH = "com.termux.RUN_COMMAND_PATH";
public static final String RUN_COMMAND_ARGUMENTS = "com.termux.RUN_COMMAND_ARGUMENTS";
public static final String RUN_COMMAND_WORKDIR = "com.termux.RUN_COMMAND_WORKDIR";
class LocalBinder extends Binder {
public final RunCommand service = RunCommand.this;
}
private final IBinder mBinder = new RunCommand.LocalBinder();
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
public int onStartCommand(Intent intent, int flags, int startId) {
if (allowExternalApps() && RUN_COMMAND_ACTION.equals(intent.getAction())) {
Uri programUri = new Uri.Builder().scheme("com.termux.file").path(intent.getStringExtra(RUN_COMMAND_PATH)).build();
Intent execIntent = new Intent(TermuxService.ACTION_EXECUTE, programUri);
execIntent.setClass(this, TermuxService.class);
execIntent.putExtra(TermuxService.EXTRA_ARGUMENTS, intent.getStringExtra(RUN_COMMAND_ARGUMENTS));
execIntent.putExtra(TermuxService.EXTRA_CURRENT_WORKING_DIRECTORY, intent.getStringExtra(RUN_COMMAND_WORKDIR));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
this.startForegroundService(execIntent);
} else {
this.startService(execIntent);
}
}
return Service.START_NOT_STICKY;
}
private boolean allowExternalApps() {
File propsFile = new File(TermuxService.HOME_PATH + "/.termux/termux.properties");
if (!propsFile.exists())
propsFile = new File(TermuxService.HOME_PATH + "/.config/termux/termux.properties");
Properties props = new Properties();
try {
if (propsFile.isFile() && propsFile.canRead()) {
try (FileInputStream in = new FileInputStream(propsFile)) {
props.load(new InputStreamReader(in, StandardCharsets.UTF_8));
}
}
} catch (Exception e) {
Log.e("termux", "Error loading props", e);
}
return props.getProperty("allow-external-apps", "false").equals("true");
}
}

View File

@@ -2,6 +2,8 @@
<resources>
<string name="application_name">Termux</string>
<string name="shared_user_label">Termux user</string>
<string name="run_command_permission_label">Run commands in Termux environment</string>
<string name="run_command_permission_description">Allow third-party applications to execute arbitrary commands within Termux environment.</string>
<string name="new_session">New session</string>
<string name="new_session_failsafe">Failsafe</string>
<string name="toggle_soft_keyboard">Keyboard</string>