mirror of https://github.com/termux/termux-app
Added: termux-am-library to integrate am with Termux.
This commit is contained in:
parent
e597ece75f
commit
4aca16326c
|
@ -25,6 +25,10 @@ android {
|
|||
|
||||
implementation project(":terminal-view")
|
||||
implementation project(":termux-shared")
|
||||
|
||||
|
||||
implementation 'com.github.tareksander:termux-am-library:main-SNAPSHOT'
|
||||
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
|
|
|
@ -3,13 +3,16 @@ package com.termux.app;
|
|||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
|
||||
import com.termux.am.Am;
|
||||
import com.termux.shared.termux.TermuxConstants;
|
||||
import com.termux.shared.termux.crash.TermuxCrashUtils;
|
||||
import com.termux.shared.termux.settings.preferences.TermuxAppSharedPreferences;
|
||||
import com.termux.shared.logger.Logger;
|
||||
import com.termux.shared.termux.settings.properties.TermuxAppSharedProperties;
|
||||
import com.termux.shared.termux.theme.TermuxThemeUtils;
|
||||
import com.termux.shared.shell.LocalSocketListener;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class TermuxApplication extends Application {
|
||||
|
||||
|
@ -31,6 +34,20 @@ public class TermuxApplication extends Application {
|
|||
|
||||
// Set NightMode.APP_NIGHT_MODE
|
||||
TermuxThemeUtils.setAppNightMode(properties.getNightMode());
|
||||
|
||||
try {
|
||||
new LocalSocketListener(this, (args, out, err) -> {
|
||||
try {
|
||||
new Am(out, err, this).run(args);
|
||||
return 0;
|
||||
} catch (Exception e) {
|
||||
return 1;
|
||||
}
|
||||
}, TermuxConstants.TERMUX_PACKAGE_NAME+"://call-am", 1000);
|
||||
}
|
||||
catch (IOException e) {
|
||||
Logger.logDebug("TermuxApplication", "am socket already in use");
|
||||
}
|
||||
}
|
||||
|
||||
public static void setLogConfig(Context context) {
|
||||
|
|
|
@ -0,0 +1,161 @@
|
|||
package com.termux.shared.shell;
|
||||
|
||||
import android.app.Application;
|
||||
import android.net.LocalServerSocket;
|
||||
import android.net.LocalSocket;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.termux.shared.logger.Logger;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.PrintStream;
|
||||
|
||||
public class LocalSocketListener
|
||||
{
|
||||
public interface LocalSocketHandler {
|
||||
int handle(String[] args, PrintStream out, PrintStream err);
|
||||
}
|
||||
|
||||
|
||||
private static final String LOG_TAG = "LocalSocketListener";
|
||||
|
||||
private final Thread thread;
|
||||
private final LocalServerSocket server;
|
||||
private final int timeoutMillis;
|
||||
|
||||
public LocalSocketListener(@NonNull Application a, @NonNull LocalSocketHandler h, String address, int timeoutMillis) throws IOException {
|
||||
this.timeoutMillis = timeoutMillis;
|
||||
server = new LocalServerSocket(address);
|
||||
thread = new Thread(new LocalSocketListenerRunnable(a, h));
|
||||
thread.setUncaughtExceptionHandler((t, e) -> Logger.logStackTraceWithMessage(LOG_TAG, "Uncaught exception in LocalSocketListenerRunnable", e));
|
||||
thread.start();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void stop() {
|
||||
try {
|
||||
thread.interrupt();
|
||||
server.close();
|
||||
} catch (Exception ignored) {}
|
||||
}
|
||||
|
||||
private class LocalSocketListenerRunnable implements Runnable {
|
||||
private final Application a;
|
||||
private final TimeoutWatcher timeoutWatcher;
|
||||
private final Thread timeoutWatcherThread;
|
||||
private final LocalSocketHandler h;
|
||||
public LocalSocketListenerRunnable(@NonNull Application a, @NonNull LocalSocketHandler h) {
|
||||
this.a = a;
|
||||
this.h = h;
|
||||
timeoutWatcher = new TimeoutWatcher();
|
||||
timeoutWatcherThread = new Thread(timeoutWatcher);
|
||||
timeoutWatcherThread.start();
|
||||
}
|
||||
|
||||
// the socket timeout for LocalSocket doesn't seem to work, so close the socket if the timeout is over, so the processing Thread doesn't get blocked.
|
||||
private class TimeoutWatcher implements Runnable {
|
||||
private final Object lock = new Object();
|
||||
private LocalSocket current = null;
|
||||
@Override
|
||||
public void run() {
|
||||
while (! Thread.currentThread().isInterrupted()) {
|
||||
LocalSocket watch = current;
|
||||
synchronized (lock) {
|
||||
while (watch == null) {
|
||||
try {
|
||||
lock.wait();
|
||||
}
|
||||
catch (InterruptedException ignored) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
watch = current;
|
||||
}
|
||||
}
|
||||
try {
|
||||
//noinspection BusyWait
|
||||
Thread.sleep(timeoutMillis);
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
try {
|
||||
watch.shutdownInput();
|
||||
} catch (Exception ignored) {}
|
||||
try {
|
||||
watch.shutdownOutput();
|
||||
} catch (Exception ignored) {}
|
||||
try {
|
||||
watch.close();
|
||||
} catch (Exception ignored) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
while (! Thread.currentThread().isInterrupted()) {
|
||||
try (LocalSocket s = server.accept();
|
||||
OutputStream sockout = s.getOutputStream();
|
||||
InputStreamReader r = new InputStreamReader(s.getInputStream())) {
|
||||
timeoutWatcher.current = s;
|
||||
synchronized (timeoutWatcher.lock) {
|
||||
timeoutWatcher.lock.notifyAll();
|
||||
}
|
||||
// ensure only Termux programs can connect
|
||||
if (s.getPeerCredentials().getUid() != a.getApplicationInfo().uid) {
|
||||
Logger.logDebug(LOG_TAG, "A program with another UID tried to connect");
|
||||
continue;
|
||||
}
|
||||
StringBuilder b = new StringBuilder();
|
||||
int c;
|
||||
while ((c = r.read()) > 0) {
|
||||
b.append((char) c);
|
||||
}
|
||||
String outString;
|
||||
String errString;
|
||||
int ret;
|
||||
try (ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
PrintStream outp = new PrintStream(out);
|
||||
ByteArrayOutputStream err = new ByteArrayOutputStream();
|
||||
PrintStream errp = new PrintStream(err)) {
|
||||
|
||||
ret = h.handle(ArgumentTokenizer.tokenize(b.toString()).toArray(new String[0]), outp, errp);
|
||||
|
||||
outp.flush();
|
||||
outString = out.toString("UTF-8");
|
||||
|
||||
errp.flush();
|
||||
errString = err.toString("UTF-8");
|
||||
}
|
||||
try (BufferedWriter w = new BufferedWriter(new OutputStreamWriter(sockout))) {
|
||||
w.write(Integer.toString(ret));
|
||||
w.write('\0');
|
||||
w.write(outString);
|
||||
w.write('\0');
|
||||
w.write(errString);
|
||||
w.flush();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Logger.logStackTraceWithMessage(LOG_TAG, "Exception while handling connection", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
try {
|
||||
server.close();
|
||||
} catch (Exception ignored) {}
|
||||
if (timeoutWatcherThread.isAlive()) {
|
||||
timeoutWatcherThread.interrupt();
|
||||
}
|
||||
}
|
||||
Logger.logDebug(LOG_TAG, "LocalSocketListenerRunnable returned");
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue