diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index ff328bb6..91cce47c 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -106,6 +106,11 @@
+
diff --git a/app/src/main/java/com/termux/api/App.java b/app/src/main/java/com/termux/api/App.java
new file mode 100644
index 00000000..d51b7e85
--- /dev/null
+++ b/app/src/main/java/com/termux/api/App.java
@@ -0,0 +1,217 @@
+package com.termux.api;
+
+import android.app.Application;
+import android.content.Intent;
+import android.net.LocalServerSocket;
+import android.net.LocalSocket;
+
+import com.termux.api.util.TermuxApiLogger;
+
+import java.io.BufferedWriter;
+import java.io.DataInputStream;
+import java.io.OutputStreamWriter;
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class App extends Application
+{
+ public static final String LISTEN_ADDRESS = "com.termux.api://listen";
+ private static final Pattern EXTRA_STRING = Pattern.compile("(-e|--es|--esa) +([^ ]+) +\"(.*?)(? {
+ try (LocalServerSocket listen = new LocalServerSocket(LISTEN_ADDRESS)) {
+ while (true) {
+ try (LocalSocket con = listen.accept();
+ DataInputStream in = new DataInputStream(con.getInputStream());
+ BufferedWriter out = new BufferedWriter(new OutputStreamWriter(con.getOutputStream()))) {
+ // only accept connections from Termux programs
+ if (con.getPeerCredentials().getUid() != getApplicationInfo().uid) {
+ continue;
+ }
+ try {
+ //System.out.println("connection");
+ int length = in.readUnsignedShort();
+ byte[] b = new byte[length];
+ in.readFully(b);
+ String cmdline = new String(b, StandardCharsets.UTF_8);
+
+ //System.out.println(cmdline.replaceAll("--es socket_input \".*?\"","").replaceAll("--es socket_output \".*?\"",""));
+ HashMap stringExtras = new HashMap<>();
+ HashMap stringArrayExtras = new HashMap<>();
+ HashMap booleanExtras = new HashMap<>();
+ HashMap intExtras = new HashMap<>();
+ HashMap floatExtras = new HashMap<>();
+ HashMap intArrayExtras = new HashMap<>();
+ HashMap longArrayExtras = new HashMap<>();
+ boolean err = false;
+
+ // extract and remove the string extras first, so another argument embedded in a string isn't counted as an argument
+ Matcher m = EXTRA_STRING.matcher(cmdline);
+ while (m.find()) {
+ String option = m.group(1);
+ if ("-e".equals(option) || "--es".equals(option)) {
+ // unescape "
+ stringExtras.put(m.group(2), Objects.requireNonNull(m.group(3)).replaceAll("\\\\\"","\""));
+ } else {
+ // split the list
+ String[] list = Objects.requireNonNull(m.group(3)).split("(? e : stringExtras.entrySet()) {
+ i.putExtra(e.getKey(), e.getValue());
+ }
+ for (Map.Entry e : stringArrayExtras.entrySet()) {
+ i.putExtra(e.getKey(), e.getValue());
+ }
+ for (Map.Entry e : intExtras.entrySet()) {
+ i.putExtra(e.getKey(), e.getValue());
+ }
+ for (Map.Entry e : booleanExtras.entrySet()) {
+ i.putExtra(e.getKey(), e.getValue());
+ }
+ for (Map.Entry e : floatExtras.entrySet()) {
+ i.putExtra(e.getKey(), e.getValue());
+ }
+ for (Map.Entry e : intArrayExtras.entrySet()) {
+ i.putExtra(e.getKey(), e.getValue());
+ }
+ for (Map.Entry e : longArrayExtras.entrySet()) {
+ i.putExtra(e.getKey(), e.getValue());
+ }
+ getApplicationContext().sendOrderedBroadcast(i, null);
+ // send a null byte as a sign that the arguments have been successfully received, parsed and the broadcast receiver is called
+ con.getOutputStream().write(0);
+ con.getOutputStream().flush();
+ } catch (Exception e) {
+ TermuxApiLogger.error("Error parsing arguments", e);
+ out.write("Exception in the plugin\n");
+ out.flush();
+ }
+ }
+ }
+ } catch (Exception e) {
+ TermuxApiLogger.error("Error listening for connections", e);
+ }
+ }).start();
+ }
+
+}
diff --git a/app/src/main/java/com/termux/api/KeepAliveService.java b/app/src/main/java/com/termux/api/KeepAliveService.java
new file mode 100644
index 00000000..666ca27a
--- /dev/null
+++ b/app/src/main/java/com/termux/api/KeepAliveService.java
@@ -0,0 +1,21 @@
+package com.termux.api;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+
+import androidx.annotation.Nullable;
+
+public class KeepAliveService extends Service
+{
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ return Service.START_STICKY;
+ }
+
+ @Nullable
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+}
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 3c789642..682868c7 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -11,6 +11,6 @@
&TERMUX_API_APP_NAME;
Share with
Grant permission
-
+ This service keeps Termux:API running in the background for faster startup of termux-* commands.
This app needs the following permission(s):\n