diff --git a/pom.xml b/pom.xml
index 140630f..796a6ce 100644
--- a/pom.xml
+++ b/pom.xml
@@ -60,6 +60,14 @@
Java-WebSocket
1.5.6
+
+
+
+ org.jetbrains.pty4j
+ pty4j
+ 0.12.35
+
+
diff --git a/src/main/java/net/geedge/api/config/AdbShellProxyHandler.java b/src/main/java/net/geedge/api/config/AdbShellProxyHandler.java
new file mode 100644
index 0000000..489b971
--- /dev/null
+++ b/src/main/java/net/geedge/api/config/AdbShellProxyHandler.java
@@ -0,0 +1,131 @@
+package net.geedge.api.config;
+
+import cn.hutool.log.Log;
+import com.pty4j.PtyProcessBuilder;
+import net.geedge.api.entity.EnvApiYml;
+import net.geedge.api.util.AdbCommandBuilder;
+import net.geedge.api.util.AdbUtil;
+import net.geedge.common.T;
+import org.springframework.web.socket.CloseStatus;
+import org.springframework.web.socket.TextMessage;
+import org.springframework.web.socket.WebSocketSession;
+import org.springframework.web.socket.handler.TextWebSocketHandler;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+public class AdbShellProxyHandler extends TextWebSocketHandler {
+
+ private static final Log log = Log.get();
+
+ private EnvApiYml.Adb adb;
+
+ private Process process = null;
+ private InputStream inputStream = null;
+ private OutputStream outputStream = null;
+ private ExecutorService executorService = Executors.newFixedThreadPool(2);
+
+ public AdbShellProxyHandler(EnvApiYml.Adb adb) {
+ this.adb = adb;
+ }
+
+ @Override
+ public synchronized void afterConnectionEstablished(WebSocketSession session) throws Exception {
+ log.info("[afterConnectionEstablished] [WebSocket connection established] [websocket uri: {}]", session.getUri());
+ super.afterConnectionEstablished(session);
+
+ List cmd = AdbCommandBuilder.builder()
+ .serial(AdbUtil.getInstance(adb).getSerial())
+ .buildShellCommand("shell")
+ .build();
+
+ Map env = new HashMap<>(System.getenv());
+ env.put("TERM", "xterm");
+
+ // start process
+ process = new PtyProcessBuilder()
+ .setCommand(cmd.toArray(new String[]{}))
+ .setEnvironment(env)
+ .setRedirectErrorStream(true)
+ .start();
+
+// process = new PtyProcessBuilder()
+// .setCommand(new String[]{"cmd.exe", "/C", "D:\\softwares\\platform-tools\\platform-tools\\adb.exe shell"})
+// .setEnvironment(env)
+// .setRedirectErrorStream(true)
+// .start();
+
+ // stream
+ inputStream = process.getInputStream();
+ outputStream = process.getOutputStream();
+
+ // server to client
+ executorService.submit(() -> {
+ byte[] buffer = new byte[1024 * 4];
+ try {
+ while (session != null && session.isOpen()) {
+ int size = inputStream.read(buffer);
+ if (size == -1) {
+ try {
+ if (session.isOpen()) {
+ session.sendMessage(new TextMessage("Connection closed \r\n"));
+ }
+ } catch (Exception e) {
+ }
+ return;
+ }
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < size; i++) {
+ char chr = (char) (buffer[i] & 0xff);
+ sb.append(chr);
+ }
+
+ String message = sb.toString();
+ message = T.StrUtil.str(message.getBytes(T.DigestUtils.getEncoding(message)), "UTF-8");
+
+ session.sendMessage(new TextMessage(message));
+ }
+ } catch (Exception e) {
+ log.error(e, "[serverToClient] [error]");
+ }
+ });
+ }
+
+ @Override
+ protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
+ super.handleTextMessage(session, message);
+ if (T.ObjectUtil.isNotNull(outputStream)) {
+ // write cmd byte
+ T.IoUtil.write(outputStream, false, message.getPayload().getBytes());
+ T.IoUtil.flush(outputStream);
+ }
+ }
+
+ @Override
+ public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
+ log.info("[afterConnectionClosed] [WebSocket connection closed] [websocket uri: {}]", session.getUri());
+ super.afterConnectionClosed(session, status);
+
+ // close resources
+ this.closeResources();
+ }
+
+ /**
+ * close resources
+ */
+ private void closeResources() {
+ try {
+ T.IoUtil.close(outputStream);
+ T.IoUtil.close(inputStream);
+ if (process != null)
+ process.destroy();
+ } catch (Exception e) {
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/net/geedge/api/config/WebSocketConfig.java b/src/main/java/net/geedge/api/config/WebSocketConfig.java
index e92f971..b51a1e4 100644
--- a/src/main/java/net/geedge/api/config/WebSocketConfig.java
+++ b/src/main/java/net/geedge/api/config/WebSocketConfig.java
@@ -17,5 +17,6 @@ public class WebSocketConfig implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(new VncProxyHandler(envApiYml.getVnc()), "/api/v1/env/novnc").setAllowedOrigins("*");
+ registry.addHandler(new AdbShellProxyHandler(envApiYml.getAdb()), "/api/v1/env/terminal").setAllowedOrigins("*");
}
}
diff --git a/src/main/java/net/geedge/common/T.java b/src/main/java/net/geedge/common/T.java
index c098b77..335d3d5 100644
--- a/src/main/java/net/geedge/common/T.java
+++ b/src/main/java/net/geedge/common/T.java
@@ -10,6 +10,8 @@ import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
import java.util.StringTokenizer;
public class T {
@@ -127,6 +129,14 @@ public class T {
public static class URLUtil extends cn.hutool.core.util.URLUtil {
}
+ /**
+ * 对象工具类,包括判空、克隆、序列化等操作
+ *
+ * @author Looly
+ */
+ public static class ObjectUtil extends cn.hutool.core.util.ObjectUtil {
+ }
+
/**
* CommandLineUtil
*
@@ -244,4 +254,24 @@ public class T {
return file.getAbsolutePath();
}
}
+
+ public class DigestUtils {
+ private static final List ENCODE_LIST = Arrays.asList("ISO-8859-1", "GB2312", "UTF-8", "GBK");
+
+ public static String getEncoding(String text) {
+ for (String enc : ENCODE_LIST) {
+ try {
+ byte[] bytes = text.getBytes(enc);
+ String str = cn.hutool.core.util.StrUtil.str(bytes, enc);
+ Arrays.fill(bytes, (byte) 0);
+ if (cn.hutool.core.util.StrUtil.equals(text, str)) {
+ return enc;
+ }
+ } catch (UnsupportedEncodingException e) {
+ }
+ }
+ return null;
+ }
+ }
+
}
\ No newline at end of file