From c52c7dcba99b217220ea1c9781a1f14b36b9abf0 Mon Sep 17 00:00:00 2001 From: ZhouYixun <291028775@qq.com> Date: Thu, 4 Nov 2021 21:21:08 +0800 Subject: [PATCH 01/10] save_scrcpy --- src/main/java/com/sonic/agent/tools/ScrcpyTool.java | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 src/main/java/com/sonic/agent/tools/ScrcpyTool.java diff --git a/src/main/java/com/sonic/agent/tools/ScrcpyTool.java b/src/main/java/com/sonic/agent/tools/ScrcpyTool.java new file mode 100644 index 00000000..971e5fcd --- /dev/null +++ b/src/main/java/com/sonic/agent/tools/ScrcpyTool.java @@ -0,0 +1,7 @@ +package com.sonic.agent.tools; + +public class ScrcpyTool { + public void startScrcpyServer() { + + } +} From e30c28d30cad794e29da2d42798040db41e3dda0 Mon Sep 17 00:00:00 2001 From: ZhouYixun <291028775@qq.com> Date: Fri, 5 Nov 2021 10:56:47 +0800 Subject: [PATCH 02/10] save_update_capture --- .../android/AndroidDeviceBridgeTool.java | 31 +++++++++---- .../sonic/agent/exception/SonicException.java | 12 ----- .../com/sonic/agent/tools/MiniCapTool.java | 34 +++++--------- src/main/resources/application-dev.yml | 8 ++-- src/main/resources/application-prod.yml | 44 +++++++++++++++++++ src/main/resources/application.yml | 2 +- 6 files changed, 81 insertions(+), 50 deletions(-) delete mode 100644 src/main/java/com/sonic/agent/exception/SonicException.java create mode 100644 src/main/resources/application-prod.yml diff --git a/src/main/java/com/sonic/agent/bridge/android/AndroidDeviceBridgeTool.java b/src/main/java/com/sonic/agent/bridge/android/AndroidDeviceBridgeTool.java index b0834b50..6716072c 100644 --- a/src/main/java/com/sonic/agent/bridge/android/AndroidDeviceBridgeTool.java +++ b/src/main/java/com/sonic/agent/bridge/android/AndroidDeviceBridgeTool.java @@ -1,17 +1,20 @@ package com.sonic.agent.bridge.android; -import com.android.ddmlib.*; -import com.sonic.agent.exception.SonicException; +import com.alibaba.fastjson.JSONObject; +import com.android.ddmlib.AndroidDebugBridge; +import com.android.ddmlib.CollectingOutputReceiver; +import com.android.ddmlib.IDevice; +import com.android.ddmlib.IShellOutputReceiver; import com.sonic.agent.tools.DownImageTool; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.DependsOn; import org.springframework.stereotype.Component; +import javax.websocket.Session; import java.io.File; import java.io.IOException; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; /** * @author ZhouYiXun @@ -373,7 +376,7 @@ public static void pushToCamera(IDevice iDevice, String url) { * @des 开启miniCap服务 * @date 2021/8/16 20:04 */ - public static void startMiniCapServer(IDevice iDevice, int quality, int screen) throws SonicException { + public static void startMiniCapServer(IDevice iDevice, int quality, int screen, Session session) { //先删除原有路径下的文件,防止上次出错后停止,再次打开会报错的情况 executeCommand(iDevice, "rm -rf /data/local/tmp/minicap*"); //获取cpu信息 @@ -399,7 +402,6 @@ public static void startMiniCapServer(IDevice iDevice, int quality, int screen) //给文件权限 executeCommand(iDevice, "chmod 777 /data/local/tmp/" + miniCapFileName); String size = getScreenSize(iDevice); - AtomicBoolean isSupport = new AtomicBoolean(true); try { //开始启动 iDevice.executeShellCommand(String.format("LD_LIBRARY_PATH=/data/local/tmp /data/local/tmp/%s -Q " + quality + " -P %s@%s/%d", miniCapFileName, size, size, screen), new IShellOutputReceiver() { @@ -408,9 +410,13 @@ public void addOutput(byte[] bytes, int i, int i1) { String res = new String(bytes, i, i1); logger.info(res); if (res.contains("Vector<> have different types")) { - isSupport.set(false); logger.info(iDevice.getSerialNumber() + "设备不兼容投屏!"); - return; + if (session != null) { + JSONObject support = new JSONObject(); + support.put("msg", "support"); + support.put("text", "该设备不兼容MiniCap投屏!"); + sendText(session, support.toJSONString()); + } } } @@ -428,8 +434,15 @@ public boolean isCancelled() { , iDevice.getSerialNumber()); logger.error(e.getMessage()); } - if (!isSupport.get()) { - throw new SonicException("该设备不兼容投屏!"); + } + + private static void sendText(Session session, String message) { + synchronized (session) { + try { + session.getBasicRemote().sendText(message); + } catch (IllegalStateException | IOException e) { + logger.error("socket发送失败!连接已关闭!"); + } } } diff --git a/src/main/java/com/sonic/agent/exception/SonicException.java b/src/main/java/com/sonic/agent/exception/SonicException.java deleted file mode 100644 index f4497fa2..00000000 --- a/src/main/java/com/sonic/agent/exception/SonicException.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.sonic.agent.exception; - -/** - * @author ZhouYiXun - * @des - * @date 2021/10/10 11:57 - */ -public class SonicException extends Exception { - public SonicException(String message) { - super(message); - } -} diff --git a/src/main/java/com/sonic/agent/tools/MiniCapTool.java b/src/main/java/com/sonic/agent/tools/MiniCapTool.java index e7a8447f..1f6aab50 100644 --- a/src/main/java/com/sonic/agent/tools/MiniCapTool.java +++ b/src/main/java/com/sonic/agent/tools/MiniCapTool.java @@ -4,7 +4,6 @@ import com.android.ddmlib.IDevice; import com.sonic.agent.bridge.android.AndroidDeviceBridgeTool; import com.sonic.agent.bridge.android.AndroidDeviceThreadPool; -import com.sonic.agent.exception.SonicException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -67,16 +66,7 @@ public Future start(String udId, AtomicReference banner, AtomicRefe int finalQua = qua; int finalC = c; Future miniCapPro = AndroidDeviceThreadPool.cachedThreadPool.submit(() -> - { - try { - AndroidDeviceBridgeTool.startMiniCapServer(iDevice, finalQua, finalC); - } catch (SonicException e) { - JSONObject support = new JSONObject(); - support.put("msg", "support"); - support.put("text", e.getMessage()); - sendText(session, support.toJSONString()); - } - }); + AndroidDeviceBridgeTool.startMiniCapServer(iDevice, finalQua, finalC, session)); try { Thread.sleep(4000); } catch (InterruptedException e) { @@ -91,21 +81,15 @@ public Future start(String udId, AtomicReference banner, AtomicRefe try { capSocket = new Socket("localhost", finalMiniCapPort); inputStream = capSocket.getInputStream(); - int len = 4096; while (!finalMiniCapPro.isDone()) { - byte[] buffer = new byte[len]; - int realLen = 0; - try { - realLen = inputStream.read(buffer); - } catch (IOException e) { - e.printStackTrace(); - } - if (buffer.length != realLen && realLen >= 0) { - buffer = subByteArray(buffer, 0, realLen); - } - if (realLen >= 0) { - dataQueue.offer(buffer); + byte[] buffer; + int len = 0; + while (len == 0) { + len = inputStream.available(); } + buffer = new byte[len]; + inputStream.read(buffer); + dataQueue.add(buffer); } } catch (IOException e) { e.printStackTrace(); @@ -208,6 +192,8 @@ public Future start(String udId, AtomicReference banner, AtomicRefe size.put("msg", "size"); size.put("width", banner.get()[9]); size.put("height", banner.get()[13]); + size.put("vWidth", banner.get()[17]); + size.put("vHeight", banner.get()[21]); sendText(session, size.toJSONString()); } } diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index ccade71d..84033be8 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -7,8 +7,8 @@ spring: rabbitmq: host: localhost port: 5672 - username: guest - password: guest + username: sonic + password: sonic virtual-host: sonic listener: simple: @@ -26,9 +26,9 @@ logging: sonic: chrome: - path: C:\\Program Files (x86)\\Google\\Chrome\\Application\\chromedriver.exe + path: C:\Program Files\Google\Chrome\Application\chromedriver.exe folder: url: http://localhost:8094/api/folder agent: host: 127.0.0.1 - key: 1 + key: 29002272-4659-4808-a804-08ce3388b136 diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml new file mode 100644 index 00000000..0f14f387 --- /dev/null +++ b/src/main/resources/application-prod.yml @@ -0,0 +1,44 @@ +server: + #Agent服务的端口,可以自行更改 + port: 7777 + +spring: + application: + name: apex-agent + rabbitmq: + #rabbitMQ的host地址(如果用体验版的mq,这个host改为部署体验版机器的ip) + host: localhost + #如果用体验版的mq,这里是5673 + port: 5673 + #MQ的用户名 + username: guest + #MQ的密码 + password: guest + #MQ的vHost + virtual-host: / + listener: + simple: + acknowledge-mode: manual + publisher-confirm-type: correlated + publisher-returns: true + +logging: + file: + name: logs/${spring.application.name}.log + logback: + rollingpolicy: + clean-history-on-start: true + max-history: 3 + +sonic: + chrome: + # Agent机器上的chrome浏览器的driver路径,可以去http://npm.taobao.org/mirrors/chromedriver/下载 + path: C:\Program Files\Google\Chrome\Application\chromedriver.exe + folder: + # 如果跨网段,这个host和port改成后端的host和port + url: http://localhost:8094/api/folder + agent: + # 替换为部署Agent机器的ipv4 + host: 127.0.0.1 + # 替换为前端新增Agent生成的key + key: 29002272-4659-4808-a804-08ce3388b136 diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 56c09806..9473ce6c 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,4 +1,4 @@ spring: version: @project.version@ profiles: - active: prod,@profileActive@ \ No newline at end of file + active: dev,@profileActive@ \ No newline at end of file From 1fd43d17fb0af149983793626cd6783c38a14a4a Mon Sep 17 00:00:00 2001 From: ZhouYixun <291028775@qq.com> Date: Fri, 5 Nov 2021 17:03:42 +0800 Subject: [PATCH 03/10] save_minicao --- .../com/sonic/agent/tools/MiniCapTool.java | 2 +- .../com/sonic/agent/tools/ScrcpyTool.java | 22 +++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/sonic/agent/tools/MiniCapTool.java b/src/main/java/com/sonic/agent/tools/MiniCapTool.java index 1f6aab50..342f719c 100644 --- a/src/main/java/com/sonic/agent/tools/MiniCapTool.java +++ b/src/main/java/com/sonic/agent/tools/MiniCapTool.java @@ -120,7 +120,7 @@ public Future start(String udId, AtomicReference banner, AtomicRefe AndroidDeviceThreadPool.cachedThreadPool.execute(() -> { int readBannerBytes = 0; - int bannerLength = 24; + int bannerLength = 2; int readFrameBytes = 0; int frameBodyLength = 0; byte[] frameBody = new byte[0]; diff --git a/src/main/java/com/sonic/agent/tools/ScrcpyTool.java b/src/main/java/com/sonic/agent/tools/ScrcpyTool.java index 971e5fcd..533ec859 100644 --- a/src/main/java/com/sonic/agent/tools/ScrcpyTool.java +++ b/src/main/java/com/sonic/agent/tools/ScrcpyTool.java @@ -1,6 +1,28 @@ package com.sonic.agent.tools; +import java.io.IOException; +import java.io.InputStream; +import java.net.Socket; + public class ScrcpyTool { + public static void main(String[] args) throws IOException { + Socket capSocket = null; + InputStream inputStream = null; + capSocket = new Socket("localhost", 8666); + inputStream = capSocket.getInputStream(); + while (capSocket.isConnected()) { + byte[] buffer; + int len = 0; + while (len == 0) { + len = inputStream.available(); + } + buffer = new byte[len]; + inputStream.read(buffer); + System.out.println(buffer); + } + System.out.println(1); + } + public void startScrcpyServer() { } From 2d210597ad65f248df8d1e28b0a8e581a3b13cd7 Mon Sep 17 00:00:00 2001 From: ZhouYixun <291028775@qq.com> Date: Sat, 6 Nov 2021 20:05:37 +0800 Subject: [PATCH 04/10] save_scrcpy --- .../java/com/sonic/agent/tools/ScrcpyTool.java | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/sonic/agent/tools/ScrcpyTool.java b/src/main/java/com/sonic/agent/tools/ScrcpyTool.java index 533ec859..b0ec2c7a 100644 --- a/src/main/java/com/sonic/agent/tools/ScrcpyTool.java +++ b/src/main/java/com/sonic/agent/tools/ScrcpyTool.java @@ -2,23 +2,17 @@ import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; import java.net.Socket; public class ScrcpyTool { public static void main(String[] args) throws IOException { Socket capSocket = null; - InputStream inputStream = null; + OutputStream outputStream = null; capSocket = new Socket("localhost", 8666); - inputStream = capSocket.getInputStream(); + outputStream = capSocket.getOutputStream(); while (capSocket.isConnected()) { - byte[] buffer; - int len = 0; - while (len == 0) { - len = inputStream.available(); - } - buffer = new byte[len]; - inputStream.read(buffer); - System.out.println(buffer); + outputStream.write(0); } System.out.println(1); } From 3776dc44fa9e24b6115f728e395b4825b18f2fc3 Mon Sep 17 00:00:00 2001 From: ZhouYixun <291028775@qq.com> Date: Sat, 6 Nov 2021 23:49:07 +0800 Subject: [PATCH 05/10] update_new_pro --- .../android/AndroidDeviceBridgeTool.java | 106 +++++++----------- .../com/sonic/agent/tools/MiniCapTool.java | 25 ++++- .../com/sonic/agent/tools/ScrcpyTool.java | 4 +- .../agent/websockets/AndroidWSServer.java | 19 +++- 4 files changed, 80 insertions(+), 74 deletions(-) diff --git a/src/main/java/com/sonic/agent/bridge/android/AndroidDeviceBridgeTool.java b/src/main/java/com/sonic/agent/bridge/android/AndroidDeviceBridgeTool.java index 6716072c..9cc152c1 100644 --- a/src/main/java/com/sonic/agent/bridge/android/AndroidDeviceBridgeTool.java +++ b/src/main/java/com/sonic/agent/bridge/android/AndroidDeviceBridgeTool.java @@ -1,10 +1,7 @@ package com.sonic.agent.bridge.android; import com.alibaba.fastjson.JSONObject; -import com.android.ddmlib.AndroidDebugBridge; -import com.android.ddmlib.CollectingOutputReceiver; -import com.android.ddmlib.IDevice; -import com.android.ddmlib.IShellOutputReceiver; +import com.android.ddmlib.*; import com.sonic.agent.tools.DownImageTool; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -219,19 +216,19 @@ public static void removeForward(IDevice iDevice, int port, String serviceName) * @des 推送文件 * @date 2021/8/16 19:59 */ - public static void pushLocalFile(IDevice iDevice, String localPath, String remotePath) { - AndroidDeviceThreadPool.cachedThreadPool.execute(() -> { - //使用iDevice的pushFile方法好像有bug,暂时用命令行去推送 - ProcessBuilder pb = new ProcessBuilder(new String[]{getADBPathFromSystemEnv(), "-s", iDevice.getSerialNumber(), "push", localPath, remotePath}); - pb.redirectErrorStream(true); - try { - pb.start(); - } catch (IOException e) { - logger.error(e.getMessage()); - return; - } - }); - } +// public static void pushLocalFile(IDevice iDevice, String localPath, String remotePath) { +// AndroidDeviceThreadPool.cachedThreadPool.execute(() -> { +// //使用iDevice的pushFile方法好像有bug,暂时用命令行去推送 +// ProcessBuilder pb = new ProcessBuilder(new String[]{getADBPathFromSystemEnv(), "-s", iDevice.getSerialNumber(), "push", localPath, remotePath}); +// pb.redirectErrorStream(true); +// try { +// pb.start(); +// } catch (IOException e) { +// logger.error(e.getMessage()); +// return; +// } +// }); +// } /** * @param iDevice @@ -332,39 +329,35 @@ public static String matchMiniTouchFile(String sdk) { } public static void pushYadb(IDevice iDevice) { - String yadbFile = executeCommand(iDevice, " ls /data/local/tmp | grep yadb"); - if (yadbFile != null && yadbFile.contains("yadb")) { - return; - } else { - File yadbLocalFile = new File("plugins" + File.separator + "yadb"); - pushLocalFile(iDevice, yadbLocalFile.getPath(), "/data/local/tmp/yadb"); - boolean yadbFileExist = false; - //轮训目录,直到推送文件结束 - while (!yadbFileExist) { - String yadbDeviceFile = executeCommand(iDevice, " ls /data/local/tmp | grep yadb"); - if (yadbDeviceFile != null && yadbDeviceFile.contains("yadb")) { - yadbFileExist = true; - } - } - executeCommand(iDevice, "chmod 777 /data/local/tmp/yadb"); + executeCommand(iDevice, "rm -rf /data/local/tmp/yadb"); + File yadbLocalFile = new File("plugins" + File.separator + "yadb"); + try { + iDevice.pushFile(yadbLocalFile.getAbsolutePath(), "/data/local/tmp/yadb"); + } catch (IOException e) { + e.printStackTrace(); + } catch (AdbCommandRejectedException e) { + e.printStackTrace(); + } catch (TimeoutException e) { + e.printStackTrace(); + } catch (SyncException e) { + e.printStackTrace(); } + executeCommand(iDevice, "chmod 777 /data/local/tmp/yadb"); } public static void pushToCamera(IDevice iDevice, String url) { try { File image = DownImageTool.download(url); - pushLocalFile(iDevice, image.getPath(), "/sdcard/DCIM/Camera/" + image.getName()); - boolean fileExist = false; - //轮训目录,直到推送文件结束 - while (!fileExist) { - String files = executeCommand(iDevice, " ls /sdcard/DCIM/Camera | grep " + image.getName()); - if (files != null && files.contains(image.getName())) { - fileExist = true; - } - } + iDevice.pushFile(image.getAbsolutePath(), "/sdcard/DCIM/Camera/" + image.getName()); executeCommand(iDevice, "am broadcast -a android.intent.action.MEDIA_SCANNER_SCAN_FILE -d file:///sdcard/DCIM/Camera/" + image.getName()); } catch (IOException e) { e.printStackTrace(); + } catch (AdbCommandRejectedException e) { + e.printStackTrace(); + } catch (SyncException e) { + e.printStackTrace(); + } catch (TimeoutException e) { + e.printStackTrace(); } } @@ -376,7 +369,7 @@ public static void pushToCamera(IDevice iDevice, String url) { * @des 开启miniCap服务 * @date 2021/8/16 20:04 */ - public static void startMiniCapServer(IDevice iDevice, int quality, int screen, Session session) { + public static void startMiniCapServer(IDevice iDevice, int quality, int screen, Session session) throws AdbCommandRejectedException, IOException, SyncException, TimeoutException { //先删除原有路径下的文件,防止上次出错后停止,再次打开会报错的情况 executeCommand(iDevice, "rm -rf /data/local/tmp/minicap*"); //获取cpu信息 @@ -388,23 +381,16 @@ public static void startMiniCapServer(IDevice iDevice, int quality, int screen, File miniCapFile = new File("mini" + File.separator + cpuAbi + File.separator + miniCapFileName); File miniCapSoFile = new File("mini/minicap-shared/aosp/libs/android-" + androidSdkVersion + File.separator + cpuAbi + File.separator + "minicap.so"); - pushLocalFile(iDevice, miniCapFile.getPath(), "/data/local/tmp/" + miniCapFileName); - pushLocalFile(iDevice, miniCapSoFile.getPath(), "/data/local/tmp/minicap.so"); - boolean miniCapFileExist = false; - //轮训目录,直到推送文件结束 - while (!miniCapFileExist) { - String miniCapBin = executeCommand(iDevice, " ls /data/local/tmp | grep " + miniCapFileName); - String miniCapSo = executeCommand(iDevice, " ls /data/local/tmp | grep minicap.so"); - if (miniCapBin != null && miniCapBin.contains(miniCapFileName) && miniCapSo != null && miniCapSo.contains("minicap.so")) { - miniCapFileExist = true; - } - } + iDevice.pushFile(miniCapFile.getAbsolutePath(), "/data/local/tmp/" + miniCapFileName); + iDevice.pushFile(miniCapSoFile.getAbsolutePath(), "/data/local/tmp/minicap.so"); //给文件权限 executeCommand(iDevice, "chmod 777 /data/local/tmp/" + miniCapFileName); String size = getScreenSize(iDevice); + String vSize = Integer.parseInt(size.substring(0, size.indexOf("x"))) / 2 + "x" + Integer.parseInt(size.substring(size.indexOf("x") + 1)) / 2; try { //开始启动 - iDevice.executeShellCommand(String.format("LD_LIBRARY_PATH=/data/local/tmp /data/local/tmp/%s -Q " + quality + " -P %s@%s/%d", miniCapFileName, size, size, screen), new IShellOutputReceiver() { + iDevice.executeShellCommand(String.format("LD_LIBRARY_PATH=/data/local/tmp /data/local/tmp/%s -Q " + quality + " -P %s@%s/%d", + miniCapFileName, size, vSize, screen), new IShellOutputReceiver() { @Override public void addOutput(byte[] bytes, int i, int i1) { String res = new String(bytes, i, i1); @@ -453,7 +439,7 @@ private static void sendText(Session session, String message) { * @des 开启miniTouch服务 * @date 2021/8/16 20:26 */ - public static void miniTouchStart(IDevice iDevice) { + public static void miniTouchStart(IDevice iDevice) throws AdbCommandRejectedException, IOException, SyncException, TimeoutException { //先删除原有路径下的文件,防止上次出错后停止,再次打开会报错的情况 executeCommand(iDevice, "rm -rf /data/local/tmp/minitouch*"); //获取cpu信息 @@ -462,15 +448,7 @@ public static void miniTouchStart(IDevice iDevice) { String androidSdkVersion = getProperties(iDevice, "ro.build.version.sdk"); String miniTouchFileName = matchMiniTouchFile(androidSdkVersion); File miniTouchFile = new File("mini" + File.separator + cpuAbi + File.separator + miniTouchFileName); - pushLocalFile(iDevice, miniTouchFile.getAbsolutePath(), "/data/local/tmp/" + miniTouchFileName); - boolean miniTouchFileExist = false; - //轮训目录,直到推送文件结束 - while (!miniTouchFileExist) { - String miniTouchBin = executeCommand(iDevice, " ls /data/local/tmp | grep " + miniTouchFileName); - if (miniTouchBin != null && miniTouchBin.contains(miniTouchFileName)) { - miniTouchFileExist = true; - } - } + iDevice.pushFile(miniTouchFile.getAbsolutePath(), "/data/local/tmp/" + miniTouchFileName); //给文件权限 executeCommand(iDevice, "chmod 777 /data/local/tmp/" + miniTouchFileName); try { diff --git a/src/main/java/com/sonic/agent/tools/MiniCapTool.java b/src/main/java/com/sonic/agent/tools/MiniCapTool.java index 342f719c..a2de6ae7 100644 --- a/src/main/java/com/sonic/agent/tools/MiniCapTool.java +++ b/src/main/java/com/sonic/agent/tools/MiniCapTool.java @@ -1,7 +1,10 @@ package com.sonic.agent.tools; import com.alibaba.fastjson.JSONObject; +import com.android.ddmlib.AdbCommandRejectedException; import com.android.ddmlib.IDevice; +import com.android.ddmlib.SyncException; +import com.android.ddmlib.TimeoutException; import com.sonic.agent.bridge.android.AndroidDeviceBridgeTool; import com.sonic.agent.bridge.android.AndroidDeviceThreadPool; import org.slf4j.Logger; @@ -33,13 +36,13 @@ public Future start(String udId, AtomicReference banner, AtomicRefe int qua = 0; switch (pic) { case "low": - qua = 5; + qua = 10; break; case "middle": - qua = 10; + qua = 50; break; case "high": - qua = 50; + qua = 80; break; } int s; @@ -66,7 +69,19 @@ public Future start(String udId, AtomicReference banner, AtomicRefe int finalQua = qua; int finalC = c; Future miniCapPro = AndroidDeviceThreadPool.cachedThreadPool.submit(() -> - AndroidDeviceBridgeTool.startMiniCapServer(iDevice, finalQua, finalC, session)); + { + try { + AndroidDeviceBridgeTool.startMiniCapServer(iDevice, finalQua, finalC, session); + } catch (AdbCommandRejectedException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } catch (SyncException e) { + e.printStackTrace(); + } catch (TimeoutException e) { + e.printStackTrace(); + } + }); try { Thread.sleep(4000); } catch (InterruptedException e) { @@ -192,8 +207,6 @@ public Future start(String udId, AtomicReference banner, AtomicRefe size.put("msg", "size"); size.put("width", banner.get()[9]); size.put("height", banner.get()[13]); - size.put("vWidth", banner.get()[17]); - size.put("vHeight", banner.get()[21]); sendText(session, size.toJSONString()); } } diff --git a/src/main/java/com/sonic/agent/tools/ScrcpyTool.java b/src/main/java/com/sonic/agent/tools/ScrcpyTool.java index b0ec2c7a..fe063fa7 100644 --- a/src/main/java/com/sonic/agent/tools/ScrcpyTool.java +++ b/src/main/java/com/sonic/agent/tools/ScrcpyTool.java @@ -7,8 +7,8 @@ public class ScrcpyTool { public static void main(String[] args) throws IOException { - Socket capSocket = null; - OutputStream outputStream = null; + Socket capSocket; + OutputStream outputStream; capSocket = new Socket("localhost", 8666); outputStream = capSocket.getOutputStream(); while (capSocket.isConnected()) { diff --git a/src/main/java/com/sonic/agent/websockets/AndroidWSServer.java b/src/main/java/com/sonic/agent/websockets/AndroidWSServer.java index 57c8f525..0a0f22bc 100644 --- a/src/main/java/com/sonic/agent/websockets/AndroidWSServer.java +++ b/src/main/java/com/sonic/agent/websockets/AndroidWSServer.java @@ -3,7 +3,10 @@ import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; +import com.android.ddmlib.AdbCommandRejectedException; import com.android.ddmlib.IDevice; +import com.android.ddmlib.SyncException; +import com.android.ddmlib.TimeoutException; import com.sonic.agent.automation.AndroidStepHandler; import com.sonic.agent.automation.HandleDes; import com.sonic.agent.automation.RemoteDebugDriver; @@ -114,7 +117,19 @@ public void onOpen(Session session, @PathParam("key") String secretKey, @PathPar if (devicePlatformVersion < 9) { int finalMiniTouchPort = PortTool.getPort(); - Future miniTouchPro = AndroidDeviceThreadPool.cachedThreadPool.submit(() -> AndroidDeviceBridgeTool.miniTouchStart(iDevice)); + Future miniTouchPro = AndroidDeviceThreadPool.cachedThreadPool.submit(() -> { + try { + AndroidDeviceBridgeTool.miniTouchStart(iDevice); + } catch (AdbCommandRejectedException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } catch (SyncException e) { + e.printStackTrace(); + } catch (TimeoutException e) { + e.printStackTrace(); + } + }); AndroidDeviceThreadPool.cachedThreadPool.execute(() -> { try { Thread.sleep(3000); @@ -338,7 +353,7 @@ public void onMessage(String message, Session session) throws InterruptedExcepti int y1 = Integer.parseInt(xy1.substring(xy1.indexOf(",") + 1)); int x2 = Integer.parseInt(xy2.substring(0, xy2.indexOf(","))); int y2 = Integer.parseInt(xy2.substring(xy2.indexOf(",") + 1)); - AndroidDeviceBridgeTool.executeCommand(udIdMap.get(session), "input swipe " + x1 + " " + y1 + " " + x2 + " " + y2 + " 300"); + AndroidDeviceBridgeTool.executeCommand(udIdMap.get(session), "input swipe " + x1 + " " + y1 + " " + x2 + " " + y2 + " 200"); } } catch (Exception e) { e.printStackTrace(); From b3d64dc819477245e874202f81b854f6119121f1 Mon Sep 17 00:00:00 2001 From: ZhouYixun <291028775@qq.com> Date: Sun, 7 Nov 2021 12:12:15 +0800 Subject: [PATCH 06/10] save_update --- src/main/java/com/sonic/agent/tools/ScrcpyTool.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/sonic/agent/tools/ScrcpyTool.java b/src/main/java/com/sonic/agent/tools/ScrcpyTool.java index fe063fa7..eb3402d7 100644 --- a/src/main/java/com/sonic/agent/tools/ScrcpyTool.java +++ b/src/main/java/com/sonic/agent/tools/ScrcpyTool.java @@ -9,10 +9,13 @@ public class ScrcpyTool { public static void main(String[] args) throws IOException { Socket capSocket; OutputStream outputStream; + InputStream inputStream; capSocket = new Socket("localhost", 8666); outputStream = capSocket.getOutputStream(); + inputStream = capSocket.getInputStream(); + outputStream.write(0); while (capSocket.isConnected()) { - outputStream.write(0); + System.out.println(inputStream.read()); } System.out.println(1); } From c8d9f6d97e889606f003d361eb31cb13787a9633 Mon Sep 17 00:00:00 2001 From: ZhouYixun <291028775@qq.com> Date: Mon, 8 Nov 2021 20:01:46 +0800 Subject: [PATCH 07/10] ignore_version --- .../agent/automation/AndroidStepHandler.java | 10 ++++----- .../android/AndroidDeviceBridgeTool.java | 22 +++++++++---------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/main/java/com/sonic/agent/automation/AndroidStepHandler.java b/src/main/java/com/sonic/agent/automation/AndroidStepHandler.java index 7fab105f..07ab8d13 100644 --- a/src/main/java/com/sonic/agent/automation/AndroidStepHandler.java +++ b/src/main/java/com/sonic/agent/automation/AndroidStepHandler.java @@ -62,7 +62,7 @@ public class AndroidStepHandler { private AndroidDriver androidDriver; private JSONObject globalParams = new JSONObject(); //包版本 - private String version = ""; +// private String version = ""; //测试起始时间 private long startTime; //测试的包名 @@ -1378,10 +1378,10 @@ public void runMonkey(HandleDes handleDes, JSONObject content, List } catch (InterruptedException e) { e.printStackTrace(); } - if (version.length() == 0) { - version = AndroidDeviceBridgeTool.getAppOnlyVersion(udId, packageName); - } - log.sendStepLog(StepType.INFO, "", packageName + "包版本:" + version + +// if (version.length() == 0) { +// version = AndroidDeviceBridgeTool.getAppOnlyVersion(udId, packageName); +// } + log.sendStepLog(StepType.INFO, "", "测试目标包:" + packageName + (isOpenPackageListener ? "
应用包名监听器已开启..." : "") + (isOpenH5Listener ? "
H5页面监听器已开启..." : "") + (isOpenActivityListener ? "
黑名单Activity监听器..." : "") + diff --git a/src/main/java/com/sonic/agent/bridge/android/AndroidDeviceBridgeTool.java b/src/main/java/com/sonic/agent/bridge/android/AndroidDeviceBridgeTool.java index 9cc152c1..3bcc0fb9 100644 --- a/src/main/java/com/sonic/agent/bridge/android/AndroidDeviceBridgeTool.java +++ b/src/main/java/com/sonic/agent/bridge/android/AndroidDeviceBridgeTool.java @@ -484,15 +484,15 @@ public boolean isCancelled() { * @des 获取app版本信息 * @date 2021/8/16 15:29 */ - public static String getAppOnlyVersion(String udId, String packageName) { - IDevice iDevice = getIDeviceByUdId(udId); - //实质是获取安卓开发在gradle定义的versionName来定义版本号 - String version = executeCommand(iDevice, String.format("pm dump %s | grep 'versionName'", packageName)); - version = version.substring(version.indexOf("=") + 1, version.length() - 1); - if (version.length() > 50) { - version = version.substring(0, version.indexOf(" ") + 1); - } - //因为不同设备获取的信息不一样,所以需要去掉\r、\n - return version.replace("\r", "").replace("\n", ""); - } +// public static String getAppOnlyVersion(String udId, String packageName) { +// IDevice iDevice = getIDeviceByUdId(udId); +// //实质是获取安卓开发在gradle定义的versionName来定义版本号 +// String version = executeCommand(iDevice, String.format("pm dump %s | grep 'versionName'", packageName)); +// version = version.substring(version.indexOf("=") + 1, version.length() - 1); +// if (version.length() > 50) { +// version = version.substring(0, version.indexOf(" ") + 1); +// } +// //因为不同设备获取的信息不一样,所以需要去掉\r、\n +// return version.replace("\r", "").replace("\n", ""); +// } } From 8425f37eee43cee976dca6f9ee73aa266adbced2 Mon Sep 17 00:00:00 2001 From: ZhouYixun <291028775@qq.com> Date: Mon, 8 Nov 2021 20:02:40 +0800 Subject: [PATCH 08/10] save_version --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f58642eb..7f6b1a47 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.sonic sonic-agent - 1.0.0 + 1.1.2 jar From b03fec180539a5f55ada5072c83041e655168098 Mon Sep 17 00:00:00 2001 From: ZhouYixun <291028775@qq.com> Date: Tue, 9 Nov 2021 21:31:33 +0800 Subject: [PATCH 09/10] save_webview --- .../java/com/sonic/agent/websockets/AndroidWSServer.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/sonic/agent/websockets/AndroidWSServer.java b/src/main/java/com/sonic/agent/websockets/AndroidWSServer.java index 0a0f22bc..c27b4c22 100644 --- a/src/main/java/com/sonic/agent/websockets/AndroidWSServer.java +++ b/src/main/java/com/sonic/agent/websockets/AndroidWSServer.java @@ -200,8 +200,12 @@ public void onMessage(String message, Session session) throws InterruptedExcepti case "forwardView": { JSONObject forwardView = new JSONObject(); IDevice iDevice = udIdMap.get(session); - List webViewList = Arrays.asList(AndroidDeviceBridgeTool - .executeCommand(iDevice, "cat /proc/net/unix | grep webview").split("\n")); + List wList = Arrays.asList("webview", "WebView"); + List webViewList = new ArrayList<>(); + for (String w : wList) { + webViewList.addAll(Arrays.asList(AndroidDeviceBridgeTool + .executeCommand(iDevice, "cat /proc/net/unix | grep " + w).split("\n"))); + } Set webSet = new HashSet<>(); for (String w : webViewList) { if (w.contains("@") && w.indexOf("@") + 1 < w.length()) { From cf18238fcdec60bb84c21d4bb50c694281857cdc Mon Sep 17 00:00:00 2001 From: ZhouYixun <291028775@qq.com> Date: Wed, 10 Nov 2021 13:08:26 +0800 Subject: [PATCH 10/10] v1.2.0-beta1 --- .../aosp/libs/android-31/arm64-v8a/minicap.so | Bin 0 -> 24488 bytes .../libs/android-31/armeabi-v7a/minicap.so | Bin 0 -> 20956 bytes .../aosp/libs/android-31/x86/minicap.so | Bin 0 -> 20532 bytes .../aosp/libs/android-31/x86_64/minicap.so | Bin 0 -> 20136 bytes pom.xml | 2 +- .../agent/automation/AndroidStepHandler.java | 23 ++++++------ .../agent/automation/RemoteDebugDriver.java | 35 ++++++++++++------ .../com/sonic/agent/tools/MiniCapTool.java | 4 +- .../agent/websockets/AndroidWSServer.java | 14 ++++--- src/main/resources/application.yml | 2 +- 10 files changed, 47 insertions(+), 33 deletions(-) create mode 100644 mini/minicap-shared/aosp/libs/android-31/arm64-v8a/minicap.so create mode 100644 mini/minicap-shared/aosp/libs/android-31/armeabi-v7a/minicap.so create mode 100644 mini/minicap-shared/aosp/libs/android-31/x86/minicap.so create mode 100644 mini/minicap-shared/aosp/libs/android-31/x86_64/minicap.so diff --git a/mini/minicap-shared/aosp/libs/android-31/arm64-v8a/minicap.so b/mini/minicap-shared/aosp/libs/android-31/arm64-v8a/minicap.so new file mode 100644 index 0000000000000000000000000000000000000000..999bbb4cd1c035f80eb3f4901b0332086a076643 GIT binary patch literal 24488 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 literal 0 HcmV?d00001 diff --git a/mini/minicap-shared/aosp/libs/android-31/x86_64/minicap.so b/mini/minicap-shared/aosp/libs/android-31/x86_64/minicap.so new file mode 100644 index 0000000000000000000000000000000000000000..03db4255ea6206226ad6dfc3b97510b4b8541944 GIT binary patch literal 20136 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 io.appium java-client - 7.5.1 + 7.6.0 diff --git a/src/main/java/com/sonic/agent/automation/AndroidStepHandler.java b/src/main/java/com/sonic/agent/automation/AndroidStepHandler.java index 07ab8d13..497e3225 100644 --- a/src/main/java/com/sonic/agent/automation/AndroidStepHandler.java +++ b/src/main/java/com/sonic/agent/automation/AndroidStepHandler.java @@ -16,9 +16,7 @@ import com.sonic.agent.interfaces.PlatformType; import com.sonic.agent.tools.PortTool; import com.sonic.agent.tools.UploadTools; -import io.appium.java_client.MobileBy; -import io.appium.java_client.MultiTouchAction; -import io.appium.java_client.TouchAction; +import io.appium.java_client.*; import io.appium.java_client.android.AndroidDriver; import io.appium.java_client.android.AndroidStartScreenRecordingOptions; import io.appium.java_client.android.appmanagement.AndroidInstallApplicationOptions; @@ -48,6 +46,7 @@ import java.time.Duration; import java.util.*; import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import static org.testng.Assert.*; @@ -134,6 +133,7 @@ public void startAndroidDriver(String udId) throws InterruptedException { desiredCapabilities.setCapability("skipLogcatCapture", true); try { androidDriver = new AndroidDriver(AppiumServer.service.getUrl(), desiredCapabilities); + androidDriver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS); log.sendStepLog(StepType.PASS, "连接设备驱动成功", ""); } catch (Exception e) { log.sendStepLog(StepType.ERROR, "连接设备驱动失败!", ""); @@ -1406,32 +1406,31 @@ public void publicStep(HandleDes handleDes, String name, JSONArray stepArray) { } public WebElement findEle(String selector, String pathValue) { - WebDriverWait wait = new WebDriverWait(androidDriver, 10);//显式等待 WebElement we = null; switch (selector) { case "id": - we = wait.until(ExpectedConditions.presenceOfElementLocated(MobileBy.id(pathValue))); + we = androidDriver.findElementById(pathValue); break; case "name": - we = wait.until(ExpectedConditions.presenceOfElementLocated(MobileBy.name(pathValue))); + we = androidDriver.findElementByName(pathValue); break; case "xpath": - we = wait.until(ExpectedConditions.presenceOfElementLocated(MobileBy.xpath(pathValue))); + we = androidDriver.findElementByXPath(pathValue); break; case "cssSelector": - we = wait.until(ExpectedConditions.presenceOfElementLocated(MobileBy.cssSelector(pathValue))); + we = androidDriver.findElementByCssSelector(pathValue); break; case "className": - we = wait.until(ExpectedConditions.presenceOfElementLocated(MobileBy.className(pathValue))); + we = androidDriver.findElementByClassName(pathValue); break; case "tagName": - we = wait.until(ExpectedConditions.presenceOfElementLocated(MobileBy.tagName(pathValue))); + we = androidDriver.findElementByTagName(pathValue); break; case "linkText": - we = wait.until(ExpectedConditions.presenceOfElementLocated(MobileBy.linkText(pathValue))); + we = androidDriver.findElementByLinkText(pathValue); break; case "partialLinkText": - we = wait.until(ExpectedConditions.presenceOfElementLocated(MobileBy.partialLinkText(pathValue))); + we = androidDriver.findElementByPartialLinkText(pathValue); break; default: log.sendStepLog(StepType.ERROR, "查找控件元素失败", "这个控件元素类型: " + selector + " 不存在!!!"); diff --git a/src/main/java/com/sonic/agent/automation/RemoteDebugDriver.java b/src/main/java/com/sonic/agent/automation/RemoteDebugDriver.java index a6813b59..9024f7b7 100644 --- a/src/main/java/com/sonic/agent/automation/RemoteDebugDriver.java +++ b/src/main/java/com/sonic/agent/automation/RemoteDebugDriver.java @@ -2,11 +2,14 @@ import com.google.common.collect.ImmutableMap; import com.sonic.agent.tools.PortTool; +import com.sonic.agent.websockets.WebViewWSServer; import org.mitre.dsmiley.httpproxy.ProxyServlet; import org.openqa.selenium.WebDriver; import org.openqa.selenium.chrome.ChromeDriver; import org.openqa.selenium.chrome.ChromeOptions; import org.openqa.selenium.remote.DesiredCapabilities; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.context.annotation.Bean; @@ -24,6 +27,7 @@ */ @Configuration public class RemoteDebugDriver { + private static final Logger logger = LoggerFactory.getLogger(RemoteDebugDriver.class); private static String chromePath; public static int port = 0; public static WebDriver webDriver; @@ -54,19 +58,26 @@ public ServletRegistrationBean proxyServletRegistration() { @Bean @DependsOn(value = "setChromePath") public static void startChromeDriver() { - DesiredCapabilities desiredCapabilities = new DesiredCapabilities(); - ChromeOptions chromeOptions = new ChromeOptions(); - System.setProperty("webdriver.chrome.driver", chromePath); - if (port == 0) { - int debugPort = PortTool.getPort(); - port = debugPort; - chromeOptions.addArguments("--remote-debugging-port=" + debugPort); - } else { - chromeOptions.addArguments("--remote-debugging-port=" + port); + try { + DesiredCapabilities desiredCapabilities = new DesiredCapabilities(); + ChromeOptions chromeOptions = new ChromeOptions(); + System.setProperty("webdriver.chrome.driver", chromePath); + if (port == 0) { + int debugPort = PortTool.getPort(); + port = debugPort; + chromeOptions.addArguments("--remote-debugging-port=" + debugPort); + } else { + chromeOptions.addArguments("--remote-debugging-port=" + port); + } + chromeOptions.addArguments("--headless"); + chromeOptions.addArguments("--no-sandbox"); + chromeOptions.addArguments("--disable-gpu"); + chromeOptions.addArguments("--disable-dev-shm-usage"); + desiredCapabilities.setCapability(ChromeOptions.CAPABILITY, chromeOptions); + webDriver = new ChromeDriver(desiredCapabilities); + } catch (Exception e) { + logger.info("chromeDriver启动失败!"); } - chromeOptions.addArguments("--headless"); - desiredCapabilities.setCapability(ChromeOptions.CAPABILITY, chromeOptions); - webDriver = new ChromeDriver(desiredCapabilities); } public static void close() { diff --git a/src/main/java/com/sonic/agent/tools/MiniCapTool.java b/src/main/java/com/sonic/agent/tools/MiniCapTool.java index a2de6ae7..350b391a 100644 --- a/src/main/java/com/sonic/agent/tools/MiniCapTool.java +++ b/src/main/java/com/sonic/agent/tools/MiniCapTool.java @@ -39,10 +39,10 @@ public Future start(String udId, AtomicReference banner, AtomicRefe qua = 10; break; case "middle": - qua = 50; + qua = 30; break; case "high": - qua = 80; + qua = 60; break; } int s; diff --git a/src/main/java/com/sonic/agent/websockets/AndroidWSServer.java b/src/main/java/com/sonic/agent/websockets/AndroidWSServer.java index c27b4c22..ef40a048 100644 --- a/src/main/java/com/sonic/agent/websockets/AndroidWSServer.java +++ b/src/main/java/com/sonic/agent/websockets/AndroidWSServer.java @@ -232,11 +232,15 @@ public void onMessage(String message, Session session) throws InterruptedExcepti has.add(j); JSONObject r = new JSONObject(); r.put("port", port); - ResponseEntity infoEntity = - restTemplate.exchange("http://localhost:" + port + "/json/version", HttpMethod.GET, new HttpEntity(headers), LinkedHashMap.class); - if (infoEntity.getStatusCode() == HttpStatus.OK) { - r.put("version", infoEntity.getBody().get("Browser")); - r.put("package", infoEntity.getBody().get("Android-Package")); + try { + ResponseEntity infoEntity = + restTemplate.exchange("http://localhost:" + port + "/json/version", HttpMethod.GET, new HttpEntity(headers), LinkedHashMap.class); + if (infoEntity.getStatusCode() == HttpStatus.OK) { + r.put("version", infoEntity.getBody().get("Browser")); + r.put("package", infoEntity.getBody().get("Android-Package")); + } + } catch (Exception e) { + continue; } ResponseEntity responseEntity = restTemplate.exchange("http://localhost:" + port + "/json/list", HttpMethod.GET, new HttpEntity(headers), JSONArray.class); diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 9473ce6c..56c09806 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,4 +1,4 @@ spring: version: @project.version@ profiles: - active: dev,@profileActive@ \ No newline at end of file + active: prod,@profileActive@ \ No newline at end of file