diff --git a/pom.xml b/pom.xml index ce841fde..21f338b9 100644 --- a/pom.xml +++ b/pom.xml @@ -4,9 +4,9 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - com.sonic + org.cloud.sonic sonic-agent - v1.3.0-beta1.1 + v1.3.0-release jar diff --git a/src/main/java/com/sonic/agent/AgentApplication.java b/src/main/java/org/cloud/sonic/agent/AgentApplication.java similarity index 91% rename from src/main/java/com/sonic/agent/AgentApplication.java rename to src/main/java/org/cloud/sonic/agent/AgentApplication.java index 51f9adbe..06b34b00 100644 --- a/src/main/java/com/sonic/agent/AgentApplication.java +++ b/src/main/java/org/cloud/sonic/agent/AgentApplication.java @@ -1,33 +1,33 @@ -package com.sonic.agent; - -import com.sonic.agent.tools.SpringTool; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.web.embedded.tomcat.TomcatConnectorCustomizer; -import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Import; - -/** - * @author ZhouYiXun - * @des Agent端启动类 - * @date 2021/08/16 19:26 - */ -@Import(SpringTool.class) -@SpringBootApplication -public class AgentApplication { - @Value("${sonic.agent.port}") - private int port; - - public static void main(String[] args) { - SpringApplication.run(AgentApplication.class, args); - } - - @Bean - public TomcatServletWebServerFactory servletContainer() { - TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory(port); - factory.addConnectorCustomizers((TomcatConnectorCustomizer) connector -> connector.setProperty("relaxedQueryChars", "|{}[]\\")); - return factory; - } -} +package org.cloud.sonic.agent; + +import org.cloud.sonic.agent.tools.SpringTool; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.web.embedded.tomcat.TomcatConnectorCustomizer; +import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Import; + +/** + * @author ZhouYiXun + * @des Agent端启动类 + * @date 2021/08/16 19:26 + */ +@Import(SpringTool.class) +@SpringBootApplication +public class AgentApplication { + @Value("${sonic.agent.port}") + private int port; + + public static void main(String[] args) { + SpringApplication.run(AgentApplication.class, args); + } + + @Bean + public TomcatServletWebServerFactory servletContainer() { + TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory(port); + factory.addConnectorCustomizers((TomcatConnectorCustomizer) connector -> connector.setProperty("relaxedQueryChars", "|{}[]\\")); + return factory; + } +} diff --git a/src/main/java/com/sonic/agent/automation/AndroidStepHandler.java b/src/main/java/org/cloud/sonic/agent/automation/AndroidStepHandler.java similarity index 96% rename from src/main/java/com/sonic/agent/automation/AndroidStepHandler.java rename to src/main/java/org/cloud/sonic/agent/automation/AndroidStepHandler.java index e8422c84..0f9393bb 100644 --- a/src/main/java/com/sonic/agent/automation/AndroidStepHandler.java +++ b/src/main/java/org/cloud/sonic/agent/automation/AndroidStepHandler.java @@ -1,1559 +1,1553 @@ -package com.sonic.agent.automation; - -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.JSONArray; -import com.alibaba.fastjson.JSONObject; -import com.android.ddmlib.IDevice; -import com.sonic.agent.bridge.android.AndroidDeviceBridgeTool; -import com.sonic.agent.bridge.android.AndroidDeviceThreadPool; -import com.sonic.agent.interfaces.ErrorType; -import com.sonic.agent.interfaces.ResultDetailStatus; -import com.sonic.agent.interfaces.StepType; -import com.sonic.agent.maps.AndroidPasswordMap; -import com.sonic.agent.tools.*; -import com.sonic.agent.interfaces.PlatformType; -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; -import io.appium.java_client.android.appmanagement.AndroidTerminateApplicationOptions; -import io.appium.java_client.android.nativekey.AndroidKey; -import io.appium.java_client.android.nativekey.KeyEvent; -import io.appium.java_client.appmanagement.BaseTerminateApplicationOptions; -import io.appium.java_client.remote.AndroidMobileCapabilityType; -import io.appium.java_client.remote.AutomationName; -import io.appium.java_client.remote.MobileCapabilityType; -import io.appium.java_client.touch.WaitOptions; -import io.appium.java_client.touch.offset.PointOption; -import org.jsoup.Jsoup; -import org.jsoup.nodes.Attribute; -import org.jsoup.nodes.Document; -import org.openqa.selenium.*; -import org.openqa.selenium.chrome.ChromeOptions; -import org.openqa.selenium.remote.DesiredCapabilities; -import org.openqa.selenium.support.ui.ExpectedConditions; -import org.openqa.selenium.support.ui.WebDriverWait; -import org.springframework.core.env.Environment; -import org.springframework.core.io.FileSystemResource; -import org.springframework.http.ResponseEntity; -import org.springframework.util.Base64Utils; -import org.springframework.util.FileCopyUtils; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; -import org.springframework.web.client.RestTemplate; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -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.*; - -/** - * @author ZhouYiXun - * @des 安卓自动化处理类 - * @date 2021/8/16 20:10 - */ -public class AndroidStepHandler { - public LogTool log = new LogTool(); - private RestTemplate restTemplate = SpringTool.getBean(RestTemplate.class); - private Environment environment = SpringTool.getBean(Environment.class); - private String baseUrl = "http://" + environment.getProperty("sonic.server.host") - + ":" + environment.getProperty("sonic.server.folder-port") + "/api/folder"; - private AndroidDriver androidDriver; - private JSONObject globalParams = new JSONObject(); - //包版本 -// private String version = ""; - //测试起始时间 - private long startTime; - //测试的包名 - private String testPackage = ""; - private String udId = ""; - //测试状态 - private int status = 1; - - public void setTestMode(int caseId, int resultId, String udId, String type, String sessionId) { - log.caseId = caseId; - log.resultId = resultId; - log.udId = udId; - log.type = type; - log.sessionId = sessionId; - } - - public void setGlobalParams(JSONObject jsonObject) { - globalParams = jsonObject; - } - - /** - * @return - * @author ZhouYiXun - * @des new时开始计时 - * @date 2021/8/16 20:01 - */ - public AndroidStepHandler() { - startTime = Calendar.getInstance().getTimeInMillis(); - } - - /** - * @param udId - * @return void - * @author ZhouYiXun - * @des 启动安卓驱动,连接设备 - * @date 2021/8/16 20:01 - */ - public void startAndroidDriver(String udId) throws InterruptedException { - this.udId = udId; - DesiredCapabilities desiredCapabilities = new DesiredCapabilities(); - //微信webView配置 - ChromeOptions chromeOptions = new ChromeOptions(); - chromeOptions.setExperimentalOption("androidProcess", "com.tencent.mm:tools"); - desiredCapabilities.setCapability(ChromeOptions.CAPABILITY, chromeOptions); - //webView通用配置,自动下载匹配的driver - desiredCapabilities.setCapability(AndroidMobileCapabilityType.RECREATE_CHROME_DRIVER_SESSIONS, true); - desiredCapabilities.setCapability(AndroidMobileCapabilityType.CHROMEDRIVER_EXECUTABLE_DIR, "webview"); - desiredCapabilities.setCapability(AndroidMobileCapabilityType.CHROMEDRIVER_CHROME_MAPPING_FILE, "webview/version.json"); - //平台 - desiredCapabilities.setCapability(AndroidMobileCapabilityType.PLATFORM_NAME, Platform.ANDROID); - //选用的自动化框架 - desiredCapabilities.setCapability(MobileCapabilityType.AUTOMATION_NAME, AutomationName.ANDROID_UIAUTOMATOR2); - //关闭运行时阻塞其他Accessibility服务,开启的话其他不能使用了 - desiredCapabilities.setCapability("disableSuppressAccessibilityService", true); - //adb指令超时时间 - desiredCapabilities.setCapability(AndroidMobileCapabilityType.ADB_EXEC_TIMEOUT, 7200000); - //UIA2安装超时时间 - desiredCapabilities.setCapability("uiautomator2ServerInstallTimeout", 600000); - - //io.appium.uiautomator2.server io.appium.uiautomator2.server.test //io.appium.settings -// desiredCapabilities.setCapability("skipServerInstallation",true); -// desiredCapabilities.setCapability("disableWindowAnimation",true); -// desiredCapabilities.setCapability("skipDeviceInitialization",true); - //等待新命令超时时间 - desiredCapabilities.setCapability(MobileCapabilityType.NEW_COMMAND_TIMEOUT, 7200); - //不重置应用 - desiredCapabilities.setCapability(MobileCapabilityType.NO_RESET, true); - //单独唤起应用的话,这个需要设置空字符串 - desiredCapabilities.setCapability(MobileCapabilityType.BROWSER_NAME, ""); - //指定设备序列号 - desiredCapabilities.setCapability(MobileCapabilityType.UDID, udId); - //随机systemPort - desiredCapabilities.setCapability(AndroidMobileCapabilityType.SYSTEM_PORT, PortTool.getPort()); - 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, "连接设备驱动失败!", ""); - //测试标记为失败 - setResultDetailStatus(ResultDetailStatus.FAIL); - throw e; - } - Capabilities capabilities = androidDriver.getCapabilities(); - Thread.sleep(100); - log.androidInfo("Android", capabilities.getCapability("platformVersion").toString(), - udId, capabilities.getCapability("deviceManufacturer").toString(), - capabilities.getCapability("deviceModel").toString(), - capabilities.getCapability("deviceApiLevel").toString(), - capabilities.getCapability("deviceScreenSize").toString()); - } - - /** - * @return void - * @author ZhouYiXun - * @des 关闭driver - * @date 2021/8/16 20:21 - */ - public void closeAndroidDriver() { - try { - if (androidDriver != null) { - //终止测试包 - if (!testPackage.equals("")) { - try { - androidDriver.terminateApp(testPackage, new AndroidTerminateApplicationOptions().withTimeout(Duration.ofMillis(1000))); - } catch (Exception e) { - e.printStackTrace(); - } - } - androidDriver.quit(); - log.sendStepLog(StepType.PASS, "退出连接设备", ""); - } - } catch (Exception e) { - log.sendStepLog(StepType.WARN, "测试终止异常!请检查设备连接状态", ""); - //测试异常 - setResultDetailStatus(ResultDetailStatus.WARN); - e.printStackTrace(); - } - } - - public void waitDevice(int waitCount) { - log.sendStepLog(StepType.INFO, "设备非空闲状态!第" + waitCount + "次等待连接...", ""); - } - - public void waitDeviceTimeOut() { - log.sendStepLog(StepType.ERROR, "等待设备超时!测试跳过!", ""); - //测试标记为异常 - setResultDetailStatus(ResultDetailStatus.WARN); - } - - public AndroidDriver getAndroidDriver() { - return androidDriver; - } - - /** - * @param status - * @return void - * @author ZhouYiXun - * @des 设置测试状态 - * @date 2021/8/16 23:46 - */ - public void setResultDetailStatus(int status) { - if (status > this.status) { - this.status = status; - } - } - - public void sendStatus() { - log.sendStatusLog(status); - } - - //判断有无出错 - public int getStatus() { - return status; - } - - //调试每次重设状态 - public void resetResultDetailStatus() { - status = 1; - } - - /** - * @return boolean - * @author ZhouYiXun - * @des 检测是否低电量 - * @date 2021/8/16 23:16 - */ - public boolean getBattery() { - double battery = androidDriver.getBatteryInfo().getLevel(); - if (battery <= 0.1) { - log.sendStepLog(StepType.ERROR, "设备电量过低!", "跳过本次测试..."); - return true; - } else { - return false; - } - } - - /** - * @return void - * @author ZhouYiXun - * @des 获取性能信息(Appium自带的cpu和network方法貌似有bug, 后续再优化) - * @date 2021/8/16 23:16 - */ - public void getPerform() { - if (!testPackage.equals("")) { - List performanceData = Arrays.asList("memoryinfo", "batteryinfo"); - for (String performName : performanceData) { - List> re = androidDriver.getPerformanceData(testPackage, performName, 1); - List mem; - if (performName.equals("memoryinfo")) { - mem = Arrays.asList(0, 1, 2, 5, 6, 7); - } else { - mem = Collections.singletonList(0); - } - JSONObject perform = new JSONObject(); - for (Integer memNum : mem) { - perform.put(re.get(0).get(memNum).toString(), re.get(1).get(memNum)); - } - log.sendPerLog(testPackage, performName.equals("memoryinfo") ? 1 : 2, perform); - } - } - } - - //配合前端渲染,需要每个节点加上id - private int xpathId = 1; - - /** - * @return com.alibaba.fastjson.JSONArray - * @author ZhouYiXun - * @des 获取页面xpath信息 - * @date 2021/8/16 23:16 - */ - public JSONArray getResource() { - androidDriver.context("NATIVE_APP"); - JSONArray elementList = new JSONArray(); - Document doc = Jsoup.parse(androidDriver.getPageSource()); - String xpath = "/hierarchy"; - elementList.addAll(getChildren(doc.body().children().get(0).children(), xpath)); - xpathId = 1; - return elementList; - } - - /** - * @param elements - * @param xpath 父级节点xpath - * @return com.alibaba.fastjson.JSONArray - * @author ZhouYiXun - * @des 获取子节点信息 - * @date 2021/8/16 23:36 - */ - public JSONArray getChildren(org.jsoup.select.Elements elements, String xpath) { - JSONArray elementList = new JSONArray(); - for (int i = 0; i < elements.size(); i++) { - JSONObject ele = new JSONObject(); - //tag次数 - int tagCount = 0; - //兄弟节点index - int siblingIndex = 0; - String indexXpath; - for (int j = 0; j < elements.size(); j++) { - if (elements.get(j).attr("class").equals(elements.get(i).attr("class"))) { - tagCount++; - } - //当i==j时候,兄弟节点index等于tag出现次数,因为xpath多个tag的时候,[]里面下标是从1开始 - if (i == j) { - siblingIndex = tagCount; - } - } - //如果tag出现次数等于1,xpath结尾不添加[] - if (tagCount == 1) { - indexXpath = xpath + "/" + elements.get(i).attr("class"); - } else { - indexXpath = xpath + "/" + elements.get(i).attr("class") + "[" + siblingIndex + "]"; - } - ele.put("id", xpathId); - xpathId++; - ele.put("label", "<" + elements.get(i).attr("class") + ">"); - JSONObject detail = new JSONObject(); - detail.put("xpath", indexXpath); - for (Attribute attr : elements.get(i).attributes()) { - //把bounds字段拆出来解析,方便前端进行截取 - if (attr.getKey().equals("bounds")) { - String bounds = attr.getValue().replace("][", ":"); - String pointStart = bounds.substring(1, bounds.indexOf(":")); - String pointEnd = bounds.substring(bounds.indexOf(":") + 1, bounds.indexOf("]")); - detail.put("bStart", pointStart); - detail.put("bEnd", pointEnd); - } - detail.put(attr.getKey(), attr.getValue()); - } - ele.put("detail", detail); - if (elements.get(i).children().size() > 0) { - ele.put("children", getChildren(elements.get(i).children(), indexXpath)); - } - elementList.add(ele); - } - return elementList; - } - - /** - * @return void - * @author ZhouYiXun - * @des 开始录像 - * @date 2021/8/16 23:56 - */ - public void startRecord() { - try { - AndroidStartScreenRecordingOptions recordOption = new AndroidStartScreenRecordingOptions(); - //限制30分钟,appium支持的最长时间 - recordOption.withTimeLimit(Duration.ofMinutes(30)); - //开启bugReport,开启后录像会有相关附加信息 - recordOption.enableBugReport(); - //是否强制终止上次录像并开始新的录像 - recordOption.enableForcedRestart(); - //限制码率,防止录像过大 - recordOption.withBitRate(3000000); - androidDriver.startRecordingScreen(recordOption); - } catch (Exception e) { - log.sendRecordLog(false, "", ""); - } - } - - /** - * @return void - * @author ZhouYiXun - * @des 停止录像 - * @date 2021/8/16 23:56 - */ - public void stopRecord() { - File recordDir = new File("test-output/record"); - if (!recordDir.exists()) { - recordDir.mkdirs(); - } - long timeMillis = Calendar.getInstance().getTimeInMillis(); - String fileName = timeMillis + "_" + udId.substring(0, 4) + ".mp4"; - File uploadFile = new File(recordDir + File.separator + fileName); - try { - //加锁防止内存泄漏 - synchronized (AndroidStepHandler.class) { - FileOutputStream fileOutputStream = new FileOutputStream(uploadFile); - byte[] bytes = Base64Utils.decodeFromString((androidDriver.stopRecordingScreen())); - fileOutputStream.write(bytes); - fileOutputStream.close(); - } - log.sendRecordLog(true, fileName, UploadTools.uploadPatchRecord(uploadFile)); - } catch (Exception e) { - log.sendRecordLog(false, fileName, ""); - } - } - -// public void settingSonicPlugins(IDevice iDevice) { -// try { -// androidDriver.activateApp("com.sonic.plugins"); -// try { -// Thread.sleep(1000); -// } catch (Exception e) { -// } -// log.sendStepLog(StepType.INFO, "已安装Sonic插件!", ""); -// } catch (Exception e) { -// log.sendStepLog(StepType.ERROR, "未安装Sonic插件!", ""); -// throw e; -// } -// try { -// if (!androidDriver.currentActivity().equals("com.sonic.plugins.MainActivity")) { -// try { -// AndroidDeviceBridgeTool.executeCommand(iDevice, "input keyevent 4"); -// Thread.sleep(1000); -// } catch (Exception e) { -// } -// } -// findEle("xpath", "//android.widget.TextView[@text='服务状态:已开启']"); -// } catch (Exception e) { -// log.sendStepLog(StepType.ERROR, "未开启Sonic插件服务!请到辅助功能或无障碍开启", ""); -// throw e; -// } -// try { -// findEle("id", "com.sonic.plugins:id/password_edit").clear(); -// if (AndroidPasswordMap.getMap().get(log.udId) != null -// && (AndroidPasswordMap.getMap().get(log.udId) != null) -// && (!AndroidPasswordMap.getMap().get(log.udId).equals(""))) { -// findEle("id", "com.sonic.plugins:id/password_edit").sendKeys(AndroidPasswordMap.getMap().get(log.udId)); -// } else { -// findEle("id", "com.sonic.plugins:id/password_edit").sendKeys("sonic123456"); -// } -// findEle("id", "com.sonic.plugins:id/save").click(); -// } catch (Exception e) { -// log.sendStepLog(StepType.ERROR, "配置Sonic插件服务失败!", ""); -// throw e; -// } -// } - - public void install(HandleDes handleDes, String path) { - handleDes.setStepDes("安装应用"); - handleDes.setDetail("App安装路径: " + path); -// IDevice iDevice = AndroidDeviceBridgeTool.getIDeviceByUdId(log.udId); -// String manufacturer = iDevice.getProperty(IDevice.PROP_DEVICE_MANUFACTURER); - try { - androidDriver.unlockDevice(); - if (androidDriver.getConnection().isAirplaneModeEnabled()) { - androidDriver.toggleAirplaneMode(); - } - if (!androidDriver.getConnection().isWiFiEnabled()) { - androidDriver.toggleWifi(); - } - } catch (Exception e) { - log.sendStepLog(StepType.WARN, "安装前准备跳过...", ""); - } - log.sendStepLog(StepType.INFO, "", "开始安装App,请稍后..."); -// if (manufacturer.equals("OPPO") || manufacturer.equals("vivo") || manufacturer.equals("Meizu")) { -// settingSonicPlugins(iDevice); -// AndroidDeviceBridgeTool.executeCommand(iDevice, "input keyevent 3"); -// } -// //单独适配一下oppo -// if (manufacturer.equals("OPPO")) { -// try { -// androidDriver.installApp(path, new AndroidInstallApplicationOptions() -// .withAllowTestPackagesEnabled().withReplaceEnabled() -// .withGrantPermissionsEnabled().withTimeout(Duration.ofMillis(60000))); -// } catch (Exception e) { -// } -// //单独再适配colorOs -// if (androidDriver.currentActivity().equals(".verification.login.AccountActivity")) { -// try { -// if (AndroidPasswordMap.getMap().get(log.udId) != null -// && (AndroidPasswordMap.getMap().get(log.udId) != null) -// && (!AndroidPasswordMap.getMap().get(log.udId).equals(""))) { -// findEle("id", "com.coloros.safecenter:id/et_login_passwd_edit" -// ).sendKeys(AndroidPasswordMap.getMap().get(log.udId)); -// } else { -// findEle("id", "com.coloros.safecenter:id/et_login_passwd_edit" -// ).sendKeys("sonic123456"); -// } -// findEle("id", "android:id/button1").click(); -// } catch (Exception e) { -// } -// } -// AtomicInteger tryTime = new AtomicInteger(0); -// AndroidDeviceThreadPool.cachedThreadPool.execute(() -> { -// while (tryTime.get() < 20) { -// tryTime.getAndIncrement(); -// //部分oppo有继续安装 -// try { -// WebElement getContinueButton = findEle("id", "com.android.packageinstaller:id/virus_scan_panel"); -// Thread.sleep(2000); -// AndroidDeviceBridgeTool.executeCommand(iDevice, -// String.format("input tap %d %d", (getContinueButton.getRect().width) / 2 -// , getContinueButton.getRect().y + getContinueButton.getRect().height)); -// Thread.sleep(2000); -// } catch (Exception e) { -// e.printStackTrace(); -// } -// //低版本oppo安装按钮在右边 -// try { -// findEle("id", "com.android.packageinstaller:id/install_confirm_panel"); -// WebElement getInstallButton = findEle("id", "com.android.packageinstaller:id/bottom_button_layout"); -// Thread.sleep(2000); -// AndroidDeviceBridgeTool.executeCommand(iDevice, String.format("input tap %d %d" -// , ((getInstallButton.getRect().width) / 4) * 3 -// , getInstallButton.getRect().y + (getInstallButton.getRect().height) / 2)); -// Thread.sleep(2000); -// } catch (Exception e) { -// e.printStackTrace(); -// } -// //部分oppo无法点击 -// try { -// findEle("xpath", "//*[@text='应用权限']"); -// WebElement getInstallButton = findEle("id", "com.android.packageinstaller:id/install_confirm_panel"); -// Thread.sleep(2000); -// AndroidDeviceBridgeTool.executeCommand(iDevice, String.format("input tap %d %d" -// , (getInstallButton.getRect().width) / 2, getInstallButton.getRect().y + getInstallButton.getRect().height)); -// Thread.sleep(2000); -// } catch (Exception e) { -// e.printStackTrace(); -// } -// if (!androidDriver.getCurrentPackage().equals("com.android.packageinstaller")) { -// break; -// } -// } -// }); -// while (androidDriver.getCurrentPackage().equals("com.android.packageinstaller") && tryTime.get() < 20) { -// try { -// findEle("xpath", "//*[@text='完成']").click(); -// } catch (Exception e) { -// } -// } -// } else { - try { - androidDriver.installApp(path, new AndroidInstallApplicationOptions() - .withAllowTestPackagesEnabled().withReplaceEnabled() - .withGrantPermissionsEnabled().withTimeout(Duration.ofMillis(60000))); - } catch (Exception e) { - handleDes.setE(e); - return; - } -// } - } - - public void uninstall(HandleDes handleDes, String appPackage) { - handleDes.setStepDes("卸载应用"); - handleDes.setDetail("App包名: " + appPackage); - try { - androidDriver.removeApp(appPackage); - } catch (Exception e) { - handleDes.setE(e); - } - } - - /** - * @param packageName - * @return void - * @author ZhouYiXun - * @des 终止app - * @date 2021/8/16 23:46 - */ - public void terminate(HandleDes handleDes, String packageName) { - handleDes.setStepDes("终止应用"); - handleDes.setDetail("应用包名: " + packageName); - try { - androidDriver.terminateApp(packageName, new AndroidTerminateApplicationOptions().withTimeout(Duration.ofMillis(1000))); - } catch (Exception e) { - handleDes.setE(e); - } - } - - public void runBackground(HandleDes handleDes, long time) { - handleDes.setStepDes("后台运行应用"); - handleDes.setDetail("后台运行App " + time + " ms"); - try { - androidDriver.runAppInBackground(Duration.ofMillis(time)); - } catch (Exception e) { - handleDes.setE(e); - } - } - - public void openApp(HandleDes handleDes, String appPackage) { - handleDes.setStepDes("打开应用"); - handleDes.setDetail("App包名: " + appPackage); - try { - testPackage = appPackage; - androidDriver.activateApp(appPackage); - } catch (Exception e) { - handleDes.setE(e); - } - } - - public void rotateDevice(HandleDes handleDes, String text) { - try { - String s = ""; - switch (text) { - case "screenSub": - s = "sub"; - handleDes.setStepDes("左转屏幕"); - break; - case "screenAdd": - s = "add"; - handleDes.setStepDes("右转屏幕"); - break; - case "screenAbort": - s = "abort"; - handleDes.setStepDes("关闭自动旋转"); - break; - } - AndroidDeviceBridgeTool.screen(AndroidDeviceBridgeTool.getIDeviceByUdId(udId), s); - } catch (Exception e) { - handleDes.setE(e); - } - } - - public void lock(HandleDes handleDes) { - handleDes.setStepDes("锁定屏幕"); - try { - androidDriver.lockDevice(); - } catch (Exception e) { - handleDes.setE(e); - } - } - - public void unLock(HandleDes handleDes) { - handleDes.setStepDes("解锁屏幕"); - try { - androidDriver.unlockDevice(); - } catch (Exception e) { - handleDes.setE(e); - } - } - - public void airPlaneMode(HandleDes handleDes) { - handleDes.setStepDes("切换飞行模式"); - try { - androidDriver.toggleAirplaneMode(); - } catch (Exception e) { - handleDes.setE(e); - } - } - - public void wifiMode(HandleDes handleDes) { - handleDes.setStepDes("打开WIFI网络"); - try { - if (!androidDriver.getConnection().isWiFiEnabled()) { - androidDriver.toggleWifi(); - } - } catch (Exception e) { - handleDes.setE(e); - } - } - - public void locationMode(HandleDes handleDes) { - handleDes.setStepDes("切换位置服务"); - try { - androidDriver.toggleLocationServices(); - } catch (Exception e) { - handleDes.setE(e); - } - } - - public void asserts(HandleDes handleDes, String actual, String expect, String type) { - handleDes.setDetail("真实值: " + actual + " 期望值: " + expect); - try { - switch (type) { - case "assertEquals": - handleDes.setStepDes("断言验证(相等)"); - assertEquals(actual, expect); - break; - case "assertTrue": - handleDes.setStepDes("断言验证(包含)"); - assertTrue(actual.contains(expect)); - break; - case "assertNotTrue": - handleDes.setStepDes("断言验证(不包含)"); - assertFalse(actual.contains(expect)); - break; - } - } catch (AssertionError e) { - handleDes.setE(e); - } - } - - public String getText(HandleDes handleDes, String des, String selector, String pathValue) { - String s = ""; - handleDes.setStepDes("获取" + des + "文本"); - handleDes.setDetail("获取" + selector + ":" + pathValue + "文本"); - try { - s = findEle(selector, pathValue).getText(); - log.sendStepLog(StepType.INFO, "", "文本获取结果: " + s); - } catch (Exception e) { - handleDes.setE(e); - } - return s; - } - - public void hideKey(HandleDes handleDes) { - handleDes.setStepDes("隐藏键盘"); - handleDes.setDetail("隐藏弹出键盘"); - try { - androidDriver.hideKeyboard(); - } catch (Exception e) { - handleDes.setE(e); - } - } - - public void toWebView(HandleDes handleDes, String webViewName) { - handleDes.setStepDes("切换到" + webViewName); - try { - androidDriver.context(webViewName); - } catch (Exception e) { - handleDes.setE(e); - } - } - - public void click(HandleDes handleDes, String des, String selector, String pathValue) { - handleDes.setStepDes("点击" + des); - handleDes.setDetail("点击" + selector + ": " + pathValue); - try { - findEle(selector, pathValue).click(); - } catch (Exception e) { - handleDes.setE(e); - } - } - - public void sendKeys(HandleDes handleDes, String des, String selector, String pathValue, String keys) { - if (keys.contains("{{random}}")) { - String random = (int) (Math.random() * 10 + Math.random() * 10 * 2) + 5 + ""; - keys = keys.replace("{{random}}", random); - } - if (keys.contains("{{timestamp}}")) { - String timeMillis = Calendar.getInstance().getTimeInMillis() + ""; - keys = keys.replace("{{timestamp}}", timeMillis); - } - keys = replaceTrans(keys); - handleDes.setStepDes("对" + des + "输入内容"); - handleDes.setDetail("对" + selector + ": " + pathValue + " 输入: " + keys); - try { - findEle(selector, pathValue).sendKeys(keys); - } catch (Exception e) { - handleDes.setE(e); - } - } - - public void getTextAndAssert(HandleDes handleDes, String des, String selector, String pathValue, String expect) { - handleDes.setStepDes("获取" + des + "文本"); - handleDes.setDetail("获取" + selector + ":" + pathValue + "文本"); - try { - String s = findEle(selector, pathValue).getText(); - log.sendStepLog(StepType.INFO, "", "文本获取结果: " + s); - try { - expect = replaceTrans(expect); - assertEquals(s, expect); - log.sendStepLog(StepType.INFO, "验证文本", "真实值: " + s + " 期望值: " + expect); - } catch (AssertionError e) { - log.sendStepLog(StepType.ERROR, "验证" + des + "文本失败!", ""); - handleDes.setE(e); - } - } catch (Exception e) { - handleDes.setE(e); - } - } - - public void longPressPoint(HandleDes handleDes, String des, String xy, int time) { - int x = Integer.parseInt(xy.substring(0, xy.indexOf(","))); - int y = Integer.parseInt(xy.substring(xy.indexOf(",") + 1)); - handleDes.setStepDes("长按" + des); - handleDes.setDetail("长按坐标" + time + "毫秒 (" + x + "," + y + ")"); - try { - TouchAction ta = new TouchAction(androidDriver); - ta.longPress(PointOption.point(x, y)).waitAction(WaitOptions.waitOptions(Duration.ofMillis(time))).release().perform(); - } catch (Exception e) { - handleDes.setE(e); - } - } - - public void keyCode(HandleDes handleDes, String key) { - handleDes.setStepDes("按系统按键" + key + "键"); - try { - androidDriver.pressKey(new KeyEvent().withKey(AndroidKey.valueOf(key))); - } catch (Exception e) { - handleDes.setE(e); - } - } - - public void multiAction(HandleDes handleDes, String des1, String xy1, String des2, String xy2, String des3, String xy3, String des4, String xy4) { - int x1 = Integer.parseInt(xy1.substring(0, xy1.indexOf(","))); - 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)); - int x3 = Integer.parseInt(xy3.substring(0, xy3.indexOf(","))); - int y3 = Integer.parseInt(xy3.substring(xy3.indexOf(",") + 1)); - int x4 = Integer.parseInt(xy4.substring(0, xy4.indexOf(","))); - int y4 = Integer.parseInt(xy4.substring(xy4.indexOf(",") + 1)); - String detail = "坐标" + des1 + "( " + x1 + ", " + y1 + " )移动到坐标" + des2 + "( " + x2 + ", " + y2 + " ),同时坐标" + des3 + "( " + x3 + ", " + y3 + " )移动到坐标" + des4 + "( " + x4 + ", " + y4 + " )"; - handleDes.setStepDes("双指操作"); - handleDes.setDetail(detail); - try { - TouchAction hand1 = new TouchAction(androidDriver); - TouchAction hand2 = new TouchAction(androidDriver); - MultiTouchAction multiTouchAction = new MultiTouchAction(androidDriver); - hand1.press(PointOption.point(x1, y1)).moveTo(PointOption.point(x2, y2)).release(); - hand2.press(PointOption.point(x3, y3)).moveTo(PointOption.point(x4, y4)).release(); - multiTouchAction.add(hand1); - multiTouchAction.add(hand2); - multiTouchAction.perform(); - } catch (Exception e) { - handleDes.setE(e); - } - } - - public void tap(HandleDes handleDes, String des, String xy) { - int x = Integer.parseInt(xy.substring(0, xy.indexOf(","))); - int y = Integer.parseInt(xy.substring(xy.indexOf(",") + 1)); - handleDes.setStepDes("点击" + des); - handleDes.setDetail("点击坐标(" + x + "," + y + ")"); - try { - TouchAction ta = new TouchAction(androidDriver); - ta.tap(PointOption.point(x, y)).perform(); - } catch (Exception e) { - handleDes.setE(e); - } - } - - public void swipe(HandleDes handleDes, String des1, String xy1, String des2, String xy2) { - int x1 = Integer.parseInt(xy1.substring(0, xy1.indexOf(","))); - 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)); - handleDes.setStepDes("滑动拖拽" + des1 + "到" + des2); - handleDes.setDetail("拖动坐标(" + x1 + "," + y1 + ")到(" + x2 + "," + y2 + ")"); - try { - TouchAction ta = new TouchAction(androidDriver); - ta.press(PointOption.point(x1, y1)).waitAction(WaitOptions.waitOptions(Duration.ofMillis(300))).moveTo(PointOption.point(x2, y2)).release().perform(); - } catch (Exception e) { - handleDes.setE(e); - } - } - - public void longPress(HandleDes handleDes, String des, String selector, String pathValue, int time) { - handleDes.setStepDes("长按" + des); - handleDes.setDetail("长按控件元素" + time + "毫秒 "); - try { - TouchAction ta = new TouchAction(androidDriver); - WebElement webElement = findEle(selector, pathValue); - int x = webElement.getLocation().getX(); - int y = webElement.getLocation().getY(); - Duration duration = Duration.ofMillis(time); - ta.longPress(PointOption.point(x, y)).waitAction(WaitOptions.waitOptions(duration)).release().perform(); - } catch (Exception e) { - handleDes.setE(e); - } - } - - public void clear(HandleDes handleDes, String des, String selector, String pathValue) { - handleDes.setStepDes("清空" + des); - handleDes.setDetail("清空" + selector + ": " + pathValue); - try { - findEle(selector, pathValue).clear(); - } catch (Exception e) { - handleDes.setE(e); - } - } - - public void getTitle(HandleDes handleDes, String expect) { - String title = androidDriver.getTitle(); - handleDes.setStepDes("验证网页标题"); - handleDes.setDetail("标题:" + title + ",期望值:" + expect); - try { - assertEquals(title, expect); - } catch (AssertionError e) { - handleDes.setE(e); - } - } - - public void clickByImg(HandleDes handleDes, String des, String pathValue) throws Exception { - handleDes.setStepDes("点击图片" + des); - handleDes.setDetail(pathValue); - File file = null; - if (pathValue.startsWith("http")) { - try { - file = DownImageTool.download(pathValue); - } catch (Exception e) { - handleDes.setE(e); - return; - } - } - File localCap = getScreenToLocal(); - FindResult findResult; - FileSystemResource resource1 = new FileSystemResource(file); - FileSystemResource resource2 = new FileSystemResource(localCap); - MultiValueMap param = new LinkedMultiValueMap<>(); - param.add("file1", resource1); - param.add("file2", resource2); - param.add("type", "finder"); - try { - ResponseEntity responseEntity = - restTemplate.postForEntity(baseUrl + "/upload/cv", param, JSONObject.class); - if (responseEntity.getBody().getInteger("code") == 2000) { - findResult = responseEntity.getBody().getJSONObject("data").toJavaObject(FindResult.class); - if (findResult != null) { - try { - log.sendStepLog(StepType.INFO, "图片定位到坐标:(" + findResult.getX() + "," + findResult.getY() + ") 耗时:" + findResult.getTime() + " ms", - findResult.getUrl()); - TouchAction ta = new TouchAction(androidDriver); - ta.tap(PointOption.point(findResult.getX(), findResult.getY())).perform(); - } catch (Exception e) { - log.sendStepLog(StepType.ERROR, "点击" + des + "失败!", ""); - handleDes.setE(e); - } - } - } else if (responseEntity.getBody().getInteger("code") == 4003) { - handleDes.setE(new Exception("图像匹配失败!")); - } else { - handleDes.setE(new Exception("点击失败!cv服务出错!")); - } - } catch (Exception e) { - handleDes.setE(new Exception("点击失败!cv服务访问出错!")); - } finally { - file.delete(); - localCap.delete(); - } - } - - public void readText(HandleDes handleDes, String language, String text) throws Exception { -// TextReader textReader = new TextReader(); -// String result = textReader.getTessResult(getScreenToLocal(), language); -// log.sendStepLog(StepType.INFO, "", -// "图像文字识别结果:
" + result); -// String filter = result.replaceAll(" ", ""); - handleDes.setStepDes("图像文字识别"); - handleDes.setDetail("(该功能暂时关闭)期望包含文本:" + text); -// if (!filter.contains(text)) { -// handleDes.setE(new Exception("图像文字识别不通过!")); -// } - } - - public void toHandle(HandleDes handleDes, String titleName) throws Exception { - handleDes.setStepDes("切换Handle"); - Thread.sleep(1000); - Set handle = androidDriver.getWindowHandles();//获取handles - String ha; - for (int i = 1; i <= handle.size(); i++) { - ha = (String) handle.toArray()[handle.size() - i];//查找handle - try { - androidDriver.switchTo().window(ha);//切换到最后一个handle - } catch (Exception e) { - } - if (androidDriver.getTitle().equals(titleName)) { - handleDes.setDetail("切换到Handle:" + ha); - log.sendStepLog(StepType.INFO, "页面标题:" + androidDriver.getTitle(), ""); - break; - } - } - } - - public File getScreenToLocal() { - File file = ((TakesScreenshot) androidDriver).getScreenshotAs(OutputType.FILE); - File resultFile = new File("test-output/" + log.udId + Calendar.getInstance().getTimeInMillis() + ".jpg"); - try { - FileCopyUtils.copy(file, resultFile); - } catch (IOException e) { - e.printStackTrace(); - } - return resultFile; - } - - public String replaceTrans(String text) { - if (text.contains("{{") && text.contains("}}")) { - String tail = text.substring(text.indexOf("{{") + 2); - if (tail.contains("}}")) { - String child = tail.substring(tail.indexOf("}}") + 2); - String middle = tail.substring(0, tail.indexOf("}}")); - text = text.substring(0, text.indexOf("}}") + 2); - if (globalParams.getString(middle) != null) { - text = text.replace("{{" + middle + "}}", globalParams.getString(middle)); - } - text = text + replaceTrans(child); - } - } - return text; - } - - public void checkImage(HandleDes handleDes, String des, String pathValue, double matchThreshold) throws Exception { - log.sendStepLog(StepType.INFO, "开始检测" + des + "兼容", "检测与当前设备截图相似度,期望相似度为" + matchThreshold + "%"); - File file = null; - if (pathValue.startsWith("http")) { - file = DownImageTool.download(pathValue); - } - File localCap = getScreenToLocal(); - FileSystemResource resource1 = new FileSystemResource(file); - FileSystemResource resource2 = new FileSystemResource(localCap); - MultiValueMap param = new LinkedMultiValueMap<>(); - param.add("file1", resource1); - param.add("file2", resource2); - param.add("type", "checker"); - try { - ResponseEntity responseEntity = - restTemplate.postForEntity(baseUrl + "/upload/cv", param, JSONObject.class); - if (responseEntity.getBody().getInteger("code") == 2000) { - double score = responseEntity.getBody().getDouble("data"); - handleDes.setStepDes("检测" + des + "图片相似度"); - handleDes.setDetail("相似度为" + score * 100 + "%"); - if (score == 0) { - handleDes.setE(new Exception("图片相似度检测不通过!比对图片分辨率不一致!")); - } else if (score < (matchThreshold / 100)) { - handleDes.setE(new Exception("图片相似度检测不通过!expect " + matchThreshold + " but " + score * 100)); - } - } else { - handleDes.setE(new Exception("图片相似度检测出错!cv服务出错!")); - } - } catch (Exception e) { - e.printStackTrace(); - handleDes.setE(new Exception("图片相似度检测出错!cv服务访问出错!")); - } finally { - file.delete(); - localCap.delete(); - } - } - - public void exceptionLog(Throwable e) { - log.sendStepLog(StepType.WARN, "", "异常信息: " + e.fillInStackTrace().toString()); - } - - public void errorScreen() { - try { - androidDriver.context("NATIVE_APP");//先切换回app - log.sendStepLog(StepType.WARN, "获取异常截图", UploadTools - .upload(((TakesScreenshot) androidDriver).getScreenshotAs(OutputType.FILE), "imageFiles")); - } catch (Exception e) { - log.sendStepLog(StepType.ERROR, "捕获截图失败", ""); - } - } - - public String stepScreen(HandleDes handleDes) { - handleDes.setStepDes("获取截图"); - String url = ""; - try { - androidDriver.context("NATIVE_APP");//先切换回app - url = UploadTools.upload(((TakesScreenshot) androidDriver) - .getScreenshotAs(OutputType.FILE), "imageFiles"); - handleDes.setDetail(url); - } catch (Exception e) { - handleDes.setE(e); - } - return url; - } - - public Set getWebView() { - Set contextNames = androidDriver.getContextHandles(); - return contextNames; - } - - public String getCurrentActivity() { - return androidDriver.currentActivity(); - } - - public void pause(HandleDes handleDes, int time) { - handleDes.setStepDes("强制等待"); - handleDes.setDetail("等待" + time + " ms"); - try { - Thread.sleep(time); - } catch (InterruptedException e) { - handleDes.setE(e); - } - } - - public void runMonkey(HandleDes handleDes, JSONObject content, List text) { - handleDes.setStepDes("运行随机事件测试完毕"); - String packageName = content.getString("packageName"); - int pctNum = content.getInteger("pctNum"); - if (!androidDriver.isAppInstalled(packageName)) { - log.sendStepLog(StepType.ERROR, "应用未安装!", "设备未安装 " + packageName); - handleDes.setE(new Exception("未安装应用")); - return; - } - IDevice iDevice = AndroidDeviceBridgeTool.getIDeviceByUdId(udId); - JSONArray options = content.getJSONArray("options"); - int width = androidDriver.manage().window().getSize().width; - int height = androidDriver.manage().window().getSize().height; - int sleepTime = 50; - int systemEvent = 0; - int tapEvent = 0; - int longPressEvent = 0; - int swipeEvent = 0; - int zoomEvent = 0; - int navEvent = 0; - boolean isOpenH5Listener = false; - boolean isOpenPackageListener = false; - boolean isOpenActivityListener = false; - boolean isOpenNetworkListener = false; - if (!options.isEmpty()) { - for (int i = options.size() - 1; i >= 0; i--) { - JSONObject jsonOption = (JSONObject) options.get(i); - if (jsonOption.getString("name").equals("sleepTime")) { - sleepTime = jsonOption.getInteger("value"); - } - if (jsonOption.getString("name").equals("systemEvent")) { - systemEvent = jsonOption.getInteger("value"); - } - if (jsonOption.getString("name").equals("tapEvent")) { - tapEvent = jsonOption.getInteger("value"); - } - if (jsonOption.getString("name").equals("longPressEvent")) { - longPressEvent = jsonOption.getInteger("value"); - } - if (jsonOption.getString("name").equals("swipeEvent")) { - swipeEvent = jsonOption.getInteger("value"); - } - if (jsonOption.getString("name").equals("zoomEvent")) { - zoomEvent = jsonOption.getInteger("value"); - } - if (jsonOption.getString("name").equals("navEvent")) { - navEvent = jsonOption.getInteger("value"); - } - if (jsonOption.getString("name").equals("isOpenH5Listener")) { - isOpenH5Listener = jsonOption.getBoolean("value"); - } - if (jsonOption.getString("name").equals("isOpenPackageListener")) { - isOpenPackageListener = jsonOption.getBoolean("value"); - } - if (jsonOption.getString("name").equals("isOpenActivityListener")) { - isOpenActivityListener = jsonOption.getBoolean("value"); - } - if (jsonOption.getString("name").equals("isOpenNetworkListener")) { - isOpenNetworkListener = jsonOption.getBoolean("value"); - } - options.remove(options.get(i)); - } - } - int finalSleepTime = sleepTime; - int finalTapEvent = tapEvent; - int finalLongPressEvent = longPressEvent; - int finalSwipeEvent = swipeEvent; - int finalZoomEvent = zoomEvent; - int finalSystemEvent = systemEvent; - int finalNavEvent = navEvent; - Future randomThread = AndroidDeviceThreadPool.cachedThreadPool.submit(() -> { - log.sendStepLog(StepType.INFO, "", "随机事件数:" + pctNum + - "
目标应用:" + packageName - + "
用户操作时延:" + finalSleepTime + " ms" - + "
轻触事件权重:" + finalTapEvent - + "
长按事件权重:" + finalLongPressEvent - + "
滑动事件权重:" + finalSwipeEvent - + "
多点触控事件权重:" + finalZoomEvent - + "
物理按键事件权重:" + finalSystemEvent - + "
系统事件权重:" + finalNavEvent - ); - openApp(new HandleDes(), packageName); - TouchAction ta = new TouchAction(androidDriver); - TouchAction ta2 = new TouchAction(androidDriver); - MultiTouchAction multiTouchAction = new MultiTouchAction(androidDriver); - int totalCount = finalSystemEvent + finalTapEvent + finalLongPressEvent + finalSwipeEvent + finalZoomEvent + finalNavEvent; - for (int i = 0; i < pctNum; i++) { - try { - int random = new Random().nextInt(totalCount); - if (random >= 0 && random < finalSystemEvent) { - int key = new Random().nextInt(9); - String keyType = ""; - switch (key) { - case 0: - keyType = "HOME"; - break; - case 1: - keyType = "BACK"; - break; - case 2: - keyType = "MENU"; - break; - case 3: - keyType = "APP_SWITCH"; - break; - case 4: - keyType = "BRIGHTNESS_DOWN"; - break; - case 5: - keyType = "BRIGHTNESS_UP"; - break; - case 6: - keyType = "VOLUME_UP"; - break; - case 7: - keyType = "VOLUME_DOWN"; - break; - case 8: - keyType = "VOLUME_MUTE"; - break; - } - androidDriver.pressKey(new KeyEvent(AndroidKey.valueOf(keyType))); - } - if (random >= finalSystemEvent && random < (finalSystemEvent + finalTapEvent)) { - int x = new Random().nextInt(width - 60) + 60; - int y = new Random().nextInt(height - 60) + 60; - ta.tap(PointOption.point(x, y)).perform(); - } - if (random >= (finalSystemEvent + finalTapEvent) && random < (finalSystemEvent + finalTapEvent + finalLongPressEvent)) { - int x = new Random().nextInt(width - 60) + 60; - int y = new Random().nextInt(height - 60) + 60; - ta.longPress(PointOption.point(x, y)).waitAction(WaitOptions.waitOptions(Duration.ofSeconds(new Random().nextInt(3) + 1))).release().perform(); - } - if (random >= (finalSystemEvent + finalTapEvent + finalLongPressEvent) && random < (finalSystemEvent + finalTapEvent + finalLongPressEvent + finalSwipeEvent)) { - int x1 = new Random().nextInt(width - 60) + 60; - int y1 = new Random().nextInt(height - 80) + 80; - int x2 = new Random().nextInt(width - 60) + 60; - int y2 = new Random().nextInt(height - 80) + 80; - ta.press(PointOption.point(x1, y1)).waitAction(WaitOptions.waitOptions(Duration.ofMillis(200))).moveTo(PointOption.point(x2, y2)).release().perform(); - } - if (random >= (finalSystemEvent + finalTapEvent + finalLongPressEvent + finalSwipeEvent) && random < (finalSystemEvent + finalTapEvent + finalLongPressEvent + finalSwipeEvent + finalZoomEvent)) { - int x1 = new Random().nextInt(width - 80); - int y1 = new Random().nextInt(height - 80); - int x2 = new Random().nextInt(width - 100); - int y2 = new Random().nextInt(height - 80); - int x3 = new Random().nextInt(width - 100); - int y3 = new Random().nextInt(height - 80); - int x4 = new Random().nextInt(width - 100); - int y4 = new Random().nextInt(height - 80); - ta.press(PointOption.point(x1, y1)).waitAction(WaitOptions.waitOptions(Duration.ofMillis(200))).moveTo(PointOption.point(x2, y2)).release(); - ta2.press(PointOption.point(x3, y3)).waitAction(WaitOptions.waitOptions(Duration.ofMillis(200))).moveTo(PointOption.point(x4, y4)).release(); - multiTouchAction.add(ta); - multiTouchAction.add(ta2); - multiTouchAction.perform(); - } - if (random >= (finalSystemEvent + finalTapEvent + finalLongPressEvent + finalSwipeEvent + finalZoomEvent) && random < (finalSystemEvent + finalTapEvent + finalLongPressEvent + finalSwipeEvent + finalZoomEvent + finalNavEvent)) { - androidDriver.toggleWifi(); - } - Thread.sleep(finalSleepTime); - } catch (Throwable e) { - e.printStackTrace(); - } - } - } - ); - Boolean finalIsOpenH5Listener = isOpenH5Listener; - Future H5Listener = AndroidDeviceThreadPool.cachedThreadPool.submit(() -> { - if (finalIsOpenH5Listener) { - int h5Time = 0; - while (!randomThread.isDone()) { - try { - Thread.sleep(8000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - try { - if (androidDriver.findElementsByClassName("android.webkit.WebView").size() > 0) { - h5Time++; - AndroidDeviceBridgeTool.executeCommand(iDevice, "input keyevent 4"); - } else { - h5Time = 0; - } - if (h5Time >= 12) { - androidDriver.terminateApp(packageName, new AndroidTerminateApplicationOptions().withTimeout(Duration.ofMillis(1000))); - h5Time = 0; - } - } catch (Throwable e) { - e.printStackTrace(); - } - } - } - } - ); - boolean finalIsOpenPackageListener = isOpenPackageListener; - Future packageListener = AndroidDeviceThreadPool.cachedThreadPool.submit(() -> { - if (finalIsOpenPackageListener) { - while (!randomThread.isDone()) { - int waitTime = 0; - while (waitTime <= 10 && (!randomThread.isDone())) { - try { - Thread.sleep(5000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - if (!androidDriver.getCurrentPackage().equals(packageName)) { - androidDriver.activateApp(packageName); - } - waitTime++; - } - androidDriver.activateApp(packageName); - } - } - } - ); - boolean finalIsOpenActivityListener = isOpenActivityListener; - Future activityListener = AndroidDeviceThreadPool.cachedThreadPool.submit(() -> { - if (finalIsOpenActivityListener) { - if (text.isEmpty()) { - return; - } - Set blackList = new HashSet<>(); - for (JSONObject activities : text) { - blackList.add(activities.getString("name")); - } - while (!randomThread.isDone()) { - try { - Thread.sleep(8000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - if (blackList.contains(getCurrentActivity())) { - AndroidDeviceBridgeTool.executeCommand(iDevice, "input keyevent 4"); - } else continue; - try { - Thread.sleep(8000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - if (blackList.contains(getCurrentActivity())) { - androidDriver.terminateApp(packageName, new AndroidTerminateApplicationOptions().withTimeout(Duration.ofMillis(1000))); - } - } - } - } - ); - boolean finalIsOpenNetworkListener = isOpenNetworkListener; - Future networkListener = AndroidDeviceThreadPool.cachedThreadPool.submit(() -> { - if (finalIsOpenNetworkListener) { - while (!randomThread.isDone()) { - try { - Thread.sleep(8000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - if (androidDriver.getConnection().isAirplaneModeEnabled()) { - androidDriver.toggleAirplaneMode(); - } - try { - Thread.sleep(8000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - if (!androidDriver.getConnection().isWiFiEnabled()) { - androidDriver.toggleWifi(); - } - } - } - } - ); - try { - Thread.sleep(500); - } catch (InterruptedException e) { - e.printStackTrace(); - } -// if (version.length() == 0) { -// version = AndroidDeviceBridgeTool.getAppOnlyVersion(udId, packageName); -// } - log.sendStepLog(StepType.INFO, "", "测试目标包:" + packageName + - (isOpenPackageListener ? "
应用包名监听器已开启..." : "") + - (isOpenH5Listener ? "
H5页面监听器已开启..." : "") + - (isOpenActivityListener ? "
黑名单Activity监听器..." : "") + - (isOpenNetworkListener ? "
网络状态监听器已开启..." : "")); - while (!randomThread.isDone() || (!packageListener.isDone()) || (!activityListener.isDone()) || (!networkListener.isDone()) || (!H5Listener.isDone())) { - } - } - - public void publicStep(HandleDes handleDes, String name, JSONArray stepArray) { - handleDes.setStepDes("执行公共步骤 " + name); - log.sendStepLog(StepType.WARN, "公共步骤 " + name + " 开始执行", ""); - for (Object publicStep : stepArray) { - JSONObject stepDetail = (JSONObject) publicStep; - try { - runStep(stepDetail); - } catch (Throwable e) { - handleDes.setE(e); - break; - } - } - } - - public WebElement findEle(String selector, String pathValue) { - WebElement we = null; - switch (selector) { - case "id": - we = androidDriver.findElementById(pathValue); - break; - case "name": - we = androidDriver.findElementByName(pathValue); - break; - case "xpath": - we = androidDriver.findElementByXPath(pathValue); - break; - case "cssSelector": - we = androidDriver.findElementByCssSelector(pathValue); - break; - case "className": - we = androidDriver.findElementByClassName(pathValue); - break; - case "tagName": - we = androidDriver.findElementByTagName(pathValue); - break; - case "linkText": - we = androidDriver.findElementByLinkText(pathValue); - break; - case "partialLinkText": - we = androidDriver.findElementByPartialLinkText(pathValue); - break; - default: - log.sendStepLog(StepType.ERROR, "查找控件元素失败", "这个控件元素类型: " + selector + " 不存在!!!"); - break; - } - return we; - } - - public void runStep(JSONObject stepJSON) throws Throwable { - JSONObject step = stepJSON.getJSONObject("step"); - JSONArray eleList = step.getJSONArray("elements"); - HandleDes handleDes = new HandleDes(); - switch (step.getString("stepType")) { - case "toWebView": - toWebView(handleDes, step.getString("content")); - break; - case "toHandle": - toHandle(handleDes, step.getString("content")); - break; - case "readText": - readText(handleDes, step.getString("content"), step.getString("text")); - break; - case "clickByImg": - clickByImg(handleDes, eleList.getJSONObject(0).getString("eleName") - , eleList.getJSONObject(0).getString("eleValue")); - break; - case "click": - click(handleDes, eleList.getJSONObject(0).getString("eleName"), eleList.getJSONObject(0).getString("eleType") - , eleList.getJSONObject(0).getString("eleValue")); - break; - case "getTitle": - getTitle(handleDes, step.getString("content")); - break; - case "sendKeys": - sendKeys(handleDes, eleList.getJSONObject(0).getString("eleName"), eleList.getJSONObject(0).getString("eleType") - , eleList.getJSONObject(0).getString("eleValue"), step.getString("content")); - break; - case "getText": - getTextAndAssert(handleDes, eleList.getJSONObject(0).getString("eleName"), eleList.getJSONObject(0).getString("eleType") - , eleList.getJSONObject(0).getString("eleValue"), step.getString("content")); - break; - case "clear": - clear(handleDes, eleList.getJSONObject(0).getString("eleName"), eleList.getJSONObject(0).getString("eleType") - , eleList.getJSONObject(0).getString("eleValue")); - break; - case "longPress": - longPress(handleDes, eleList.getJSONObject(0).getString("eleName"), eleList.getJSONObject(0).getString("eleType") - , eleList.getJSONObject(0).getString("eleValue"), Integer.parseInt(step.getString("content"))); - break; - case "swipe": - swipe(handleDes, eleList.getJSONObject(0).getString("eleName"), eleList.getJSONObject(0).getString("eleValue") - , eleList.getJSONObject(1).getString("eleName"), eleList.getJSONObject(1).getString("eleValue")); - break; - case "tap": - tap(handleDes, eleList.getJSONObject(0).getString("eleName"), eleList.getJSONObject(0).getString("eleValue")); - break; - case "longPressPoint": - longPressPoint(handleDes, eleList.getJSONObject(0).getString("eleName"), eleList.getJSONObject(0).getString("eleValue") - , Integer.parseInt(step.getString("content"))); - break; - case "pause": - pause(handleDes, Integer.parseInt(step.getString("content"))); - break; - case "checkImage": - checkImage(handleDes, eleList.getJSONObject(0).getString("eleName"), eleList.getJSONObject(0).getString("eleValue") - , step.getDouble("content")); - break; - case "stepScreen": - stepScreen(handleDes); - break; - case "openApp": - openApp(handleDes, step.getString("text")); - break; - case "terminate": - terminate(handleDes, step.getString("text")); - break; - case "install": - install(handleDes, step.getString("text")); - break; - case "uninstall": - uninstall(handleDes, step.getString("text")); - break; - case "runBack": - runBackground(handleDes, Long.parseLong(step.getString("content"))); - break; - case "screenSub": - case "screenAdd": - case "screenAbort": - rotateDevice(handleDes, step.getString("stepType")); - break; - case "lock": - lock(handleDes); - break; - case "unLock": - unLock(handleDes); - break; - case "airPlaneMode": - airPlaneMode(handleDes); - break; - case "wifiMode": - wifiMode(handleDes); - break; - case "locationMode": - locationMode(handleDes); - break; - case "zoom": - multiAction(handleDes, eleList.getJSONObject(0).getString("eleName"), eleList.getJSONObject(0).getString("eleValue") - , eleList.getJSONObject(1).getString("eleName"), eleList.getJSONObject(1).getString("eleValue") - , eleList.getJSONObject(2).getString("eleName"), eleList.getJSONObject(2).getString("eleValue") - , eleList.getJSONObject(3).getString("eleName"), eleList.getJSONObject(3).getString("eleValue")); - break; - case "keyCode": - keyCode(handleDes, step.getString("content")); - break; - case "assertEquals": - case "assertTrue": - case "assertNotTrue": - String actual = replaceTrans(step.getString("text")); - String expect = replaceTrans(step.getString("content")); - asserts(handleDes, actual, expect, step.getString("stepType")); - break; - case "getTextValue": - globalParams.put(step.getString("content"), getText(handleDes, eleList.getJSONObject(0).getString("eleName") - , eleList.getJSONObject(0).getString("eleType"), eleList.getJSONObject(0).getString("eleValue"))); - break; - case "hideKey": - hideKey(handleDes); - break; - case "monkey": - runMonkey(handleDes, step.getJSONObject("content"), step.getJSONArray("text").toJavaList(JSONObject.class)); - break; - case "publicStep": - publicStep(handleDes, step.getString("content"), stepJSON.getJSONArray("pubSteps")); - } - switchType(step.getInteger("error"), handleDes.getStepDes(), handleDes.getDetail(), handleDes.getE()); - } - - public void switchType(int error, String step, String detail, Throwable e) throws Throwable { - if (e != null) { - switch (error) { - case ErrorType.IGNORE: - log.sendStepLog(StepType.PASS, step + "异常!已忽略...", detail); - break; - case ErrorType.WARNING: - log.sendStepLog(StepType.WARN, step + "异常!", detail); - setResultDetailStatus(ResultDetailStatus.WARN); - errorScreen(); - exceptionLog(e); - break; - case ErrorType.SHUTDOWN: - log.sendStepLog(StepType.ERROR, step + "异常!", detail); - setResultDetailStatus(ResultDetailStatus.FAIL); - errorScreen(); - exceptionLog(e); - throw e; - } - } else { - log.sendStepLog(StepType.PASS, step, detail); - } - } -} +package org.cloud.sonic.agent.automation; + +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.android.ddmlib.IDevice; +import org.cloud.sonic.agent.bridge.android.AndroidDeviceBridgeTool; +import org.cloud.sonic.agent.bridge.android.AndroidDeviceThreadPool; +import org.cloud.sonic.agent.interfaces.ErrorType; +import org.cloud.sonic.agent.interfaces.ResultDetailStatus; +import org.cloud.sonic.agent.interfaces.StepType; +import org.cloud.sonic.agent.tools.*; +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; +import io.appium.java_client.android.appmanagement.AndroidTerminateApplicationOptions; +import io.appium.java_client.android.nativekey.AndroidKey; +import io.appium.java_client.android.nativekey.KeyEvent; +import io.appium.java_client.remote.AndroidMobileCapabilityType; +import io.appium.java_client.remote.AutomationName; +import io.appium.java_client.remote.MobileCapabilityType; +import io.appium.java_client.touch.WaitOptions; +import io.appium.java_client.touch.offset.PointOption; +import org.cloud.sonic.agent.tools.*; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Attribute; +import org.jsoup.nodes.Document; +import org.openqa.selenium.*; +import org.openqa.selenium.chrome.ChromeOptions; +import org.openqa.selenium.remote.DesiredCapabilities; +import org.springframework.core.env.Environment; +import org.springframework.core.io.FileSystemResource; +import org.springframework.http.ResponseEntity; +import org.springframework.util.Base64Utils; +import org.springframework.util.FileCopyUtils; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestTemplate; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.time.Duration; +import java.util.*; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +import static org.testng.Assert.*; + +/** + * @author ZhouYiXun + * @des 安卓自动化处理类 + * @date 2021/8/16 20:10 + */ +public class AndroidStepHandler { + public LogTool log = new LogTool(); + private RestTemplate restTemplate = SpringTool.getBean(RestTemplate.class); + private Environment environment = SpringTool.getBean(Environment.class); + private String baseUrl = "http://" + environment.getProperty("sonic.server.host") + + ":" + environment.getProperty("sonic.server.folder-port") + "/api/folder"; + private AndroidDriver androidDriver; + private JSONObject globalParams = new JSONObject(); + //包版本 +// private String version = ""; + //测试起始时间 + private long startTime; + //测试的包名 + private String testPackage = ""; + private String udId = ""; + //测试状态 + private int status = 1; + + public void setTestMode(int caseId, int resultId, String udId, String type, String sessionId) { + log.caseId = caseId; + log.resultId = resultId; + log.udId = udId; + log.type = type; + log.sessionId = sessionId; + } + + public void setGlobalParams(JSONObject jsonObject) { + globalParams = jsonObject; + } + + /** + * @return + * @author ZhouYiXun + * @des new时开始计时 + * @date 2021/8/16 20:01 + */ + public AndroidStepHandler() { + startTime = Calendar.getInstance().getTimeInMillis(); + } + + /** + * @param udId + * @return void + * @author ZhouYiXun + * @des 启动安卓驱动,连接设备 + * @date 2021/8/16 20:01 + */ + public void startAndroidDriver(String udId) throws InterruptedException { + this.udId = udId; + DesiredCapabilities desiredCapabilities = new DesiredCapabilities(); + //微信webView配置 + ChromeOptions chromeOptions = new ChromeOptions(); + chromeOptions.setExperimentalOption("androidProcess", "com.tencent.mm:tools"); + desiredCapabilities.setCapability(ChromeOptions.CAPABILITY, chromeOptions); + //webView通用配置,自动下载匹配的driver + desiredCapabilities.setCapability(AndroidMobileCapabilityType.RECREATE_CHROME_DRIVER_SESSIONS, true); + desiredCapabilities.setCapability(AndroidMobileCapabilityType.CHROMEDRIVER_EXECUTABLE_DIR, "webview"); + desiredCapabilities.setCapability(AndroidMobileCapabilityType.CHROMEDRIVER_CHROME_MAPPING_FILE, "webview/version.json"); + //平台 + desiredCapabilities.setCapability(AndroidMobileCapabilityType.PLATFORM_NAME, Platform.ANDROID); + //选用的自动化框架 + desiredCapabilities.setCapability(MobileCapabilityType.AUTOMATION_NAME, AutomationName.ANDROID_UIAUTOMATOR2); + //关闭运行时阻塞其他Accessibility服务,开启的话其他不能使用了 + desiredCapabilities.setCapability("disableSuppressAccessibilityService", true); + //adb指令超时时间 + desiredCapabilities.setCapability(AndroidMobileCapabilityType.ADB_EXEC_TIMEOUT, 7200000); + //UIA2安装超时时间 + desiredCapabilities.setCapability("uiautomator2ServerInstallTimeout", 600000); + + //io.appium.uiautomator2.server io.appium.uiautomator2.server.test //io.appium.settings +// desiredCapabilities.setCapability("skipServerInstallation",true); +// desiredCapabilities.setCapability("disableWindowAnimation",true); +// desiredCapabilities.setCapability("skipDeviceInitialization",true); + //等待新命令超时时间 + desiredCapabilities.setCapability(MobileCapabilityType.NEW_COMMAND_TIMEOUT, 7200); + //不重置应用 + desiredCapabilities.setCapability(MobileCapabilityType.NO_RESET, true); + //单独唤起应用的话,这个需要设置空字符串 + desiredCapabilities.setCapability(MobileCapabilityType.BROWSER_NAME, ""); + //指定设备序列号 + desiredCapabilities.setCapability(MobileCapabilityType.UDID, udId); + //随机systemPort + desiredCapabilities.setCapability(AndroidMobileCapabilityType.SYSTEM_PORT, PortTool.getPort()); + 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, "连接设备驱动失败!", ""); + //测试标记为失败 + setResultDetailStatus(ResultDetailStatus.FAIL); + throw e; + } + Capabilities capabilities = androidDriver.getCapabilities(); + Thread.sleep(100); + log.androidInfo("Android", capabilities.getCapability("platformVersion").toString(), + udId, capabilities.getCapability("deviceManufacturer").toString(), + capabilities.getCapability("deviceModel").toString(), + capabilities.getCapability("deviceApiLevel").toString(), + capabilities.getCapability("deviceScreenSize").toString()); + } + + /** + * @return void + * @author ZhouYiXun + * @des 关闭driver + * @date 2021/8/16 20:21 + */ + public void closeAndroidDriver() { + try { + if (androidDriver != null) { + //终止测试包 + if (!testPackage.equals("")) { + try { + androidDriver.terminateApp(testPackage, new AndroidTerminateApplicationOptions().withTimeout(Duration.ofMillis(1000))); + } catch (Exception e) { + e.printStackTrace(); + } + } + androidDriver.quit(); + log.sendStepLog(StepType.PASS, "退出连接设备", ""); + } + } catch (Exception e) { + log.sendStepLog(StepType.WARN, "测试终止异常!请检查设备连接状态", ""); + //测试异常 + setResultDetailStatus(ResultDetailStatus.WARN); + e.printStackTrace(); + } + } + + public void waitDevice(int waitCount) { + log.sendStepLog(StepType.INFO, "设备非空闲状态!第" + waitCount + "次等待连接...", ""); + } + + public void waitDeviceTimeOut() { + log.sendStepLog(StepType.ERROR, "等待设备超时!测试跳过!", ""); + //测试标记为异常 + setResultDetailStatus(ResultDetailStatus.WARN); + } + + public AndroidDriver getAndroidDriver() { + return androidDriver; + } + + /** + * @param status + * @return void + * @author ZhouYiXun + * @des 设置测试状态 + * @date 2021/8/16 23:46 + */ + public void setResultDetailStatus(int status) { + if (status > this.status) { + this.status = status; + } + } + + public void sendStatus() { + log.sendStatusLog(status); + } + + //判断有无出错 + public int getStatus() { + return status; + } + + //调试每次重设状态 + public void resetResultDetailStatus() { + status = 1; + } + + /** + * @return boolean + * @author ZhouYiXun + * @des 检测是否低电量 + * @date 2021/8/16 23:16 + */ + public boolean getBattery() { + double battery = androidDriver.getBatteryInfo().getLevel(); + if (battery <= 0.1) { + log.sendStepLog(StepType.ERROR, "设备电量过低!", "跳过本次测试..."); + return true; + } else { + return false; + } + } + + /** + * @return void + * @author ZhouYiXun + * @des 获取性能信息(Appium自带的cpu和network方法貌似有bug, 后续再优化) + * @date 2021/8/16 23:16 + */ + public void getPerform() { + if (!testPackage.equals("")) { + List performanceData = Arrays.asList("memoryinfo", "batteryinfo"); + for (String performName : performanceData) { + List> re = androidDriver.getPerformanceData(testPackage, performName, 1); + List mem; + if (performName.equals("memoryinfo")) { + mem = Arrays.asList(0, 1, 2, 5, 6, 7); + } else { + mem = Collections.singletonList(0); + } + JSONObject perform = new JSONObject(); + for (Integer memNum : mem) { + perform.put(re.get(0).get(memNum).toString(), re.get(1).get(memNum)); + } + log.sendPerLog(testPackage, performName.equals("memoryinfo") ? 1 : 2, perform); + } + } + } + + //配合前端渲染,需要每个节点加上id + private int xpathId = 1; + + /** + * @return com.alibaba.fastjson.JSONArray + * @author ZhouYiXun + * @des 获取页面xpath信息 + * @date 2021/8/16 23:16 + */ + public JSONArray getResource() { + androidDriver.context("NATIVE_APP"); + JSONArray elementList = new JSONArray(); + Document doc = Jsoup.parse(androidDriver.getPageSource()); + String xpath = "/hierarchy"; + elementList.addAll(getChildren(doc.body().children().get(0).children(), xpath)); + xpathId = 1; + return elementList; + } + + /** + * @param elements + * @param xpath 父级节点xpath + * @return com.alibaba.fastjson.JSONArray + * @author ZhouYiXun + * @des 获取子节点信息 + * @date 2021/8/16 23:36 + */ + public JSONArray getChildren(org.jsoup.select.Elements elements, String xpath) { + JSONArray elementList = new JSONArray(); + for (int i = 0; i < elements.size(); i++) { + JSONObject ele = new JSONObject(); + //tag次数 + int tagCount = 0; + //兄弟节点index + int siblingIndex = 0; + String indexXpath; + for (int j = 0; j < elements.size(); j++) { + if (elements.get(j).attr("class").equals(elements.get(i).attr("class"))) { + tagCount++; + } + //当i==j时候,兄弟节点index等于tag出现次数,因为xpath多个tag的时候,[]里面下标是从1开始 + if (i == j) { + siblingIndex = tagCount; + } + } + //如果tag出现次数等于1,xpath结尾不添加[] + if (tagCount == 1) { + indexXpath = xpath + "/" + elements.get(i).attr("class"); + } else { + indexXpath = xpath + "/" + elements.get(i).attr("class") + "[" + siblingIndex + "]"; + } + ele.put("id", xpathId); + xpathId++; + ele.put("label", "<" + elements.get(i).attr("class") + ">"); + JSONObject detail = new JSONObject(); + detail.put("xpath", indexXpath); + for (Attribute attr : elements.get(i).attributes()) { + //把bounds字段拆出来解析,方便前端进行截取 + if (attr.getKey().equals("bounds")) { + String bounds = attr.getValue().replace("][", ":"); + String pointStart = bounds.substring(1, bounds.indexOf(":")); + String pointEnd = bounds.substring(bounds.indexOf(":") + 1, bounds.indexOf("]")); + detail.put("bStart", pointStart); + detail.put("bEnd", pointEnd); + } + detail.put(attr.getKey(), attr.getValue()); + } + ele.put("detail", detail); + if (elements.get(i).children().size() > 0) { + ele.put("children", getChildren(elements.get(i).children(), indexXpath)); + } + elementList.add(ele); + } + return elementList; + } + + /** + * @return void + * @author ZhouYiXun + * @des 开始录像 + * @date 2021/8/16 23:56 + */ + public void startRecord() { + try { + AndroidStartScreenRecordingOptions recordOption = new AndroidStartScreenRecordingOptions(); + //限制30分钟,appium支持的最长时间 + recordOption.withTimeLimit(Duration.ofMinutes(30)); + //开启bugReport,开启后录像会有相关附加信息 + recordOption.enableBugReport(); + //是否强制终止上次录像并开始新的录像 + recordOption.enableForcedRestart(); + //限制码率,防止录像过大 + recordOption.withBitRate(3000000); + androidDriver.startRecordingScreen(recordOption); + } catch (Exception e) { + log.sendRecordLog(false, "", ""); + } + } + + /** + * @return void + * @author ZhouYiXun + * @des 停止录像 + * @date 2021/8/16 23:56 + */ + public void stopRecord() { + File recordDir = new File("test-output/record"); + if (!recordDir.exists()) { + recordDir.mkdirs(); + } + long timeMillis = Calendar.getInstance().getTimeInMillis(); + String fileName = timeMillis + "_" + udId.substring(0, 4) + ".mp4"; + File uploadFile = new File(recordDir + File.separator + fileName); + try { + //加锁防止内存泄漏 + synchronized (AndroidStepHandler.class) { + FileOutputStream fileOutputStream = new FileOutputStream(uploadFile); + byte[] bytes = Base64Utils.decodeFromString((androidDriver.stopRecordingScreen())); + fileOutputStream.write(bytes); + fileOutputStream.close(); + } + log.sendRecordLog(true, fileName, UploadTools.uploadPatchRecord(uploadFile)); + } catch (Exception e) { + log.sendRecordLog(false, fileName, ""); + } + } + +// public void settingSonicPlugins(IDevice iDevice) { +// try { +// androidDriver.activateApp("com.sonic.plugins"); +// try { +// Thread.sleep(1000); +// } catch (Exception e) { +// } +// log.sendStepLog(StepType.INFO, "已安装Sonic插件!", ""); +// } catch (Exception e) { +// log.sendStepLog(StepType.ERROR, "未安装Sonic插件!", ""); +// throw e; +// } +// try { +// if (!androidDriver.currentActivity().equals("com.sonic.plugins.MainActivity")) { +// try { +// AndroidDeviceBridgeTool.executeCommand(iDevice, "input keyevent 4"); +// Thread.sleep(1000); +// } catch (Exception e) { +// } +// } +// findEle("xpath", "//android.widget.TextView[@text='服务状态:已开启']"); +// } catch (Exception e) { +// log.sendStepLog(StepType.ERROR, "未开启Sonic插件服务!请到辅助功能或无障碍开启", ""); +// throw e; +// } +// try { +// findEle("id", "com.sonic.plugins:id/password_edit").clear(); +// if (AndroidPasswordMap.getMap().get(log.udId) != null +// && (AndroidPasswordMap.getMap().get(log.udId) != null) +// && (!AndroidPasswordMap.getMap().get(log.udId).equals(""))) { +// findEle("id", "com.sonic.plugins:id/password_edit").sendKeys(AndroidPasswordMap.getMap().get(log.udId)); +// } else { +// findEle("id", "com.sonic.plugins:id/password_edit").sendKeys("sonic123456"); +// } +// findEle("id", "com.sonic.plugins:id/save").click(); +// } catch (Exception e) { +// log.sendStepLog(StepType.ERROR, "配置Sonic插件服务失败!", ""); +// throw e; +// } +// } + + public void install(HandleDes handleDes, String path) { + handleDes.setStepDes("安装应用"); + handleDes.setDetail("App安装路径: " + path); +// IDevice iDevice = AndroidDeviceBridgeTool.getIDeviceByUdId(log.udId); +// String manufacturer = iDevice.getProperty(IDevice.PROP_DEVICE_MANUFACTURER); + try { + androidDriver.unlockDevice(); + if (androidDriver.getConnection().isAirplaneModeEnabled()) { + androidDriver.toggleAirplaneMode(); + } + if (!androidDriver.getConnection().isWiFiEnabled()) { + androidDriver.toggleWifi(); + } + } catch (Exception e) { + log.sendStepLog(StepType.WARN, "安装前准备跳过...", ""); + } + log.sendStepLog(StepType.INFO, "", "开始安装App,请稍后..."); +// if (manufacturer.equals("OPPO") || manufacturer.equals("vivo") || manufacturer.equals("Meizu")) { +// settingSonicPlugins(iDevice); +// AndroidDeviceBridgeTool.executeCommand(iDevice, "input keyevent 3"); +// } +// //单独适配一下oppo +// if (manufacturer.equals("OPPO")) { +// try { +// androidDriver.installApp(path, new AndroidInstallApplicationOptions() +// .withAllowTestPackagesEnabled().withReplaceEnabled() +// .withGrantPermissionsEnabled().withTimeout(Duration.ofMillis(60000))); +// } catch (Exception e) { +// } +// //单独再适配colorOs +// if (androidDriver.currentActivity().equals(".verification.login.AccountActivity")) { +// try { +// if (AndroidPasswordMap.getMap().get(log.udId) != null +// && (AndroidPasswordMap.getMap().get(log.udId) != null) +// && (!AndroidPasswordMap.getMap().get(log.udId).equals(""))) { +// findEle("id", "com.coloros.safecenter:id/et_login_passwd_edit" +// ).sendKeys(AndroidPasswordMap.getMap().get(log.udId)); +// } else { +// findEle("id", "com.coloros.safecenter:id/et_login_passwd_edit" +// ).sendKeys("sonic123456"); +// } +// findEle("id", "android:id/button1").click(); +// } catch (Exception e) { +// } +// } +// AtomicInteger tryTime = new AtomicInteger(0); +// AndroidDeviceThreadPool.cachedThreadPool.execute(() -> { +// while (tryTime.get() < 20) { +// tryTime.getAndIncrement(); +// //部分oppo有继续安装 +// try { +// WebElement getContinueButton = findEle("id", "com.android.packageinstaller:id/virus_scan_panel"); +// Thread.sleep(2000); +// AndroidDeviceBridgeTool.executeCommand(iDevice, +// String.format("input tap %d %d", (getContinueButton.getRect().width) / 2 +// , getContinueButton.getRect().y + getContinueButton.getRect().height)); +// Thread.sleep(2000); +// } catch (Exception e) { +// e.printStackTrace(); +// } +// //低版本oppo安装按钮在右边 +// try { +// findEle("id", "com.android.packageinstaller:id/install_confirm_panel"); +// WebElement getInstallButton = findEle("id", "com.android.packageinstaller:id/bottom_button_layout"); +// Thread.sleep(2000); +// AndroidDeviceBridgeTool.executeCommand(iDevice, String.format("input tap %d %d" +// , ((getInstallButton.getRect().width) / 4) * 3 +// , getInstallButton.getRect().y + (getInstallButton.getRect().height) / 2)); +// Thread.sleep(2000); +// } catch (Exception e) { +// e.printStackTrace(); +// } +// //部分oppo无法点击 +// try { +// findEle("xpath", "//*[@text='应用权限']"); +// WebElement getInstallButton = findEle("id", "com.android.packageinstaller:id/install_confirm_panel"); +// Thread.sleep(2000); +// AndroidDeviceBridgeTool.executeCommand(iDevice, String.format("input tap %d %d" +// , (getInstallButton.getRect().width) / 2, getInstallButton.getRect().y + getInstallButton.getRect().height)); +// Thread.sleep(2000); +// } catch (Exception e) { +// e.printStackTrace(); +// } +// if (!androidDriver.getCurrentPackage().equals("com.android.packageinstaller")) { +// break; +// } +// } +// }); +// while (androidDriver.getCurrentPackage().equals("com.android.packageinstaller") && tryTime.get() < 20) { +// try { +// findEle("xpath", "//*[@text='完成']").click(); +// } catch (Exception e) { +// } +// } +// } else { + try { + androidDriver.installApp(path, new AndroidInstallApplicationOptions() + .withAllowTestPackagesEnabled().withReplaceEnabled() + .withGrantPermissionsEnabled().withTimeout(Duration.ofMillis(60000))); + } catch (Exception e) { + handleDes.setE(e); + return; + } +// } + } + + public void uninstall(HandleDes handleDes, String appPackage) { + handleDes.setStepDes("卸载应用"); + handleDes.setDetail("App包名: " + appPackage); + try { + androidDriver.removeApp(appPackage); + } catch (Exception e) { + handleDes.setE(e); + } + } + + /** + * @param packageName + * @return void + * @author ZhouYiXun + * @des 终止app + * @date 2021/8/16 23:46 + */ + public void terminate(HandleDes handleDes, String packageName) { + handleDes.setStepDes("终止应用"); + handleDes.setDetail("应用包名: " + packageName); + try { + androidDriver.terminateApp(packageName, new AndroidTerminateApplicationOptions().withTimeout(Duration.ofMillis(1000))); + } catch (Exception e) { + handleDes.setE(e); + } + } + + public void runBackground(HandleDes handleDes, long time) { + handleDes.setStepDes("后台运行应用"); + handleDes.setDetail("后台运行App " + time + " ms"); + try { + androidDriver.runAppInBackground(Duration.ofMillis(time)); + } catch (Exception e) { + handleDes.setE(e); + } + } + + public void openApp(HandleDes handleDes, String appPackage) { + handleDes.setStepDes("打开应用"); + handleDes.setDetail("App包名: " + appPackage); + try { + testPackage = appPackage; + androidDriver.activateApp(appPackage); + } catch (Exception e) { + handleDes.setE(e); + } + } + + public void rotateDevice(HandleDes handleDes, String text) { + try { + String s = ""; + switch (text) { + case "screenSub": + s = "sub"; + handleDes.setStepDes("左转屏幕"); + break; + case "screenAdd": + s = "add"; + handleDes.setStepDes("右转屏幕"); + break; + case "screenAbort": + s = "abort"; + handleDes.setStepDes("关闭自动旋转"); + break; + } + AndroidDeviceBridgeTool.screen(AndroidDeviceBridgeTool.getIDeviceByUdId(udId), s); + } catch (Exception e) { + handleDes.setE(e); + } + } + + public void lock(HandleDes handleDes) { + handleDes.setStepDes("锁定屏幕"); + try { + androidDriver.lockDevice(); + } catch (Exception e) { + handleDes.setE(e); + } + } + + public void unLock(HandleDes handleDes) { + handleDes.setStepDes("解锁屏幕"); + try { + androidDriver.unlockDevice(); + } catch (Exception e) { + handleDes.setE(e); + } + } + + public void airPlaneMode(HandleDes handleDes) { + handleDes.setStepDes("切换飞行模式"); + try { + androidDriver.toggleAirplaneMode(); + } catch (Exception e) { + handleDes.setE(e); + } + } + + public void wifiMode(HandleDes handleDes) { + handleDes.setStepDes("打开WIFI网络"); + try { + if (!androidDriver.getConnection().isWiFiEnabled()) { + androidDriver.toggleWifi(); + } + } catch (Exception e) { + handleDes.setE(e); + } + } + + public void locationMode(HandleDes handleDes) { + handleDes.setStepDes("切换位置服务"); + try { + androidDriver.toggleLocationServices(); + } catch (Exception e) { + handleDes.setE(e); + } + } + + public void asserts(HandleDes handleDes, String actual, String expect, String type) { + handleDes.setDetail("真实值: " + actual + " 期望值: " + expect); + try { + switch (type) { + case "assertEquals": + handleDes.setStepDes("断言验证(相等)"); + assertEquals(actual, expect); + break; + case "assertTrue": + handleDes.setStepDes("断言验证(包含)"); + assertTrue(actual.contains(expect)); + break; + case "assertNotTrue": + handleDes.setStepDes("断言验证(不包含)"); + assertFalse(actual.contains(expect)); + break; + } + } catch (AssertionError e) { + handleDes.setE(e); + } + } + + public String getText(HandleDes handleDes, String des, String selector, String pathValue) { + String s = ""; + handleDes.setStepDes("获取" + des + "文本"); + handleDes.setDetail("获取" + selector + ":" + pathValue + "文本"); + try { + s = findEle(selector, pathValue).getText(); + log.sendStepLog(StepType.INFO, "", "文本获取结果: " + s); + } catch (Exception e) { + handleDes.setE(e); + } + return s; + } + + public void hideKey(HandleDes handleDes) { + handleDes.setStepDes("隐藏键盘"); + handleDes.setDetail("隐藏弹出键盘"); + try { + androidDriver.hideKeyboard(); + } catch (Exception e) { + handleDes.setE(e); + } + } + + public void toWebView(HandleDes handleDes, String webViewName) { + handleDes.setStepDes("切换到" + webViewName); + try { + androidDriver.context(webViewName); + } catch (Exception e) { + handleDes.setE(e); + } + } + + public void click(HandleDes handleDes, String des, String selector, String pathValue) { + handleDes.setStepDes("点击" + des); + handleDes.setDetail("点击" + selector + ": " + pathValue); + try { + findEle(selector, pathValue).click(); + } catch (Exception e) { + handleDes.setE(e); + } + } + + public void sendKeys(HandleDes handleDes, String des, String selector, String pathValue, String keys) { + if (keys.contains("{{random}}")) { + String random = (int) (Math.random() * 10 + Math.random() * 10 * 2) + 5 + ""; + keys = keys.replace("{{random}}", random); + } + if (keys.contains("{{timestamp}}")) { + String timeMillis = Calendar.getInstance().getTimeInMillis() + ""; + keys = keys.replace("{{timestamp}}", timeMillis); + } + keys = replaceTrans(keys); + handleDes.setStepDes("对" + des + "输入内容"); + handleDes.setDetail("对" + selector + ": " + pathValue + " 输入: " + keys); + try { + findEle(selector, pathValue).sendKeys(keys); + } catch (Exception e) { + handleDes.setE(e); + } + } + + public void getTextAndAssert(HandleDes handleDes, String des, String selector, String pathValue, String expect) { + handleDes.setStepDes("获取" + des + "文本"); + handleDes.setDetail("获取" + selector + ":" + pathValue + "文本"); + try { + String s = findEle(selector, pathValue).getText(); + log.sendStepLog(StepType.INFO, "", "文本获取结果: " + s); + try { + expect = replaceTrans(expect); + assertEquals(s, expect); + log.sendStepLog(StepType.INFO, "验证文本", "真实值: " + s + " 期望值: " + expect); + } catch (AssertionError e) { + log.sendStepLog(StepType.ERROR, "验证" + des + "文本失败!", ""); + handleDes.setE(e); + } + } catch (Exception e) { + handleDes.setE(e); + } + } + + public void longPressPoint(HandleDes handleDes, String des, String xy, int time) { + int x = Integer.parseInt(xy.substring(0, xy.indexOf(","))); + int y = Integer.parseInt(xy.substring(xy.indexOf(",") + 1)); + handleDes.setStepDes("长按" + des); + handleDes.setDetail("长按坐标" + time + "毫秒 (" + x + "," + y + ")"); + try { + TouchAction ta = new TouchAction(androidDriver); + ta.longPress(PointOption.point(x, y)).waitAction(WaitOptions.waitOptions(Duration.ofMillis(time))).release().perform(); + } catch (Exception e) { + handleDes.setE(e); + } + } + + public void keyCode(HandleDes handleDes, String key) { + handleDes.setStepDes("按系统按键" + key + "键"); + try { + androidDriver.pressKey(new KeyEvent().withKey(AndroidKey.valueOf(key))); + } catch (Exception e) { + handleDes.setE(e); + } + } + + public void multiAction(HandleDes handleDes, String des1, String xy1, String des2, String xy2, String des3, String xy3, String des4, String xy4) { + int x1 = Integer.parseInt(xy1.substring(0, xy1.indexOf(","))); + 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)); + int x3 = Integer.parseInt(xy3.substring(0, xy3.indexOf(","))); + int y3 = Integer.parseInt(xy3.substring(xy3.indexOf(",") + 1)); + int x4 = Integer.parseInt(xy4.substring(0, xy4.indexOf(","))); + int y4 = Integer.parseInt(xy4.substring(xy4.indexOf(",") + 1)); + String detail = "坐标" + des1 + "( " + x1 + ", " + y1 + " )移动到坐标" + des2 + "( " + x2 + ", " + y2 + " ),同时坐标" + des3 + "( " + x3 + ", " + y3 + " )移动到坐标" + des4 + "( " + x4 + ", " + y4 + " )"; + handleDes.setStepDes("双指操作"); + handleDes.setDetail(detail); + try { + TouchAction hand1 = new TouchAction(androidDriver); + TouchAction hand2 = new TouchAction(androidDriver); + MultiTouchAction multiTouchAction = new MultiTouchAction(androidDriver); + hand1.press(PointOption.point(x1, y1)).moveTo(PointOption.point(x2, y2)).release(); + hand2.press(PointOption.point(x3, y3)).moveTo(PointOption.point(x4, y4)).release(); + multiTouchAction.add(hand1); + multiTouchAction.add(hand2); + multiTouchAction.perform(); + } catch (Exception e) { + handleDes.setE(e); + } + } + + public void tap(HandleDes handleDes, String des, String xy) { + int x = Integer.parseInt(xy.substring(0, xy.indexOf(","))); + int y = Integer.parseInt(xy.substring(xy.indexOf(",") + 1)); + handleDes.setStepDes("点击" + des); + handleDes.setDetail("点击坐标(" + x + "," + y + ")"); + try { + TouchAction ta = new TouchAction(androidDriver); + ta.tap(PointOption.point(x, y)).perform(); + } catch (Exception e) { + handleDes.setE(e); + } + } + + public void swipe(HandleDes handleDes, String des1, String xy1, String des2, String xy2) { + int x1 = Integer.parseInt(xy1.substring(0, xy1.indexOf(","))); + 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)); + handleDes.setStepDes("滑动拖拽" + des1 + "到" + des2); + handleDes.setDetail("拖动坐标(" + x1 + "," + y1 + ")到(" + x2 + "," + y2 + ")"); + try { + TouchAction ta = new TouchAction(androidDriver); + ta.press(PointOption.point(x1, y1)).waitAction(WaitOptions.waitOptions(Duration.ofMillis(300))).moveTo(PointOption.point(x2, y2)).release().perform(); + } catch (Exception e) { + handleDes.setE(e); + } + } + + public void longPress(HandleDes handleDes, String des, String selector, String pathValue, int time) { + handleDes.setStepDes("长按" + des); + handleDes.setDetail("长按控件元素" + time + "毫秒 "); + try { + TouchAction ta = new TouchAction(androidDriver); + WebElement webElement = findEle(selector, pathValue); + int x = webElement.getLocation().getX(); + int y = webElement.getLocation().getY(); + Duration duration = Duration.ofMillis(time); + ta.longPress(PointOption.point(x, y)).waitAction(WaitOptions.waitOptions(duration)).release().perform(); + } catch (Exception e) { + handleDes.setE(e); + } + } + + public void clear(HandleDes handleDes, String des, String selector, String pathValue) { + handleDes.setStepDes("清空" + des); + handleDes.setDetail("清空" + selector + ": " + pathValue); + try { + findEle(selector, pathValue).clear(); + } catch (Exception e) { + handleDes.setE(e); + } + } + + public void getTitle(HandleDes handleDes, String expect) { + String title = androidDriver.getTitle(); + handleDes.setStepDes("验证网页标题"); + handleDes.setDetail("标题:" + title + ",期望值:" + expect); + try { + assertEquals(title, expect); + } catch (AssertionError e) { + handleDes.setE(e); + } + } + + public void clickByImg(HandleDes handleDes, String des, String pathValue) throws Exception { + handleDes.setStepDes("点击图片" + des); + handleDes.setDetail(pathValue); + File file = null; + if (pathValue.startsWith("http")) { + try { + file = DownImageTool.download(pathValue); + } catch (Exception e) { + handleDes.setE(e); + return; + } + } + File localCap = getScreenToLocal(); + FindResult findResult; + FileSystemResource resource1 = new FileSystemResource(file); + FileSystemResource resource2 = new FileSystemResource(localCap); + MultiValueMap param = new LinkedMultiValueMap<>(); + param.add("file1", resource1); + param.add("file2", resource2); + param.add("type", "finder"); + try { + ResponseEntity responseEntity = + restTemplate.postForEntity(baseUrl + "/upload/cv", param, JSONObject.class); + if (responseEntity.getBody().getInteger("code") == 2000) { + findResult = responseEntity.getBody().getJSONObject("data").toJavaObject(FindResult.class); + if (findResult != null) { + try { + log.sendStepLog(StepType.INFO, "图片定位到坐标:(" + findResult.getX() + "," + findResult.getY() + ") 耗时:" + findResult.getTime() + " ms", + findResult.getUrl()); + TouchAction ta = new TouchAction(androidDriver); + ta.tap(PointOption.point(findResult.getX(), findResult.getY())).perform(); + } catch (Exception e) { + log.sendStepLog(StepType.ERROR, "点击" + des + "失败!", ""); + handleDes.setE(e); + } + } + } else if (responseEntity.getBody().getInteger("code") == 4003) { + handleDes.setE(new Exception("图像匹配失败!")); + } else { + handleDes.setE(new Exception("点击失败!cv服务出错!")); + } + } catch (Exception e) { + handleDes.setE(new Exception("点击失败!cv服务访问出错!")); + } finally { + file.delete(); + localCap.delete(); + } + } + + public void readText(HandleDes handleDes, String language, String text) throws Exception { +// TextReader textReader = new TextReader(); +// String result = textReader.getTessResult(getScreenToLocal(), language); +// log.sendStepLog(StepType.INFO, "", +// "图像文字识别结果:
" + result); +// String filter = result.replaceAll(" ", ""); + handleDes.setStepDes("图像文字识别"); + handleDes.setDetail("(该功能暂时关闭)期望包含文本:" + text); +// if (!filter.contains(text)) { +// handleDes.setE(new Exception("图像文字识别不通过!")); +// } + } + + public void toHandle(HandleDes handleDes, String titleName) throws Exception { + handleDes.setStepDes("切换Handle"); + Thread.sleep(1000); + Set handle = androidDriver.getWindowHandles();//获取handles + String ha; + for (int i = 1; i <= handle.size(); i++) { + ha = (String) handle.toArray()[handle.size() - i];//查找handle + try { + androidDriver.switchTo().window(ha);//切换到最后一个handle + } catch (Exception e) { + } + if (androidDriver.getTitle().equals(titleName)) { + handleDes.setDetail("切换到Handle:" + ha); + log.sendStepLog(StepType.INFO, "页面标题:" + androidDriver.getTitle(), ""); + break; + } + } + } + + public File getScreenToLocal() { + File file = ((TakesScreenshot) androidDriver).getScreenshotAs(OutputType.FILE); + File resultFile = new File("test-output/" + log.udId + Calendar.getInstance().getTimeInMillis() + ".jpg"); + try { + FileCopyUtils.copy(file, resultFile); + } catch (IOException e) { + e.printStackTrace(); + } + return resultFile; + } + + public String replaceTrans(String text) { + if (text.contains("{{") && text.contains("}}")) { + String tail = text.substring(text.indexOf("{{") + 2); + if (tail.contains("}}")) { + String child = tail.substring(tail.indexOf("}}") + 2); + String middle = tail.substring(0, tail.indexOf("}}")); + text = text.substring(0, text.indexOf("}}") + 2); + if (globalParams.getString(middle) != null) { + text = text.replace("{{" + middle + "}}", globalParams.getString(middle)); + } + text = text + replaceTrans(child); + } + } + return text; + } + + public void checkImage(HandleDes handleDes, String des, String pathValue, double matchThreshold) throws Exception { + log.sendStepLog(StepType.INFO, "开始检测" + des + "兼容", "检测与当前设备截图相似度,期望相似度为" + matchThreshold + "%"); + File file = null; + if (pathValue.startsWith("http")) { + file = DownImageTool.download(pathValue); + } + File localCap = getScreenToLocal(); + FileSystemResource resource1 = new FileSystemResource(file); + FileSystemResource resource2 = new FileSystemResource(localCap); + MultiValueMap param = new LinkedMultiValueMap<>(); + param.add("file1", resource1); + param.add("file2", resource2); + param.add("type", "checker"); + try { + ResponseEntity responseEntity = + restTemplate.postForEntity(baseUrl + "/upload/cv", param, JSONObject.class); + if (responseEntity.getBody().getInteger("code") == 2000) { + double score = responseEntity.getBody().getDouble("data"); + handleDes.setStepDes("检测" + des + "图片相似度"); + handleDes.setDetail("相似度为" + score * 100 + "%"); + if (score == 0) { + handleDes.setE(new Exception("图片相似度检测不通过!比对图片分辨率不一致!")); + } else if (score < (matchThreshold / 100)) { + handleDes.setE(new Exception("图片相似度检测不通过!expect " + matchThreshold + " but " + score * 100)); + } + } else { + handleDes.setE(new Exception("图片相似度检测出错!cv服务出错!")); + } + } catch (Exception e) { + e.printStackTrace(); + handleDes.setE(new Exception("图片相似度检测出错!cv服务访问出错!")); + } finally { + file.delete(); + localCap.delete(); + } + } + + public void exceptionLog(Throwable e) { + log.sendStepLog(StepType.WARN, "", "异常信息: " + e.fillInStackTrace().toString()); + } + + public void errorScreen() { + try { + androidDriver.context("NATIVE_APP");//先切换回app + log.sendStepLog(StepType.WARN, "获取异常截图", UploadTools + .upload(((TakesScreenshot) androidDriver).getScreenshotAs(OutputType.FILE), "imageFiles")); + } catch (Exception e) { + log.sendStepLog(StepType.ERROR, "捕获截图失败", ""); + } + } + + public String stepScreen(HandleDes handleDes) { + handleDes.setStepDes("获取截图"); + String url = ""; + try { + androidDriver.context("NATIVE_APP");//先切换回app + url = UploadTools.upload(((TakesScreenshot) androidDriver) + .getScreenshotAs(OutputType.FILE), "imageFiles"); + handleDes.setDetail(url); + } catch (Exception e) { + handleDes.setE(e); + } + return url; + } + + public Set getWebView() { + Set contextNames = androidDriver.getContextHandles(); + return contextNames; + } + + public String getCurrentActivity() { + return androidDriver.currentActivity(); + } + + public void pause(HandleDes handleDes, int time) { + handleDes.setStepDes("强制等待"); + handleDes.setDetail("等待" + time + " ms"); + try { + Thread.sleep(time); + } catch (InterruptedException e) { + handleDes.setE(e); + } + } + + public void runMonkey(HandleDes handleDes, JSONObject content, List text) { + handleDes.setStepDes("运行随机事件测试完毕"); + String packageName = content.getString("packageName"); + int pctNum = content.getInteger("pctNum"); + if (!androidDriver.isAppInstalled(packageName)) { + log.sendStepLog(StepType.ERROR, "应用未安装!", "设备未安装 " + packageName); + handleDes.setE(new Exception("未安装应用")); + return; + } + IDevice iDevice = AndroidDeviceBridgeTool.getIDeviceByUdId(udId); + JSONArray options = content.getJSONArray("options"); + int width = androidDriver.manage().window().getSize().width; + int height = androidDriver.manage().window().getSize().height; + int sleepTime = 50; + int systemEvent = 0; + int tapEvent = 0; + int longPressEvent = 0; + int swipeEvent = 0; + int zoomEvent = 0; + int navEvent = 0; + boolean isOpenH5Listener = false; + boolean isOpenPackageListener = false; + boolean isOpenActivityListener = false; + boolean isOpenNetworkListener = false; + if (!options.isEmpty()) { + for (int i = options.size() - 1; i >= 0; i--) { + JSONObject jsonOption = (JSONObject) options.get(i); + if (jsonOption.getString("name").equals("sleepTime")) { + sleepTime = jsonOption.getInteger("value"); + } + if (jsonOption.getString("name").equals("systemEvent")) { + systemEvent = jsonOption.getInteger("value"); + } + if (jsonOption.getString("name").equals("tapEvent")) { + tapEvent = jsonOption.getInteger("value"); + } + if (jsonOption.getString("name").equals("longPressEvent")) { + longPressEvent = jsonOption.getInteger("value"); + } + if (jsonOption.getString("name").equals("swipeEvent")) { + swipeEvent = jsonOption.getInteger("value"); + } + if (jsonOption.getString("name").equals("zoomEvent")) { + zoomEvent = jsonOption.getInteger("value"); + } + if (jsonOption.getString("name").equals("navEvent")) { + navEvent = jsonOption.getInteger("value"); + } + if (jsonOption.getString("name").equals("isOpenH5Listener")) { + isOpenH5Listener = jsonOption.getBoolean("value"); + } + if (jsonOption.getString("name").equals("isOpenPackageListener")) { + isOpenPackageListener = jsonOption.getBoolean("value"); + } + if (jsonOption.getString("name").equals("isOpenActivityListener")) { + isOpenActivityListener = jsonOption.getBoolean("value"); + } + if (jsonOption.getString("name").equals("isOpenNetworkListener")) { + isOpenNetworkListener = jsonOption.getBoolean("value"); + } + options.remove(options.get(i)); + } + } + int finalSleepTime = sleepTime; + int finalTapEvent = tapEvent; + int finalLongPressEvent = longPressEvent; + int finalSwipeEvent = swipeEvent; + int finalZoomEvent = zoomEvent; + int finalSystemEvent = systemEvent; + int finalNavEvent = navEvent; + Future randomThread = AndroidDeviceThreadPool.cachedThreadPool.submit(() -> { + log.sendStepLog(StepType.INFO, "", "随机事件数:" + pctNum + + "
目标应用:" + packageName + + "
用户操作时延:" + finalSleepTime + " ms" + + "
轻触事件权重:" + finalTapEvent + + "
长按事件权重:" + finalLongPressEvent + + "
滑动事件权重:" + finalSwipeEvent + + "
多点触控事件权重:" + finalZoomEvent + + "
物理按键事件权重:" + finalSystemEvent + + "
系统事件权重:" + finalNavEvent + ); + openApp(new HandleDes(), packageName); + TouchAction ta = new TouchAction(androidDriver); + TouchAction ta2 = new TouchAction(androidDriver); + MultiTouchAction multiTouchAction = new MultiTouchAction(androidDriver); + int totalCount = finalSystemEvent + finalTapEvent + finalLongPressEvent + finalSwipeEvent + finalZoomEvent + finalNavEvent; + for (int i = 0; i < pctNum; i++) { + try { + int random = new Random().nextInt(totalCount); + if (random >= 0 && random < finalSystemEvent) { + int key = new Random().nextInt(9); + String keyType = ""; + switch (key) { + case 0: + keyType = "HOME"; + break; + case 1: + keyType = "BACK"; + break; + case 2: + keyType = "MENU"; + break; + case 3: + keyType = "APP_SWITCH"; + break; + case 4: + keyType = "BRIGHTNESS_DOWN"; + break; + case 5: + keyType = "BRIGHTNESS_UP"; + break; + case 6: + keyType = "VOLUME_UP"; + break; + case 7: + keyType = "VOLUME_DOWN"; + break; + case 8: + keyType = "VOLUME_MUTE"; + break; + } + androidDriver.pressKey(new KeyEvent(AndroidKey.valueOf(keyType))); + } + if (random >= finalSystemEvent && random < (finalSystemEvent + finalTapEvent)) { + int x = new Random().nextInt(width - 60) + 60; + int y = new Random().nextInt(height - 60) + 60; + ta.tap(PointOption.point(x, y)).perform(); + } + if (random >= (finalSystemEvent + finalTapEvent) && random < (finalSystemEvent + finalTapEvent + finalLongPressEvent)) { + int x = new Random().nextInt(width - 60) + 60; + int y = new Random().nextInt(height - 60) + 60; + ta.longPress(PointOption.point(x, y)).waitAction(WaitOptions.waitOptions(Duration.ofSeconds(new Random().nextInt(3) + 1))).release().perform(); + } + if (random >= (finalSystemEvent + finalTapEvent + finalLongPressEvent) && random < (finalSystemEvent + finalTapEvent + finalLongPressEvent + finalSwipeEvent)) { + int x1 = new Random().nextInt(width - 60) + 60; + int y1 = new Random().nextInt(height - 80) + 80; + int x2 = new Random().nextInt(width - 60) + 60; + int y2 = new Random().nextInt(height - 80) + 80; + ta.press(PointOption.point(x1, y1)).waitAction(WaitOptions.waitOptions(Duration.ofMillis(200))).moveTo(PointOption.point(x2, y2)).release().perform(); + } + if (random >= (finalSystemEvent + finalTapEvent + finalLongPressEvent + finalSwipeEvent) && random < (finalSystemEvent + finalTapEvent + finalLongPressEvent + finalSwipeEvent + finalZoomEvent)) { + int x1 = new Random().nextInt(width - 80); + int y1 = new Random().nextInt(height - 80); + int x2 = new Random().nextInt(width - 100); + int y2 = new Random().nextInt(height - 80); + int x3 = new Random().nextInt(width - 100); + int y3 = new Random().nextInt(height - 80); + int x4 = new Random().nextInt(width - 100); + int y4 = new Random().nextInt(height - 80); + ta.press(PointOption.point(x1, y1)).waitAction(WaitOptions.waitOptions(Duration.ofMillis(200))).moveTo(PointOption.point(x2, y2)).release(); + ta2.press(PointOption.point(x3, y3)).waitAction(WaitOptions.waitOptions(Duration.ofMillis(200))).moveTo(PointOption.point(x4, y4)).release(); + multiTouchAction.add(ta); + multiTouchAction.add(ta2); + multiTouchAction.perform(); + } + if (random >= (finalSystemEvent + finalTapEvent + finalLongPressEvent + finalSwipeEvent + finalZoomEvent) && random < (finalSystemEvent + finalTapEvent + finalLongPressEvent + finalSwipeEvent + finalZoomEvent + finalNavEvent)) { + androidDriver.toggleWifi(); + } + Thread.sleep(finalSleepTime); + } catch (Throwable e) { + e.printStackTrace(); + } + } + } + ); + Boolean finalIsOpenH5Listener = isOpenH5Listener; + Future H5Listener = AndroidDeviceThreadPool.cachedThreadPool.submit(() -> { + if (finalIsOpenH5Listener) { + int h5Time = 0; + while (!randomThread.isDone()) { + try { + Thread.sleep(8000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + try { + if (androidDriver.findElementsByClassName("android.webkit.WebView").size() > 0) { + h5Time++; + AndroidDeviceBridgeTool.executeCommand(iDevice, "input keyevent 4"); + } else { + h5Time = 0; + } + if (h5Time >= 12) { + androidDriver.terminateApp(packageName, new AndroidTerminateApplicationOptions().withTimeout(Duration.ofMillis(1000))); + h5Time = 0; + } + } catch (Throwable e) { + e.printStackTrace(); + } + } + } + } + ); + boolean finalIsOpenPackageListener = isOpenPackageListener; + Future packageListener = AndroidDeviceThreadPool.cachedThreadPool.submit(() -> { + if (finalIsOpenPackageListener) { + while (!randomThread.isDone()) { + int waitTime = 0; + while (waitTime <= 10 && (!randomThread.isDone())) { + try { + Thread.sleep(5000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + if (!androidDriver.getCurrentPackage().equals(packageName)) { + androidDriver.activateApp(packageName); + } + waitTime++; + } + androidDriver.activateApp(packageName); + } + } + } + ); + boolean finalIsOpenActivityListener = isOpenActivityListener; + Future activityListener = AndroidDeviceThreadPool.cachedThreadPool.submit(() -> { + if (finalIsOpenActivityListener) { + if (text.isEmpty()) { + return; + } + Set blackList = new HashSet<>(); + for (JSONObject activities : text) { + blackList.add(activities.getString("name")); + } + while (!randomThread.isDone()) { + try { + Thread.sleep(8000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + if (blackList.contains(getCurrentActivity())) { + AndroidDeviceBridgeTool.executeCommand(iDevice, "input keyevent 4"); + } else continue; + try { + Thread.sleep(8000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + if (blackList.contains(getCurrentActivity())) { + androidDriver.terminateApp(packageName, new AndroidTerminateApplicationOptions().withTimeout(Duration.ofMillis(1000))); + } + } + } + } + ); + boolean finalIsOpenNetworkListener = isOpenNetworkListener; + Future networkListener = AndroidDeviceThreadPool.cachedThreadPool.submit(() -> { + if (finalIsOpenNetworkListener) { + while (!randomThread.isDone()) { + try { + Thread.sleep(8000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + if (androidDriver.getConnection().isAirplaneModeEnabled()) { + androidDriver.toggleAirplaneMode(); + } + try { + Thread.sleep(8000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + if (!androidDriver.getConnection().isWiFiEnabled()) { + androidDriver.toggleWifi(); + } + } + } + } + ); + try { + Thread.sleep(500); + } catch (InterruptedException e) { + e.printStackTrace(); + } +// if (version.length() == 0) { +// version = AndroidDeviceBridgeTool.getAppOnlyVersion(udId, packageName); +// } + log.sendStepLog(StepType.INFO, "", "测试目标包:" + packageName + + (isOpenPackageListener ? "
应用包名监听器已开启..." : "") + + (isOpenH5Listener ? "
H5页面监听器已开启..." : "") + + (isOpenActivityListener ? "
黑名单Activity监听器..." : "") + + (isOpenNetworkListener ? "
网络状态监听器已开启..." : "")); + while (!randomThread.isDone() || (!packageListener.isDone()) || (!activityListener.isDone()) || (!networkListener.isDone()) || (!H5Listener.isDone())) { + } + } + + public void publicStep(HandleDes handleDes, String name, JSONArray stepArray) { + handleDes.setStepDes("执行公共步骤 " + name); + log.sendStepLog(StepType.WARN, "公共步骤 " + name + " 开始执行", ""); + for (Object publicStep : stepArray) { + JSONObject stepDetail = (JSONObject) publicStep; + try { + runStep(stepDetail); + } catch (Throwable e) { + handleDes.setE(e); + break; + } + } + } + + public WebElement findEle(String selector, String pathValue) { + WebElement we = null; + switch (selector) { + case "id": + we = androidDriver.findElementById(pathValue); + break; + case "name": + we = androidDriver.findElementByName(pathValue); + break; + case "xpath": + we = androidDriver.findElementByXPath(pathValue); + break; + case "cssSelector": + we = androidDriver.findElementByCssSelector(pathValue); + break; + case "className": + we = androidDriver.findElementByClassName(pathValue); + break; + case "tagName": + we = androidDriver.findElementByTagName(pathValue); + break; + case "linkText": + we = androidDriver.findElementByLinkText(pathValue); + break; + case "partialLinkText": + we = androidDriver.findElementByPartialLinkText(pathValue); + break; + default: + log.sendStepLog(StepType.ERROR, "查找控件元素失败", "这个控件元素类型: " + selector + " 不存在!!!"); + break; + } + return we; + } + + public void runStep(JSONObject stepJSON) throws Throwable { + JSONObject step = stepJSON.getJSONObject("step"); + JSONArray eleList = step.getJSONArray("elements"); + HandleDes handleDes = new HandleDes(); + switch (step.getString("stepType")) { + case "toWebView": + toWebView(handleDes, step.getString("content")); + break; + case "toHandle": + toHandle(handleDes, step.getString("content")); + break; + case "readText": + readText(handleDes, step.getString("content"), step.getString("text")); + break; + case "clickByImg": + clickByImg(handleDes, eleList.getJSONObject(0).getString("eleName") + , eleList.getJSONObject(0).getString("eleValue")); + break; + case "click": + click(handleDes, eleList.getJSONObject(0).getString("eleName"), eleList.getJSONObject(0).getString("eleType") + , eleList.getJSONObject(0).getString("eleValue")); + break; + case "getTitle": + getTitle(handleDes, step.getString("content")); + break; + case "sendKeys": + sendKeys(handleDes, eleList.getJSONObject(0).getString("eleName"), eleList.getJSONObject(0).getString("eleType") + , eleList.getJSONObject(0).getString("eleValue"), step.getString("content")); + break; + case "getText": + getTextAndAssert(handleDes, eleList.getJSONObject(0).getString("eleName"), eleList.getJSONObject(0).getString("eleType") + , eleList.getJSONObject(0).getString("eleValue"), step.getString("content")); + break; + case "clear": + clear(handleDes, eleList.getJSONObject(0).getString("eleName"), eleList.getJSONObject(0).getString("eleType") + , eleList.getJSONObject(0).getString("eleValue")); + break; + case "longPress": + longPress(handleDes, eleList.getJSONObject(0).getString("eleName"), eleList.getJSONObject(0).getString("eleType") + , eleList.getJSONObject(0).getString("eleValue"), Integer.parseInt(step.getString("content"))); + break; + case "swipe": + swipe(handleDes, eleList.getJSONObject(0).getString("eleName"), eleList.getJSONObject(0).getString("eleValue") + , eleList.getJSONObject(1).getString("eleName"), eleList.getJSONObject(1).getString("eleValue")); + break; + case "tap": + tap(handleDes, eleList.getJSONObject(0).getString("eleName"), eleList.getJSONObject(0).getString("eleValue")); + break; + case "longPressPoint": + longPressPoint(handleDes, eleList.getJSONObject(0).getString("eleName"), eleList.getJSONObject(0).getString("eleValue") + , Integer.parseInt(step.getString("content"))); + break; + case "pause": + pause(handleDes, Integer.parseInt(step.getString("content"))); + break; + case "checkImage": + checkImage(handleDes, eleList.getJSONObject(0).getString("eleName"), eleList.getJSONObject(0).getString("eleValue") + , step.getDouble("content")); + break; + case "stepScreen": + stepScreen(handleDes); + break; + case "openApp": + openApp(handleDes, step.getString("text")); + break; + case "terminate": + terminate(handleDes, step.getString("text")); + break; + case "install": + install(handleDes, step.getString("text")); + break; + case "uninstall": + uninstall(handleDes, step.getString("text")); + break; + case "runBack": + runBackground(handleDes, Long.parseLong(step.getString("content"))); + break; + case "screenSub": + case "screenAdd": + case "screenAbort": + rotateDevice(handleDes, step.getString("stepType")); + break; + case "lock": + lock(handleDes); + break; + case "unLock": + unLock(handleDes); + break; + case "airPlaneMode": + airPlaneMode(handleDes); + break; + case "wifiMode": + wifiMode(handleDes); + break; + case "locationMode": + locationMode(handleDes); + break; + case "zoom": + multiAction(handleDes, eleList.getJSONObject(0).getString("eleName"), eleList.getJSONObject(0).getString("eleValue") + , eleList.getJSONObject(1).getString("eleName"), eleList.getJSONObject(1).getString("eleValue") + , eleList.getJSONObject(2).getString("eleName"), eleList.getJSONObject(2).getString("eleValue") + , eleList.getJSONObject(3).getString("eleName"), eleList.getJSONObject(3).getString("eleValue")); + break; + case "keyCode": + keyCode(handleDes, step.getString("content")); + break; + case "assertEquals": + case "assertTrue": + case "assertNotTrue": + String actual = replaceTrans(step.getString("text")); + String expect = replaceTrans(step.getString("content")); + asserts(handleDes, actual, expect, step.getString("stepType")); + break; + case "getTextValue": + globalParams.put(step.getString("content"), getText(handleDes, eleList.getJSONObject(0).getString("eleName") + , eleList.getJSONObject(0).getString("eleType"), eleList.getJSONObject(0).getString("eleValue"))); + break; + case "hideKey": + hideKey(handleDes); + break; + case "monkey": + runMonkey(handleDes, step.getJSONObject("content"), step.getJSONArray("text").toJavaList(JSONObject.class)); + break; + case "publicStep": + publicStep(handleDes, step.getString("content"), stepJSON.getJSONArray("pubSteps")); + } + switchType(step.getInteger("error"), handleDes.getStepDes(), handleDes.getDetail(), handleDes.getE()); + } + + public void switchType(int error, String step, String detail, Throwable e) throws Throwable { + if (e != null) { + switch (error) { + case ErrorType.IGNORE: + log.sendStepLog(StepType.PASS, step + "异常!已忽略...", detail); + break; + case ErrorType.WARNING: + log.sendStepLog(StepType.WARN, step + "异常!", detail); + setResultDetailStatus(ResultDetailStatus.WARN); + errorScreen(); + exceptionLog(e); + break; + case ErrorType.SHUTDOWN: + log.sendStepLog(StepType.ERROR, step + "异常!", detail); + setResultDetailStatus(ResultDetailStatus.FAIL); + errorScreen(); + exceptionLog(e); + throw e; + } + } else { + log.sendStepLog(StepType.PASS, step, detail); + } + } +} diff --git a/src/main/java/com/sonic/agent/automation/AppiumServer.java b/src/main/java/org/cloud/sonic/agent/automation/AppiumServer.java similarity index 93% rename from src/main/java/com/sonic/agent/automation/AppiumServer.java rename to src/main/java/org/cloud/sonic/agent/automation/AppiumServer.java index 707e79f5..1485c654 100644 --- a/src/main/java/com/sonic/agent/automation/AppiumServer.java +++ b/src/main/java/org/cloud/sonic/agent/automation/AppiumServer.java @@ -1,33 +1,33 @@ -package com.sonic.agent.automation; - -import io.appium.java_client.service.local.AppiumDriverLocalService; -import io.appium.java_client.service.local.AppiumServiceBuilder; -import io.appium.java_client.service.local.flags.GeneralServerFlag; - -/** - * @author ZhouYiXun - * @des - * @date 2021/8/27 17:24 - */ -public class AppiumServer { - public static AppiumDriverLocalService service; - - /** - * @return void - * @author ZhouYiXun - * @des 启动appium服务 - * @date 2021/8/16 20:01 - */ - public static void start() { - service = AppiumDriverLocalService.buildService(new AppiumServiceBuilder().usingAnyFreePort() - .withArgument(GeneralServerFlag.LOG_LEVEL, "error") - .withArgument(GeneralServerFlag.ALLOW_INSECURE, "chromedriver_autodownload")); - service.start(); - } - - public static void close() { - if (service != null && service.isRunning()) { - service.stop(); - } - } -} +package org.cloud.sonic.agent.automation; + +import io.appium.java_client.service.local.AppiumDriverLocalService; +import io.appium.java_client.service.local.AppiumServiceBuilder; +import io.appium.java_client.service.local.flags.GeneralServerFlag; + +/** + * @author ZhouYiXun + * @des + * @date 2021/8/27 17:24 + */ +public class AppiumServer { + public static AppiumDriverLocalService service; + + /** + * @return void + * @author ZhouYiXun + * @des 启动appium服务 + * @date 2021/8/16 20:01 + */ + public static void start() { + service = AppiumDriverLocalService.buildService(new AppiumServiceBuilder().usingAnyFreePort() + .withArgument(GeneralServerFlag.LOG_LEVEL, "error") + .withArgument(GeneralServerFlag.ALLOW_INSECURE, "chromedriver_autodownload")); + service.start(); + } + + public static void close() { + if (service != null && service.isRunning()) { + service.stop(); + } + } +} diff --git a/src/main/java/com/sonic/agent/automation/FindResult.java b/src/main/java/org/cloud/sonic/agent/automation/FindResult.java similarity index 95% rename from src/main/java/com/sonic/agent/automation/FindResult.java rename to src/main/java/org/cloud/sonic/agent/automation/FindResult.java index db6877bf..70b6f938 100644 --- a/src/main/java/com/sonic/agent/automation/FindResult.java +++ b/src/main/java/org/cloud/sonic/agent/automation/FindResult.java @@ -1,4 +1,4 @@ -package com.sonic.agent.automation; +package org.cloud.sonic.agent.automation; public class FindResult { private int x; diff --git a/src/main/java/com/sonic/agent/automation/HandleDes.java b/src/main/java/org/cloud/sonic/agent/automation/HandleDes.java similarity index 91% rename from src/main/java/com/sonic/agent/automation/HandleDes.java rename to src/main/java/org/cloud/sonic/agent/automation/HandleDes.java index fb30e103..6932ddbd 100644 --- a/src/main/java/com/sonic/agent/automation/HandleDes.java +++ b/src/main/java/org/cloud/sonic/agent/automation/HandleDes.java @@ -1,51 +1,51 @@ -package com.sonic.agent.automation; - -/** - * @author ZhouYiXun - * @des - * @date 2021/8/26 23:46 - */ -public class HandleDes { - private String stepDes; - private String detail; - private Throwable e; - - public HandleDes(){ - this.stepDes = ""; - this.detail = ""; - this.e = null; - } - - public String getStepDes() { - return stepDes; - } - - public void setStepDes(String stepDes) { - this.stepDes = stepDes; - } - - public String getDetail() { - return detail; - } - - public void setDetail(String detail) { - this.detail = detail; - } - - public Throwable getE() { - return e; - } - - public void setE(Throwable e) { - this.e = e; - } - - @Override - public String toString() { - return "HandleDes{" + - "stepDes='" + stepDes + '\'' + - ", detail='" + detail + '\'' + - ", e=" + e + - '}'; - } -} +package org.cloud.sonic.agent.automation; + +/** + * @author ZhouYiXun + * @des + * @date 2021/8/26 23:46 + */ +public class HandleDes { + private String stepDes; + private String detail; + private Throwable e; + + public HandleDes(){ + this.stepDes = ""; + this.detail = ""; + this.e = null; + } + + public String getStepDes() { + return stepDes; + } + + public void setStepDes(String stepDes) { + this.stepDes = stepDes; + } + + public String getDetail() { + return detail; + } + + public void setDetail(String detail) { + this.detail = detail; + } + + public Throwable getE() { + return e; + } + + public void setE(Throwable e) { + this.e = e; + } + + @Override + public String toString() { + return "HandleDes{" + + "stepDes='" + stepDes + '\'' + + ", detail='" + detail + '\'' + + ", e=" + e + + '}'; + } +} diff --git a/src/main/java/com/sonic/agent/automation/IOSStepHandler.java b/src/main/java/org/cloud/sonic/agent/automation/IOSStepHandler.java similarity index 98% rename from src/main/java/com/sonic/agent/automation/IOSStepHandler.java rename to src/main/java/org/cloud/sonic/agent/automation/IOSStepHandler.java index e87d0d8e..e03831aa 100644 --- a/src/main/java/com/sonic/agent/automation/IOSStepHandler.java +++ b/src/main/java/org/cloud/sonic/agent/automation/IOSStepHandler.java @@ -1,25 +1,22 @@ -package com.sonic.agent.automation; +package org.cloud.sonic.agent.automation; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; -import com.sonic.agent.bridge.ios.TIDeviceTool; -import com.sonic.agent.interfaces.ErrorType; -import com.sonic.agent.interfaces.ResultDetailStatus; -import com.sonic.agent.interfaces.StepType; -import com.sonic.agent.maps.IOSProcessMap; -import com.sonic.agent.maps.IOSSizeMap; -import com.sonic.agent.tools.DownImageTool; -import com.sonic.agent.tools.LogTool; -import com.sonic.agent.tools.SpringTool; -import com.sonic.agent.tools.UploadTools; -import io.appium.java_client.MobileBy; +import org.cloud.sonic.agent.bridge.ios.TIDeviceTool; +import org.cloud.sonic.agent.interfaces.ErrorType; +import org.cloud.sonic.agent.interfaces.ResultDetailStatus; +import org.cloud.sonic.agent.interfaces.StepType; +import org.cloud.sonic.agent.maps.IOSProcessMap; +import org.cloud.sonic.agent.maps.IOSSizeMap; +import org.cloud.sonic.agent.tools.DownImageTool; +import org.cloud.sonic.agent.tools.LogTool; +import org.cloud.sonic.agent.tools.SpringTool; +import org.cloud.sonic.agent.tools.UploadTools; import io.appium.java_client.MultiTouchAction; import io.appium.java_client.Setting; import io.appium.java_client.TouchAction; import io.appium.java_client.android.appmanagement.AndroidTerminateApplicationOptions; -import io.appium.java_client.android.nativekey.AndroidKey; -import io.appium.java_client.android.nativekey.KeyEvent; import io.appium.java_client.appmanagement.BaseInstallApplicationOptions; import io.appium.java_client.appmanagement.BaseTerminateApplicationOptions; import io.appium.java_client.ios.IOSDriver; @@ -37,7 +34,6 @@ import org.openqa.selenium.TakesScreenshot; import org.openqa.selenium.WebElement; import org.openqa.selenium.remote.DesiredCapabilities; -import org.openqa.selenium.support.ui.ExpectedConditions; import org.springframework.core.env.Environment; import org.springframework.core.io.FileSystemResource; import org.springframework.http.ResponseEntity; diff --git a/src/main/java/com/sonic/agent/automation/PackageHandler.java b/src/main/java/org/cloud/sonic/agent/automation/PackageHandler.java similarity index 67% rename from src/main/java/com/sonic/agent/automation/PackageHandler.java rename to src/main/java/org/cloud/sonic/agent/automation/PackageHandler.java index d41dcd1e..943aef8b 100644 --- a/src/main/java/com/sonic/agent/automation/PackageHandler.java +++ b/src/main/java/org/cloud/sonic/agent/automation/PackageHandler.java @@ -1,9 +1,9 @@ -package com.sonic.agent.automation; - -/** - * @author ZhouYiXun - * @des - * @date 2021/8/26 22:32 - */ -public class PackageHandler { -} +package org.cloud.sonic.agent.automation; + +/** + * @author ZhouYiXun + * @des + * @date 2021/8/26 22:32 + */ +public class PackageHandler { +} diff --git a/src/main/java/com/sonic/agent/automation/RemoteDebugDriver.java b/src/main/java/org/cloud/sonic/agent/automation/RemoteDebugDriver.java similarity index 95% rename from src/main/java/com/sonic/agent/automation/RemoteDebugDriver.java rename to src/main/java/org/cloud/sonic/agent/automation/RemoteDebugDriver.java index 56ac8fe8..201802d7 100644 --- a/src/main/java/com/sonic/agent/automation/RemoteDebugDriver.java +++ b/src/main/java/org/cloud/sonic/agent/automation/RemoteDebugDriver.java @@ -1,78 +1,78 @@ -package com.sonic.agent.automation; - -import com.sonic.agent.tools.PortTool; -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.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.context.ApplicationListener; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.event.ContextRefreshedEvent; -import org.springframework.lang.NonNull; - -/** - * @author ZhouYiXun - * @des - * @date 2021/10/29 0:28 - */ -@ConditionalOnProperty(value = "modules.webview.enable", havingValue = "true") -@Configuration -public class RemoteDebugDriver implements ApplicationListener { - private static final Logger logger = LoggerFactory.getLogger(RemoteDebugDriver.class); - private static String chromePath; - public static int chromePort; - public static WebDriver webDriver; - @Value("${modules.webview.chrome-driver-path}") - private String path; - @Value("${modules.webview.chrome-driver-debug-port}") - private int port; - - @Bean - public void setChromePath() { - chromePath = path; - chromePort = port; - } - - @Override - public void onApplicationEvent(@NonNull ContextRefreshedEvent event) { - startChromeDriver(); - } - - public static void startChromeDriver() { - logger.info("开启webview相关功能"); - System.setProperty("webdriver.chrome.silentOutput", "true"); - try { - DesiredCapabilities desiredCapabilities = new DesiredCapabilities(); - ChromeOptions chromeOptions = new ChromeOptions(); - System.setProperty("webdriver.chrome.driver", chromePath); - if (chromePort == 0) { - int debugPort = PortTool.getPort(); - chromePort = debugPort; - chromeOptions.addArguments("--remote-debugging-port=" + debugPort); - } else { - chromeOptions.addArguments("--remote-debugging-port=" + chromePort); - } - chromeOptions.addArguments("--remote-debugging-address=0.0.0.0"); - 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); - logger.info("chromeDriver启动完毕!"); - } catch (Exception e) { - logger.info("chromeDriver启动失败!"); - } - } - - public static void close() { - if (webDriver != null) { - webDriver.quit(); - } - } -} +package org.cloud.sonic.agent.automation; + +import org.cloud.sonic.agent.tools.PortTool; +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.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.ApplicationListener; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.event.ContextRefreshedEvent; +import org.springframework.lang.NonNull; + +/** + * @author ZhouYiXun + * @des + * @date 2021/10/29 0:28 + */ +@ConditionalOnProperty(value = "modules.webview.enable", havingValue = "true") +@Configuration +public class RemoteDebugDriver implements ApplicationListener { + private static final Logger logger = LoggerFactory.getLogger(RemoteDebugDriver.class); + private static String chromePath; + public static int chromePort; + public static WebDriver webDriver; + @Value("${modules.webview.chrome-driver-path}") + private String path; + @Value("${modules.webview.chrome-driver-debug-port}") + private int port; + + @Bean + public void setChromePath() { + chromePath = path; + chromePort = port; + } + + @Override + public void onApplicationEvent(@NonNull ContextRefreshedEvent event) { + startChromeDriver(); + } + + public static void startChromeDriver() { + logger.info("开启webview相关功能"); + System.setProperty("webdriver.chrome.silentOutput", "true"); + try { + DesiredCapabilities desiredCapabilities = new DesiredCapabilities(); + ChromeOptions chromeOptions = new ChromeOptions(); + System.setProperty("webdriver.chrome.driver", chromePath); + if (chromePort == 0) { + int debugPort = PortTool.getPort(); + chromePort = debugPort; + chromeOptions.addArguments("--remote-debugging-port=" + debugPort); + } else { + chromeOptions.addArguments("--remote-debugging-port=" + chromePort); + } + chromeOptions.addArguments("--remote-debugging-address=0.0.0.0"); + 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); + logger.info("chromeDriver启动完毕!"); + } catch (Exception e) { + logger.info("chromeDriver启动失败!"); + } + } + + public static void close() { + if (webDriver != null) { + webDriver.quit(); + } + } +} diff --git a/src/main/java/com/sonic/agent/bridge/android/AndroidDeviceBridgeTool.java b/src/main/java/org/cloud/sonic/agent/bridge/android/AndroidDeviceBridgeTool.java similarity index 96% rename from src/main/java/com/sonic/agent/bridge/android/AndroidDeviceBridgeTool.java rename to src/main/java/org/cloud/sonic/agent/bridge/android/AndroidDeviceBridgeTool.java index 7f1e245b..1eca98db 100644 --- a/src/main/java/com/sonic/agent/bridge/android/AndroidDeviceBridgeTool.java +++ b/src/main/java/org/cloud/sonic/agent/bridge/android/AndroidDeviceBridgeTool.java @@ -1,406 +1,406 @@ -package com.sonic.agent.bridge.android; - -import com.android.ddmlib.*; -import com.sonic.agent.tests.android.AndroidTemperThread; -import com.sonic.agent.tools.DownImageTool; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.context.ApplicationListener; -import org.springframework.context.annotation.DependsOn; -import org.springframework.context.event.ContextRefreshedEvent; -import org.springframework.lang.NonNull; -import org.springframework.stereotype.Component; - -import java.io.File; -import java.io.IOException; -import java.util.concurrent.TimeUnit; - -/** - * @author ZhouYiXun - * @des ADB工具类 - * @date 2021/08/16 19:26 - */ -@ConditionalOnProperty(value = "modules.android.enable", havingValue = "true") -@DependsOn({"androidThreadPoolInit", "nettyMsgInit"}) -@Component -public class AndroidDeviceBridgeTool implements ApplicationListener { - private static final Logger logger = LoggerFactory.getLogger(AndroidDeviceBridgeTool.class); - public static AndroidDebugBridge androidDebugBridge = null; - private AndroidTemperThread androidTemperThread = null; - - @Autowired - private AndroidDeviceStatusListener androidDeviceStatusListener; - - - @Override - public void onApplicationEvent(@NonNull ContextRefreshedEvent event) { - logger.info("开启安卓相关功能"); - } - - /** - * @return java.lang.String - * @author ZhouYiXun - * @des 获取系统安卓SDK路径 - * @date 2021/8/16 19:35 - */ - private static String getADBPathFromSystemEnv() { - String path = System.getenv("ANDROID_HOME"); - if (path != null) { - path += File.separator + "platform-tools" + File.separator + "adb"; - } else { - logger.error("获取ANDROID_HOME环境变量失败!"); - return null; - } - return path; - } - - /** - * @return void - * @author ZhouYiXun - * @des 定义方法 - * @date 2021/8/16 19:36 - */ - public void init() { - //获取系统SDK路径 - String systemADBPath = getADBPathFromSystemEnv(); - //添加设备上下线监听 - androidDebugBridge.addDeviceChangeListener(androidDeviceStatusListener); - try { - AndroidDebugBridge.init(false); - } catch (IllegalStateException e) { - logger.warn("AndroidDebugBridge已经初始化过,无需再初始化"); - } - //开始创建ADB - androidDebugBridge = AndroidDebugBridge.createBridge(systemADBPath, true, Long.MAX_VALUE, TimeUnit.MILLISECONDS); - if (androidDebugBridge != null) { - logger.info("安卓设备监听已开启"); - } - int count = 0; - //获取设备列表,超时后退出 - while (androidDebugBridge.hasInitialDeviceList() == false) { - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - count++; - if (count > 200) { - break; - } - } - if (androidTemperThread == null || !androidTemperThread.isAlive()) { - androidTemperThread = new AndroidTemperThread(); - androidTemperThread.start(); - } - } - - /** - * @return com.android.ddmlib.IDevice[] - * @author ZhouYiXun - * @des 获取真实在线设备列表 - * @date 2021/8/16 19:38 - */ - public static IDevice[] getRealOnLineDevices() { - if (androidDebugBridge != null) { - return androidDebugBridge.getDevices(); - } else { - return null; - } - } - - /** - * @param iDevice - * @return void - * @author ZhouYiXun - * @des 重启设备 - * @date 2021/8/16 19:41 - */ - public static void reboot(IDevice iDevice) { - if (iDevice != null) { - executeCommand(iDevice, "reboot"); - } - } - - /** - * @param udId - * @return com.android.ddmlib.IDevice - * @author ZhouYiXun - * @des 根据udId获取iDevice对象 - * @date 2021/8/16 19:42 - */ - public static IDevice getIDeviceByUdId(String udId) { - IDevice iDevice = null; - for (IDevice device : AndroidDeviceBridgeTool.getRealOnLineDevices()) { - //如果设备是在线状态并且序列号相等,则就是这个设备 - if (device.getState().equals(IDevice.DeviceState.ONLINE) - && device.getSerialNumber().equals(udId)) { - iDevice = device; - break; - } - } - if (iDevice == null) { - logger.info("设备未连接!"); - } - return iDevice; - } - - /** - * @param iDevice - * @return java.lang.String - * @author ZhouYiXun - * @des 获取屏幕大小 - * @date 2021/8/16 19:44 - */ - public static String getScreenSize(IDevice iDevice) { - String size = ""; - try { - size = executeCommand(iDevice, "wm size"); - if (size.contains("Override size")) { - size = size.substring(size.indexOf("Override size")); - } else { - size = size.split(":")[1]; - } - //注意顺序问题 - size = size.trim() - .replace(":", "") - .replace("Override size", "") - .replace("\r", "") - .replace("\n", "") - .replace(" ", ""); - if (size.length() > 20) { - size = "未知"; - } - } catch (Exception e) { - logger.info("获取屏幕尺寸失败!拔插瞬间可忽略该错误..."); - } - return size; - } - - /** - * @param iDevice - * @param command - * @return java.lang.String - * @author ZhouYiXun - * @des 发送shell指令给对应设备 - * @date 2021/8/16 19:47 - */ - public static String executeCommand(IDevice iDevice, String command) { - CollectingOutputReceiver output = new CollectingOutputReceiver(); - try { - iDevice.executeShellCommand(command, output, 0, TimeUnit.MILLISECONDS); - } catch (Exception e) { - logger.info("发送shell指令 {} 给设备 {} 异常!" - , command, iDevice.getSerialNumber()); - logger.error(e.getMessage()); - } - return output.getOutput(); - } - - /** - * @param iDevice - * @param port - * @param service - * @return void - * @author ZhouYiXun - * @des 同adb forward指令,将设备内进程的端口暴露给pc本地,但是只能转发给localhost,不能转发给ipv4 - * @date 2021/8/16 19:52 - */ - public static void forward(IDevice iDevice, int port, String service) { - try { - logger.info("{} 设备 {} 服务端口转发到:{}", iDevice.getSerialNumber(), service, port); - iDevice.createForward(port, service, IDevice.DeviceUnixSocketNamespace.ABSTRACT); - } catch (Exception e) { - logger.error(e.getMessage()); - } - } - - /** - * @param iDevice - * @param port - * @param serviceName - * @return void - * @author ZhouYiXun - * @des 去掉转发 - * @date 2021/8/16 19:53 - */ - public static void removeForward(IDevice iDevice, int port, String serviceName) { - try { - logger.info("{} 设备 {} 服务端口取消转发到:{}", iDevice.getSerialNumber(), serviceName, port); - iDevice.removeForward(port, serviceName, IDevice.DeviceUnixSocketNamespace.ABSTRACT); - } catch (Exception e) { - logger.error(e.getMessage()); - } - } - - /** - * @param iDevice - * @param localPath - * @param remotePath - * @return void - * @author ZhouYiXun - * @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; -// } -// }); -// } - - /** - * @param iDevice - * @param keyNum - * @return void - * @author ZhouYiXun - * @des 输入对应按键 - * @date 2021/8/16 19:59 - */ - public static void pressKey(IDevice iDevice, int keyNum) { - executeCommand(iDevice, String.format("input keyevent %s", keyNum)); - } - - /** - * @param iDevice - * @param key - * @return java.lang.String - * @author ZhouYiXun - * @des 获取设备配置信息 - * @date 2021/8/16 20:01 - */ - public static String getProperties(IDevice iDevice, String key) { - return iDevice.getProperty(key); - } - - /** - * @param sdk - * @return java.lang.String - * @author ZhouYiXun - * @des 根据sdk匹配对应的文件 - * @date 2021/8/16 20:01 - */ - public static String matchMiniCapFile(String sdk) { - String filePath; - if (Integer.valueOf(sdk) < 16) { - filePath = "minicap-nopie"; - } else { - filePath = "minicap"; - } - return filePath; - } - - public static void screen(IDevice iDevice, String type) { - int p = getScreen(iDevice); - try { - switch (type) { - case "abort": - executeCommand(iDevice, "content insert --uri content://settings/system --bind name:s:accelerometer_rotation --bind value:i:0"); - break; - case "add": - if (p == 3) { - p = 0; - } else { - p++; - } - executeCommand(iDevice, "content insert --uri content://settings/system --bind name:s:user_rotation --bind value:i:" + p); - break; - case "sub": - if (p == 0) { - p = 3; - } else { - p--; - } - executeCommand(iDevice, "content insert --uri content://settings/system --bind name:s:user_rotation --bind value:i:" + p); - break; - } - } catch (Exception e) { - logger.error(e.getMessage()); - } - } - - public static int getScreen(IDevice iDevice) { - try { - return Integer.parseInt(executeCommand(iDevice, "settings get system user_rotation") - .trim().replaceAll("\n", "") - .replace("\t", "")); - } catch (Exception e) { - logger.error(e.getMessage()); - return 0; - } - } - - public static void pushYadb(IDevice iDevice) { - 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); - 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(); - } - } - - public static void searchDevice(IDevice iDevice) { - executeCommand(iDevice, "am start -n com.sonic.plugins.assist/com.sonic.plugins.assist.SearchActivity"); - } - - public static void controlBattery(IDevice iDevice, int type) { - if (type == 0) { - executeCommand(iDevice, "dumpsys battery unplug && dumpsys battery set status 1"); - } - if (type == 1) { - executeCommand(iDevice, "dumpsys battery reset"); - } - } - - /** - * @param udId - * @param packageName - * @return java.lang.String - * @author ZhouYiXun - * @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", ""); -// } -} +package org.cloud.sonic.agent.bridge.android; + +import com.android.ddmlib.*; +import org.cloud.sonic.agent.tests.android.AndroidTemperThread; +import org.cloud.sonic.agent.tools.DownImageTool; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.ApplicationListener; +import org.springframework.context.annotation.DependsOn; +import org.springframework.context.event.ContextRefreshedEvent; +import org.springframework.lang.NonNull; +import org.springframework.stereotype.Component; + +import java.io.File; +import java.io.IOException; +import java.util.concurrent.TimeUnit; + +/** + * @author ZhouYiXun + * @des ADB工具类 + * @date 2021/08/16 19:26 + */ +@ConditionalOnProperty(value = "modules.android.enable", havingValue = "true") +@DependsOn({"androidThreadPoolInit", "nettyMsgInit"}) +@Component +public class AndroidDeviceBridgeTool implements ApplicationListener { + private static final Logger logger = LoggerFactory.getLogger(AndroidDeviceBridgeTool.class); + public static AndroidDebugBridge androidDebugBridge = null; + private AndroidTemperThread androidTemperThread = null; + + @Autowired + private AndroidDeviceStatusListener androidDeviceStatusListener; + + + @Override + public void onApplicationEvent(@NonNull ContextRefreshedEvent event) { + logger.info("开启安卓相关功能"); + } + + /** + * @return java.lang.String + * @author ZhouYiXun + * @des 获取系统安卓SDK路径 + * @date 2021/8/16 19:35 + */ + private static String getADBPathFromSystemEnv() { + String path = System.getenv("ANDROID_HOME"); + if (path != null) { + path += File.separator + "platform-tools" + File.separator + "adb"; + } else { + logger.error("获取ANDROID_HOME环境变量失败!"); + return null; + } + return path; + } + + /** + * @return void + * @author ZhouYiXun + * @des 定义方法 + * @date 2021/8/16 19:36 + */ + public void init() { + //获取系统SDK路径 + String systemADBPath = getADBPathFromSystemEnv(); + //添加设备上下线监听 + androidDebugBridge.addDeviceChangeListener(androidDeviceStatusListener); + try { + AndroidDebugBridge.init(false); + } catch (IllegalStateException e) { + logger.warn("AndroidDebugBridge已经初始化过,无需再初始化"); + } + //开始创建ADB + androidDebugBridge = AndroidDebugBridge.createBridge(systemADBPath, true, Long.MAX_VALUE, TimeUnit.MILLISECONDS); + if (androidDebugBridge != null) { + logger.info("安卓设备监听已开启"); + } + int count = 0; + //获取设备列表,超时后退出 + while (androidDebugBridge.hasInitialDeviceList() == false) { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + count++; + if (count > 200) { + break; + } + } + if (androidTemperThread == null || !androidTemperThread.isAlive()) { + androidTemperThread = new AndroidTemperThread(); + androidTemperThread.start(); + } + } + + /** + * @return com.android.ddmlib.IDevice[] + * @author ZhouYiXun + * @des 获取真实在线设备列表 + * @date 2021/8/16 19:38 + */ + public static IDevice[] getRealOnLineDevices() { + if (androidDebugBridge != null) { + return androidDebugBridge.getDevices(); + } else { + return null; + } + } + + /** + * @param iDevice + * @return void + * @author ZhouYiXun + * @des 重启设备 + * @date 2021/8/16 19:41 + */ + public static void reboot(IDevice iDevice) { + if (iDevice != null) { + executeCommand(iDevice, "reboot"); + } + } + + /** + * @param udId + * @return com.android.ddmlib.IDevice + * @author ZhouYiXun + * @des 根据udId获取iDevice对象 + * @date 2021/8/16 19:42 + */ + public static IDevice getIDeviceByUdId(String udId) { + IDevice iDevice = null; + for (IDevice device : AndroidDeviceBridgeTool.getRealOnLineDevices()) { + //如果设备是在线状态并且序列号相等,则就是这个设备 + if (device.getState().equals(IDevice.DeviceState.ONLINE) + && device.getSerialNumber().equals(udId)) { + iDevice = device; + break; + } + } + if (iDevice == null) { + logger.info("设备未连接!"); + } + return iDevice; + } + + /** + * @param iDevice + * @return java.lang.String + * @author ZhouYiXun + * @des 获取屏幕大小 + * @date 2021/8/16 19:44 + */ + public static String getScreenSize(IDevice iDevice) { + String size = ""; + try { + size = executeCommand(iDevice, "wm size"); + if (size.contains("Override size")) { + size = size.substring(size.indexOf("Override size")); + } else { + size = size.split(":")[1]; + } + //注意顺序问题 + size = size.trim() + .replace(":", "") + .replace("Override size", "") + .replace("\r", "") + .replace("\n", "") + .replace(" ", ""); + if (size.length() > 20) { + size = "未知"; + } + } catch (Exception e) { + logger.info("获取屏幕尺寸失败!拔插瞬间可忽略该错误..."); + } + return size; + } + + /** + * @param iDevice + * @param command + * @return java.lang.String + * @author ZhouYiXun + * @des 发送shell指令给对应设备 + * @date 2021/8/16 19:47 + */ + public static String executeCommand(IDevice iDevice, String command) { + CollectingOutputReceiver output = new CollectingOutputReceiver(); + try { + iDevice.executeShellCommand(command, output, 0, TimeUnit.MILLISECONDS); + } catch (Exception e) { + logger.info("发送shell指令 {} 给设备 {} 异常!" + , command, iDevice.getSerialNumber()); + logger.error(e.getMessage()); + } + return output.getOutput(); + } + + /** + * @param iDevice + * @param port + * @param service + * @return void + * @author ZhouYiXun + * @des 同adb forward指令,将设备内进程的端口暴露给pc本地,但是只能转发给localhost,不能转发给ipv4 + * @date 2021/8/16 19:52 + */ + public static void forward(IDevice iDevice, int port, String service) { + try { + logger.info("{} 设备 {} 服务端口转发到:{}", iDevice.getSerialNumber(), service, port); + iDevice.createForward(port, service, IDevice.DeviceUnixSocketNamespace.ABSTRACT); + } catch (Exception e) { + logger.error(e.getMessage()); + } + } + + /** + * @param iDevice + * @param port + * @param serviceName + * @return void + * @author ZhouYiXun + * @des 去掉转发 + * @date 2021/8/16 19:53 + */ + public static void removeForward(IDevice iDevice, int port, String serviceName) { + try { + logger.info("{} 设备 {} 服务端口取消转发到:{}", iDevice.getSerialNumber(), serviceName, port); + iDevice.removeForward(port, serviceName, IDevice.DeviceUnixSocketNamespace.ABSTRACT); + } catch (Exception e) { + logger.error(e.getMessage()); + } + } + + /** + * @param iDevice + * @param localPath + * @param remotePath + * @return void + * @author ZhouYiXun + * @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; +// } +// }); +// } + + /** + * @param iDevice + * @param keyNum + * @return void + * @author ZhouYiXun + * @des 输入对应按键 + * @date 2021/8/16 19:59 + */ + public static void pressKey(IDevice iDevice, int keyNum) { + executeCommand(iDevice, String.format("input keyevent %s", keyNum)); + } + + /** + * @param iDevice + * @param key + * @return java.lang.String + * @author ZhouYiXun + * @des 获取设备配置信息 + * @date 2021/8/16 20:01 + */ + public static String getProperties(IDevice iDevice, String key) { + return iDevice.getProperty(key); + } + + /** + * @param sdk + * @return java.lang.String + * @author ZhouYiXun + * @des 根据sdk匹配对应的文件 + * @date 2021/8/16 20:01 + */ + public static String matchMiniCapFile(String sdk) { + String filePath; + if (Integer.valueOf(sdk) < 16) { + filePath = "minicap-nopie"; + } else { + filePath = "minicap"; + } + return filePath; + } + + public static void screen(IDevice iDevice, String type) { + int p = getScreen(iDevice); + try { + switch (type) { + case "abort": + executeCommand(iDevice, "content insert --uri content://settings/system --bind name:s:accelerometer_rotation --bind value:i:0"); + break; + case "add": + if (p == 3) { + p = 0; + } else { + p++; + } + executeCommand(iDevice, "content insert --uri content://settings/system --bind name:s:user_rotation --bind value:i:" + p); + break; + case "sub": + if (p == 0) { + p = 3; + } else { + p--; + } + executeCommand(iDevice, "content insert --uri content://settings/system --bind name:s:user_rotation --bind value:i:" + p); + break; + } + } catch (Exception e) { + logger.error(e.getMessage()); + } + } + + public static int getScreen(IDevice iDevice) { + try { + return Integer.parseInt(executeCommand(iDevice, "settings get system user_rotation") + .trim().replaceAll("\n", "") + .replace("\t", "")); + } catch (Exception e) { + logger.error(e.getMessage()); + return 0; + } + } + + public static void pushYadb(IDevice iDevice) { + 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); + 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(); + } + } + + public static void searchDevice(IDevice iDevice) { + executeCommand(iDevice, "am start -n com.sonic.plugins.assist/com.sonic.plugins.assist.SearchActivity"); + } + + public static void controlBattery(IDevice iDevice, int type) { + if (type == 0) { + executeCommand(iDevice, "dumpsys battery unplug && dumpsys battery set status 1"); + } + if (type == 1) { + executeCommand(iDevice, "dumpsys battery reset"); + } + } + + /** + * @param udId + * @param packageName + * @return java.lang.String + * @author ZhouYiXun + * @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", ""); +// } +} diff --git a/src/main/java/com/sonic/agent/bridge/android/AndroidDeviceLocalStatus.java b/src/main/java/org/cloud/sonic/agent/bridge/android/AndroidDeviceLocalStatus.java similarity index 90% rename from src/main/java/com/sonic/agent/bridge/android/AndroidDeviceLocalStatus.java rename to src/main/java/org/cloud/sonic/agent/bridge/android/AndroidDeviceLocalStatus.java index 053c4300..fa12cfb8 100644 --- a/src/main/java/com/sonic/agent/bridge/android/AndroidDeviceLocalStatus.java +++ b/src/main/java/org/cloud/sonic/agent/bridge/android/AndroidDeviceLocalStatus.java @@ -1,97 +1,97 @@ -package com.sonic.agent.bridge.android; - -import com.alibaba.fastjson.JSONObject; -import com.sonic.agent.interfaces.DeviceStatus; -import com.sonic.agent.maps.AndroidDeviceManagerMap; -import com.sonic.agent.netty.NettyThreadPool; - -/** - * @author ZhouYiXun - * @des 本地自定义的状态变更,不是通过adb的变更 - * @date 2021/08/16 19:26 - */ -public class AndroidDeviceLocalStatus { - - /** - * @param udId - * @param status - * @return void - * @author ZhouYiXun - * @des 发送状态变更消息 - * @date 2021/8/16 20:56 - */ - public static void send(String udId, String status) { - JSONObject deviceDetail = new JSONObject(); - deviceDetail.put("msg", "deviceDetail"); - deviceDetail.put("udId", udId); - deviceDetail.put("status", status); - NettyThreadPool.send(deviceDetail); - } - - /** - * @param udId - * @return void - * @author ZhouYiXun - * @des 开始测试 - * @date 2021/8/16 20:57 - */ - public static boolean startTest(String udId) { - synchronized (AndroidDeviceLocalStatus.class) { - if (AndroidDeviceManagerMap.getMap().get(udId) == null) { - send(udId, DeviceStatus.TESTING); - AndroidDeviceManagerMap.getMap().put(udId, DeviceStatus.TESTING); - return true; - } else { - return false; - } - } - } - - /** - * @param udId - * @return void - * @author ZhouYiXun - * @des 开始调试 - * @date 2021/8/16 20:57 - */ - public static void startDebug(String udId) { - send(udId, DeviceStatus.DEBUGGING); - AndroidDeviceManagerMap.getMap().put(udId, DeviceStatus.DEBUGGING); - } - - /** - * @param udId - * @return void - * @author ZhouYiXun - * @des 使用完毕 - * @date 2021/8/16 19:47 - */ - public static void finish(String udId) { - if (AndroidDeviceBridgeTool.getIDeviceByUdId(udId) != null - && AndroidDeviceManagerMap.getMap().get(udId) != null) { - if (AndroidDeviceManagerMap.getMap().get(udId).equals(DeviceStatus.DEBUGGING) - || AndroidDeviceManagerMap.getMap().get(udId).equals(DeviceStatus.TESTING)) { - send(udId, DeviceStatus.ONLINE); - } - } - AndroidDeviceManagerMap.getMap().remove(udId); - } - - /** - * @param udId - * @return void - * @author ZhouYiXun - * @des 使用完毕异常 - * @date 2021/8/16 19:47 - */ - public static void finishError(String udId) { - if (AndroidDeviceBridgeTool.getIDeviceByUdId(udId) != null - && AndroidDeviceManagerMap.getMap().get(udId) != null) { - if (AndroidDeviceManagerMap.getMap().get(udId).equals(DeviceStatus.DEBUGGING) - || AndroidDeviceManagerMap.getMap().get(udId).equals(DeviceStatus.TESTING)) { - send(udId, DeviceStatus.ERROR); - } - } - AndroidDeviceManagerMap.getMap().remove(udId); - } -} +package org.cloud.sonic.agent.bridge.android; + +import com.alibaba.fastjson.JSONObject; +import org.cloud.sonic.agent.interfaces.DeviceStatus; +import org.cloud.sonic.agent.maps.AndroidDeviceManagerMap; +import org.cloud.sonic.agent.netty.NettyThreadPool; + +/** + * @author ZhouYiXun + * @des 本地自定义的状态变更,不是通过adb的变更 + * @date 2021/08/16 19:26 + */ +public class AndroidDeviceLocalStatus { + + /** + * @param udId + * @param status + * @return void + * @author ZhouYiXun + * @des 发送状态变更消息 + * @date 2021/8/16 20:56 + */ + public static void send(String udId, String status) { + JSONObject deviceDetail = new JSONObject(); + deviceDetail.put("msg", "deviceDetail"); + deviceDetail.put("udId", udId); + deviceDetail.put("status", status); + NettyThreadPool.send(deviceDetail); + } + + /** + * @param udId + * @return void + * @author ZhouYiXun + * @des 开始测试 + * @date 2021/8/16 20:57 + */ + public static boolean startTest(String udId) { + synchronized (AndroidDeviceLocalStatus.class) { + if (AndroidDeviceManagerMap.getMap().get(udId) == null) { + send(udId, DeviceStatus.TESTING); + AndroidDeviceManagerMap.getMap().put(udId, DeviceStatus.TESTING); + return true; + } else { + return false; + } + } + } + + /** + * @param udId + * @return void + * @author ZhouYiXun + * @des 开始调试 + * @date 2021/8/16 20:57 + */ + public static void startDebug(String udId) { + send(udId, DeviceStatus.DEBUGGING); + AndroidDeviceManagerMap.getMap().put(udId, DeviceStatus.DEBUGGING); + } + + /** + * @param udId + * @return void + * @author ZhouYiXun + * @des 使用完毕 + * @date 2021/8/16 19:47 + */ + public static void finish(String udId) { + if (AndroidDeviceBridgeTool.getIDeviceByUdId(udId) != null + && AndroidDeviceManagerMap.getMap().get(udId) != null) { + if (AndroidDeviceManagerMap.getMap().get(udId).equals(DeviceStatus.DEBUGGING) + || AndroidDeviceManagerMap.getMap().get(udId).equals(DeviceStatus.TESTING)) { + send(udId, DeviceStatus.ONLINE); + } + } + AndroidDeviceManagerMap.getMap().remove(udId); + } + + /** + * @param udId + * @return void + * @author ZhouYiXun + * @des 使用完毕异常 + * @date 2021/8/16 19:47 + */ + public static void finishError(String udId) { + if (AndroidDeviceBridgeTool.getIDeviceByUdId(udId) != null + && AndroidDeviceManagerMap.getMap().get(udId) != null) { + if (AndroidDeviceManagerMap.getMap().get(udId).equals(DeviceStatus.DEBUGGING) + || AndroidDeviceManagerMap.getMap().get(udId).equals(DeviceStatus.TESTING)) { + send(udId, DeviceStatus.ERROR); + } + } + AndroidDeviceManagerMap.getMap().remove(udId); + } +} diff --git a/src/main/java/com/sonic/agent/bridge/android/AndroidDeviceStatusListener.java b/src/main/java/org/cloud/sonic/agent/bridge/android/AndroidDeviceStatusListener.java similarity index 90% rename from src/main/java/com/sonic/agent/bridge/android/AndroidDeviceStatusListener.java rename to src/main/java/org/cloud/sonic/agent/bridge/android/AndroidDeviceStatusListener.java index 268ff575..698418e3 100644 --- a/src/main/java/com/sonic/agent/bridge/android/AndroidDeviceStatusListener.java +++ b/src/main/java/org/cloud/sonic/agent/bridge/android/AndroidDeviceStatusListener.java @@ -1,67 +1,67 @@ -package com.sonic.agent.bridge.android; - -import com.alibaba.fastjson.JSONObject; -import com.android.ddmlib.AndroidDebugBridge; -import com.android.ddmlib.IDevice; -import com.sonic.agent.interfaces.PlatformType; -import com.sonic.agent.maps.AndroidDeviceManagerMap; -import com.sonic.agent.netty.NettyThreadPool; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.stereotype.Component; - -/** - * @author ZhouYiXun - * @des adb上下线监听,发送对应给server - * @date 2021/08/16 19:26 - */ -@Component -public class AndroidDeviceStatusListener implements AndroidDebugBridge.IDeviceChangeListener { - private final Logger logger = LoggerFactory.getLogger(AndroidDeviceStatusListener.class); - - /** - * @param device - * @return void - * @author ZhouYiXun - * @des 发送设备状态 - * @date 2021/8/16 19:58 - */ - private void send(IDevice device) { - JSONObject deviceDetail = new JSONObject(); - deviceDetail.put("msg", "deviceDetail"); - deviceDetail.put("udId", device.getSerialNumber()); - deviceDetail.put("name", device.getProperty("ro.product.name")); - deviceDetail.put("model", device.getProperty(IDevice.PROP_DEVICE_MODEL)); - deviceDetail.put("status", device.getState()); - deviceDetail.put("platform", PlatformType.ANDROID); - deviceDetail.put("version", device.getProperty(IDevice.PROP_BUILD_VERSION)); - deviceDetail.put("size", AndroidDeviceBridgeTool.getScreenSize(device)); - deviceDetail.put("cpu", device.getProperty(IDevice.PROP_DEVICE_CPU_ABI)); - deviceDetail.put("manufacturer", device.getProperty(IDevice.PROP_DEVICE_MANUFACTURER)); - NettyThreadPool.send(deviceDetail); - } - - @Override - public void deviceConnected(IDevice device) { - logger.info("Android设备:" + device.getSerialNumber() + " ONLINE!"); - AndroidDeviceManagerMap.getMap().remove(device.getSerialNumber()); - send(device); - } - - @Override - public void deviceDisconnected(IDevice device) { - logger.info("Android设备:" + device.getSerialNumber() + " OFFLINE!"); - AndroidDeviceManagerMap.getMap().remove(device.getSerialNumber()); - send(device); - } - - @Override - public void deviceChanged(IDevice device, int changeMask) { - if (device.isOnline()) { - logger.info("Android设备:" + device.getSerialNumber() + " ONLINE!"); - } else { - logger.info("Android设备:" + device.getSerialNumber() + " OFFLINE!"); - } - send(device); - } +package org.cloud.sonic.agent.bridge.android; + +import com.alibaba.fastjson.JSONObject; +import com.android.ddmlib.AndroidDebugBridge; +import com.android.ddmlib.IDevice; +import org.cloud.sonic.agent.interfaces.PlatformType; +import org.cloud.sonic.agent.maps.AndroidDeviceManagerMap; +import org.cloud.sonic.agent.netty.NettyThreadPool; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +/** + * @author ZhouYiXun + * @des adb上下线监听,发送对应给server + * @date 2021/08/16 19:26 + */ +@Component +public class AndroidDeviceStatusListener implements AndroidDebugBridge.IDeviceChangeListener { + private final Logger logger = LoggerFactory.getLogger(AndroidDeviceStatusListener.class); + + /** + * @param device + * @return void + * @author ZhouYiXun + * @des 发送设备状态 + * @date 2021/8/16 19:58 + */ + private void send(IDevice device) { + JSONObject deviceDetail = new JSONObject(); + deviceDetail.put("msg", "deviceDetail"); + deviceDetail.put("udId", device.getSerialNumber()); + deviceDetail.put("name", device.getProperty("ro.product.name")); + deviceDetail.put("model", device.getProperty(IDevice.PROP_DEVICE_MODEL)); + deviceDetail.put("status", device.getState()); + deviceDetail.put("platform", PlatformType.ANDROID); + deviceDetail.put("version", device.getProperty(IDevice.PROP_BUILD_VERSION)); + deviceDetail.put("size", AndroidDeviceBridgeTool.getScreenSize(device)); + deviceDetail.put("cpu", device.getProperty(IDevice.PROP_DEVICE_CPU_ABI)); + deviceDetail.put("manufacturer", device.getProperty(IDevice.PROP_DEVICE_MANUFACTURER)); + NettyThreadPool.send(deviceDetail); + } + + @Override + public void deviceConnected(IDevice device) { + logger.info("Android设备:" + device.getSerialNumber() + " ONLINE!"); + AndroidDeviceManagerMap.getMap().remove(device.getSerialNumber()); + send(device); + } + + @Override + public void deviceDisconnected(IDevice device) { + logger.info("Android设备:" + device.getSerialNumber() + " OFFLINE!"); + AndroidDeviceManagerMap.getMap().remove(device.getSerialNumber()); + send(device); + } + + @Override + public void deviceChanged(IDevice device, int changeMask) { + if (device.isOnline()) { + logger.info("Android设备:" + device.getSerialNumber() + " ONLINE!"); + } else { + logger.info("Android设备:" + device.getSerialNumber() + " OFFLINE!"); + } + send(device); + } } \ No newline at end of file diff --git a/src/main/java/com/sonic/agent/bridge/android/AndroidDeviceThreadPool.java b/src/main/java/org/cloud/sonic/agent/bridge/android/AndroidDeviceThreadPool.java similarity index 91% rename from src/main/java/com/sonic/agent/bridge/android/AndroidDeviceThreadPool.java rename to src/main/java/org/cloud/sonic/agent/bridge/android/AndroidDeviceThreadPool.java index bfd04dbf..38a85846 100644 --- a/src/main/java/com/sonic/agent/bridge/android/AndroidDeviceThreadPool.java +++ b/src/main/java/org/cloud/sonic/agent/bridge/android/AndroidDeviceThreadPool.java @@ -1,24 +1,24 @@ -package com.sonic.agent.bridge.android; - -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - -/** - * @author ZhouYiXun - * @des 所有安卓相关线程都放这个线程池 - * @date 2021/08/16 19:26 - */ -@Configuration -@ConditionalOnProperty(value = "modules.android.enable", havingValue = "true") -public class AndroidDeviceThreadPool { - public static ExecutorService cachedThreadPool; - - @Bean - public void androidThreadPoolInit() { - cachedThreadPool = Executors.newCachedThreadPool(); - } +package org.cloud.sonic.agent.bridge.android; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +/** + * @author ZhouYiXun + * @des 所有安卓相关线程都放这个线程池 + * @date 2021/08/16 19:26 + */ +@Configuration +@ConditionalOnProperty(value = "modules.android.enable", havingValue = "true") +public class AndroidDeviceThreadPool { + public static ExecutorService cachedThreadPool; + + @Bean + public void androidThreadPoolInit() { + cachedThreadPool = Executors.newCachedThreadPool(); + } } \ No newline at end of file diff --git a/src/main/java/com/sonic/agent/bridge/ios/IOSDeviceLocalStatus.java b/src/main/java/org/cloud/sonic/agent/bridge/ios/IOSDeviceLocalStatus.java similarity index 88% rename from src/main/java/com/sonic/agent/bridge/ios/IOSDeviceLocalStatus.java rename to src/main/java/org/cloud/sonic/agent/bridge/ios/IOSDeviceLocalStatus.java index 960daeeb..8559c767 100644 --- a/src/main/java/com/sonic/agent/bridge/ios/IOSDeviceLocalStatus.java +++ b/src/main/java/org/cloud/sonic/agent/bridge/ios/IOSDeviceLocalStatus.java @@ -1,10 +1,10 @@ -package com.sonic.agent.bridge.ios; +package org.cloud.sonic.agent.bridge.ios; import com.alibaba.fastjson.JSONObject; -import com.sonic.agent.interfaces.DeviceStatus; -import com.sonic.agent.maps.IOSDeviceManagerMap; -import com.sonic.agent.maps.IOSSizeMap; -import com.sonic.agent.netty.NettyThreadPool; +import org.cloud.sonic.agent.interfaces.DeviceStatus; +import org.cloud.sonic.agent.maps.IOSDeviceManagerMap; +import org.cloud.sonic.agent.maps.IOSSizeMap; +import org.cloud.sonic.agent.netty.NettyThreadPool; public class IOSDeviceLocalStatus { diff --git a/src/main/java/com/sonic/agent/bridge/ios/IOSDeviceThreadPool.java b/src/main/java/org/cloud/sonic/agent/bridge/ios/IOSDeviceThreadPool.java similarity index 91% rename from src/main/java/com/sonic/agent/bridge/ios/IOSDeviceThreadPool.java rename to src/main/java/org/cloud/sonic/agent/bridge/ios/IOSDeviceThreadPool.java index b26941cd..2f92e801 100644 --- a/src/main/java/com/sonic/agent/bridge/ios/IOSDeviceThreadPool.java +++ b/src/main/java/org/cloud/sonic/agent/bridge/ios/IOSDeviceThreadPool.java @@ -1,24 +1,24 @@ -package com.sonic.agent.bridge.ios; - -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - -/** - * @author ZhouYiXun - * @des 所有iOS相关线程都放这个线程池 - * @date 2021/08/25 19:26 - */ -@Configuration -@ConditionalOnProperty(value = "modules.ios.enable", havingValue = "true") -public class IOSDeviceThreadPool { - public static ExecutorService cachedThreadPool; - - @Bean - public void iOSThreadPoolInit() { - cachedThreadPool = Executors.newCachedThreadPool(); - } +package org.cloud.sonic.agent.bridge.ios; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +/** + * @author ZhouYiXun + * @des 所有iOS相关线程都放这个线程池 + * @date 2021/08/25 19:26 + */ +@Configuration +@ConditionalOnProperty(value = "modules.ios.enable", havingValue = "true") +public class IOSDeviceThreadPool { + public static ExecutorService cachedThreadPool; + + @Bean + public void iOSThreadPoolInit() { + cachedThreadPool = Executors.newCachedThreadPool(); + } } \ No newline at end of file diff --git a/src/main/java/com/sonic/agent/bridge/ios/TIDeviceTool.java b/src/main/java/org/cloud/sonic/agent/bridge/ios/TIDeviceTool.java similarity index 95% rename from src/main/java/com/sonic/agent/bridge/ios/TIDeviceTool.java rename to src/main/java/org/cloud/sonic/agent/bridge/ios/TIDeviceTool.java index 4143bd9d..341ca380 100644 --- a/src/main/java/com/sonic/agent/bridge/ios/TIDeviceTool.java +++ b/src/main/java/org/cloud/sonic/agent/bridge/ios/TIDeviceTool.java @@ -1,14 +1,14 @@ -package com.sonic.agent.bridge.ios; +package org.cloud.sonic.agent.bridge.ios; import com.alibaba.fastjson.JSONObject; -import com.sonic.agent.interfaces.PlatformType; -import com.sonic.agent.maps.IOSDeviceManagerMap; -import com.sonic.agent.maps.IOSProcessMap; -import com.sonic.agent.maps.IOSSizeMap; -import com.sonic.agent.netty.NettyClientHandler; -import com.sonic.agent.netty.NettyThreadPool; -import com.sonic.agent.tools.PortTool; -import com.sonic.agent.tools.ProcessCommandTool; +import org.cloud.sonic.agent.interfaces.PlatformType; +import org.cloud.sonic.agent.maps.IOSDeviceManagerMap; +import org.cloud.sonic.agent.maps.IOSProcessMap; +import org.cloud.sonic.agent.maps.IOSSizeMap; +import org.cloud.sonic.agent.netty.NettyClientHandler; +import org.cloud.sonic.agent.netty.NettyThreadPool; +import org.cloud.sonic.agent.tools.PortTool; +import org.cloud.sonic.agent.tools.ProcessCommandTool; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; diff --git a/src/main/java/com/sonic/agent/config/RestTemplateConfig.java b/src/main/java/org/cloud/sonic/agent/config/RestTemplateConfig.java similarity index 93% rename from src/main/java/com/sonic/agent/config/RestTemplateConfig.java rename to src/main/java/org/cloud/sonic/agent/config/RestTemplateConfig.java index 52f34da6..c3d85a24 100644 --- a/src/main/java/com/sonic/agent/config/RestTemplateConfig.java +++ b/src/main/java/org/cloud/sonic/agent/config/RestTemplateConfig.java @@ -1,24 +1,24 @@ -package com.sonic.agent.config; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.http.client.ClientHttpRequestFactory; -import org.springframework.http.client.SimpleClientHttpRequestFactory; -import org.springframework.web.client.RestTemplate; - -@Configuration -public class RestTemplateConfig { - - @Bean - public RestTemplate restTemplate(ClientHttpRequestFactory factory) { - return new RestTemplate(factory); - } - - @Bean - public ClientHttpRequestFactory simpleClientHttpRequestFactory() { - SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory(); - factory.setReadTimeout(120000); - factory.setConnectTimeout(150000); - return factory; - } +package org.cloud.sonic.agent.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.client.ClientHttpRequestFactory; +import org.springframework.http.client.SimpleClientHttpRequestFactory; +import org.springframework.web.client.RestTemplate; + +@Configuration +public class RestTemplateConfig { + + @Bean + public RestTemplate restTemplate(ClientHttpRequestFactory factory) { + return new RestTemplate(factory); + } + + @Bean + public ClientHttpRequestFactory simpleClientHttpRequestFactory() { + SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory(); + factory.setReadTimeout(120000); + factory.setConnectTimeout(150000); + return factory; + } } \ No newline at end of file diff --git a/src/main/java/com/sonic/agent/interfaces/DeviceStatus.java b/src/main/java/org/cloud/sonic/agent/interfaces/DeviceStatus.java similarity index 87% rename from src/main/java/com/sonic/agent/interfaces/DeviceStatus.java rename to src/main/java/org/cloud/sonic/agent/interfaces/DeviceStatus.java index 93ee781a..987a1599 100644 --- a/src/main/java/com/sonic/agent/interfaces/DeviceStatus.java +++ b/src/main/java/org/cloud/sonic/agent/interfaces/DeviceStatus.java @@ -1,16 +1,16 @@ -package com.sonic.agent.interfaces; - -/** - * @author ZhouYiXun - * @des 枚举设备状态 - * @date 2021/08/16 19:26 - */ -public interface DeviceStatus { - String ONLINE = "ONLINE"; - String OFFLINE = "OFFLINE"; - String TESTING = "TESTING"; - String DEBUGGING = "DEBUGGING"; - String ERROR = "ERROR"; - String UNAUTHORIZED = "UNAUTHORIZED"; - String DISCONNECTED = "DISCONNECTED"; -} +package org.cloud.sonic.agent.interfaces; + +/** + * @author ZhouYiXun + * @des 枚举设备状态 + * @date 2021/08/16 19:26 + */ +public interface DeviceStatus { + String ONLINE = "ONLINE"; + String OFFLINE = "OFFLINE"; + String TESTING = "TESTING"; + String DEBUGGING = "DEBUGGING"; + String ERROR = "ERROR"; + String UNAUTHORIZED = "UNAUTHORIZED"; + String DISCONNECTED = "DISCONNECTED"; +} diff --git a/src/main/java/com/sonic/agent/interfaces/ErrorType.java b/src/main/java/org/cloud/sonic/agent/interfaces/ErrorType.java similarity index 68% rename from src/main/java/com/sonic/agent/interfaces/ErrorType.java rename to src/main/java/org/cloud/sonic/agent/interfaces/ErrorType.java index 74776a73..a9f78c64 100644 --- a/src/main/java/com/sonic/agent/interfaces/ErrorType.java +++ b/src/main/java/org/cloud/sonic/agent/interfaces/ErrorType.java @@ -1,7 +1,7 @@ -package com.sonic.agent.interfaces; - -public interface ErrorType { - int IGNORE = 1; - int WARNING = 2; - int SHUTDOWN = 3; -} +package org.cloud.sonic.agent.interfaces; + +public interface ErrorType { + int IGNORE = 1; + int WARNING = 2; + int SHUTDOWN = 3; +} diff --git a/src/main/java/com/sonic/agent/interfaces/PlatformType.java b/src/main/java/org/cloud/sonic/agent/interfaces/PlatformType.java similarity index 81% rename from src/main/java/com/sonic/agent/interfaces/PlatformType.java rename to src/main/java/org/cloud/sonic/agent/interfaces/PlatformType.java index 5c65dcd3..a685d114 100644 --- a/src/main/java/com/sonic/agent/interfaces/PlatformType.java +++ b/src/main/java/org/cloud/sonic/agent/interfaces/PlatformType.java @@ -1,14 +1,14 @@ -package com.sonic.agent.interfaces; - -/** - * @author ZhouYiXun - * @des 定义全局平台类型 - * @date 2021/8/15 19:26 - */ -public interface PlatformType { - int ANDROID = 1; - int IOS = 2; - int WINDOWS = 3; - int MAC = 4; - int WEB = 5; -} +package org.cloud.sonic.agent.interfaces; + +/** + * @author ZhouYiXun + * @des 定义全局平台类型 + * @date 2021/8/15 19:26 + */ +public interface PlatformType { + int ANDROID = 1; + int IOS = 2; + int WINDOWS = 3; + int MAC = 4; + int WEB = 5; +} diff --git a/src/main/java/com/sonic/agent/interfaces/ResultDetailStatus.java b/src/main/java/org/cloud/sonic/agent/interfaces/ResultDetailStatus.java similarity index 68% rename from src/main/java/com/sonic/agent/interfaces/ResultDetailStatus.java rename to src/main/java/org/cloud/sonic/agent/interfaces/ResultDetailStatus.java index ffafd66b..9eb870d8 100644 --- a/src/main/java/com/sonic/agent/interfaces/ResultDetailStatus.java +++ b/src/main/java/org/cloud/sonic/agent/interfaces/ResultDetailStatus.java @@ -1,7 +1,7 @@ -package com.sonic.agent.interfaces; - -public interface ResultDetailStatus { - int PASS = 1; - int WARN = 2; - int FAIL = 3; -} +package org.cloud.sonic.agent.interfaces; + +public interface ResultDetailStatus { + int PASS = 1; + int WARN = 2; + int FAIL = 3; +} diff --git a/src/main/java/com/sonic/agent/interfaces/StepType.java b/src/main/java/org/cloud/sonic/agent/interfaces/StepType.java similarity index 78% rename from src/main/java/com/sonic/agent/interfaces/StepType.java rename to src/main/java/org/cloud/sonic/agent/interfaces/StepType.java index d1a54f05..67fdd07f 100644 --- a/src/main/java/com/sonic/agent/interfaces/StepType.java +++ b/src/main/java/org/cloud/sonic/agent/interfaces/StepType.java @@ -1,13 +1,13 @@ -package com.sonic.agent.interfaces; - -/** - * @author ZhouYiXun - * @des 定义log类型 - * @date 2021/8/15 19:26 - */ -public interface StepType { - int INFO = 1; - int PASS = 2; - int WARN = 3; - int ERROR = 4; -} +package org.cloud.sonic.agent.interfaces; + +/** + * @author ZhouYiXun + * @des 定义log类型 + * @date 2021/8/15 19:26 + */ +public interface StepType { + int INFO = 1; + int PASS = 2; + int WARN = 3; + int ERROR = 4; +} diff --git a/src/main/java/com/sonic/agent/maps/AndroidDeviceManagerMap.java b/src/main/java/org/cloud/sonic/agent/maps/AndroidDeviceManagerMap.java similarity index 89% rename from src/main/java/com/sonic/agent/maps/AndroidDeviceManagerMap.java rename to src/main/java/org/cloud/sonic/agent/maps/AndroidDeviceManagerMap.java index e28c39fb..066441e0 100644 --- a/src/main/java/com/sonic/agent/maps/AndroidDeviceManagerMap.java +++ b/src/main/java/org/cloud/sonic/agent/maps/AndroidDeviceManagerMap.java @@ -1,16 +1,16 @@ -package com.sonic.agent.maps; - -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -/** - * @author ZhouYiXun - * @des 本地自己维护一下设备状态 - * @date 2021/08/16 10:26 - */ -public class AndroidDeviceManagerMap { - private static Map deviceStatusMap = new ConcurrentHashMap(); - public static Map getMap() { - return deviceStatusMap; - } -} +package org.cloud.sonic.agent.maps; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @author ZhouYiXun + * @des 本地自己维护一下设备状态 + * @date 2021/08/16 10:26 + */ +public class AndroidDeviceManagerMap { + private static Map deviceStatusMap = new ConcurrentHashMap(); + public static Map getMap() { + return deviceStatusMap; + } +} diff --git a/src/main/java/com/sonic/agent/maps/AndroidPasswordMap.java b/src/main/java/org/cloud/sonic/agent/maps/AndroidPasswordMap.java similarity index 88% rename from src/main/java/com/sonic/agent/maps/AndroidPasswordMap.java rename to src/main/java/org/cloud/sonic/agent/maps/AndroidPasswordMap.java index 05ae3694..5bb2bb88 100644 --- a/src/main/java/com/sonic/agent/maps/AndroidPasswordMap.java +++ b/src/main/java/org/cloud/sonic/agent/maps/AndroidPasswordMap.java @@ -1,16 +1,16 @@ -package com.sonic.agent.maps; - -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -/** - * @author ZhouYiXun - * @des 存放密码 - * @date 2021/8/27 22:01 - */ -public class AndroidPasswordMap { - private static Map devicePasswordMap = new ConcurrentHashMap(); - public static Map getMap() { - return devicePasswordMap; - } -} +package org.cloud.sonic.agent.maps; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @author ZhouYiXun + * @des 存放密码 + * @date 2021/8/27 22:01 + */ +public class AndroidPasswordMap { + private static Map devicePasswordMap = new ConcurrentHashMap(); + public static Map getMap() { + return devicePasswordMap; + } +} diff --git a/src/main/java/com/sonic/agent/maps/DevicesLockMap.java b/src/main/java/org/cloud/sonic/agent/maps/DevicesLockMap.java similarity index 98% rename from src/main/java/com/sonic/agent/maps/DevicesLockMap.java rename to src/main/java/org/cloud/sonic/agent/maps/DevicesLockMap.java index 6b1de9bb..0a9a4201 100644 --- a/src/main/java/com/sonic/agent/maps/DevicesLockMap.java +++ b/src/main/java/org/cloud/sonic/agent/maps/DevicesLockMap.java @@ -1,4 +1,4 @@ -package com.sonic.agent.maps; +package org.cloud.sonic.agent.maps; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/main/java/com/sonic/agent/maps/GlobalProcessMap.java b/src/main/java/org/cloud/sonic/agent/maps/GlobalProcessMap.java similarity index 88% rename from src/main/java/com/sonic/agent/maps/GlobalProcessMap.java rename to src/main/java/org/cloud/sonic/agent/maps/GlobalProcessMap.java index 10118663..10d2b05f 100644 --- a/src/main/java/com/sonic/agent/maps/GlobalProcessMap.java +++ b/src/main/java/org/cloud/sonic/agent/maps/GlobalProcessMap.java @@ -1,4 +1,4 @@ -package com.sonic.agent.maps; +package org.cloud.sonic.agent.maps; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; diff --git a/src/main/java/com/sonic/agent/maps/HandlerMap.java b/src/main/java/org/cloud/sonic/agent/maps/HandlerMap.java similarity index 77% rename from src/main/java/com/sonic/agent/maps/HandlerMap.java rename to src/main/java/org/cloud/sonic/agent/maps/HandlerMap.java index c0d188a3..ce29537b 100644 --- a/src/main/java/com/sonic/agent/maps/HandlerMap.java +++ b/src/main/java/org/cloud/sonic/agent/maps/HandlerMap.java @@ -1,19 +1,19 @@ -package com.sonic.agent.maps; - -import com.sonic.agent.automation.AndroidStepHandler; -import com.sonic.agent.automation.IOSStepHandler; - -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -public class HandlerMap { - private static Map androidHandlerMap = new ConcurrentHashMap(); - public static Map getAndroidMap() { - return androidHandlerMap; - } - - private static Map iosHandlerMap = new ConcurrentHashMap(); - public static Map getIOSMap() { - return iosHandlerMap; - } -} +package org.cloud.sonic.agent.maps; + +import org.cloud.sonic.agent.automation.AndroidStepHandler; +import org.cloud.sonic.agent.automation.IOSStepHandler; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class HandlerMap { + private static Map androidHandlerMap = new ConcurrentHashMap(); + public static Map getAndroidMap() { + return androidHandlerMap; + } + + private static Map iosHandlerMap = new ConcurrentHashMap(); + public static Map getIOSMap() { + return iosHandlerMap; + } +} diff --git a/src/main/java/com/sonic/agent/maps/IOSDeviceManagerMap.java b/src/main/java/org/cloud/sonic/agent/maps/IOSDeviceManagerMap.java similarity index 89% rename from src/main/java/com/sonic/agent/maps/IOSDeviceManagerMap.java rename to src/main/java/org/cloud/sonic/agent/maps/IOSDeviceManagerMap.java index 46d04bbe..bc722405 100644 --- a/src/main/java/com/sonic/agent/maps/IOSDeviceManagerMap.java +++ b/src/main/java/org/cloud/sonic/agent/maps/IOSDeviceManagerMap.java @@ -1,16 +1,16 @@ -package com.sonic.agent.maps; - -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -/** - * @author ZhouYiXun - * @des 本地自己维护一下设备状态 - * @date 2021/08/16 10:26 - */ -public class IOSDeviceManagerMap { - private static Map deviceStatusMap = new ConcurrentHashMap(); - public static Map getMap() { - return deviceStatusMap; - } -} +package org.cloud.sonic.agent.maps; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @author ZhouYiXun + * @des 本地自己维护一下设备状态 + * @date 2021/08/16 10:26 + */ +public class IOSDeviceManagerMap { + private static Map deviceStatusMap = new ConcurrentHashMap(); + public static Map getMap() { + return deviceStatusMap; + } +} diff --git a/src/main/java/com/sonic/agent/maps/IOSProcessMap.java b/src/main/java/org/cloud/sonic/agent/maps/IOSProcessMap.java similarity index 89% rename from src/main/java/com/sonic/agent/maps/IOSProcessMap.java rename to src/main/java/org/cloud/sonic/agent/maps/IOSProcessMap.java index fafe253a..1af8024a 100644 --- a/src/main/java/com/sonic/agent/maps/IOSProcessMap.java +++ b/src/main/java/org/cloud/sonic/agent/maps/IOSProcessMap.java @@ -1,4 +1,4 @@ -package com.sonic.agent.maps; +package org.cloud.sonic.agent.maps; import java.util.List; import java.util.Map; diff --git a/src/main/java/com/sonic/agent/maps/IOSSizeMap.java b/src/main/java/org/cloud/sonic/agent/maps/IOSSizeMap.java similarity index 88% rename from src/main/java/com/sonic/agent/maps/IOSSizeMap.java rename to src/main/java/org/cloud/sonic/agent/maps/IOSSizeMap.java index 1a89d365..084f5d36 100644 --- a/src/main/java/com/sonic/agent/maps/IOSSizeMap.java +++ b/src/main/java/org/cloud/sonic/agent/maps/IOSSizeMap.java @@ -1,4 +1,4 @@ -package com.sonic.agent.maps; +package org.cloud.sonic.agent.maps; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; diff --git a/src/main/java/com/sonic/agent/maps/MiniCapMap.java b/src/main/java/org/cloud/sonic/agent/maps/MiniCapMap.java similarity index 90% rename from src/main/java/com/sonic/agent/maps/MiniCapMap.java rename to src/main/java/org/cloud/sonic/agent/maps/MiniCapMap.java index 83fe5c89..b1692f24 100644 --- a/src/main/java/com/sonic/agent/maps/MiniCapMap.java +++ b/src/main/java/org/cloud/sonic/agent/maps/MiniCapMap.java @@ -1,4 +1,4 @@ -package com.sonic.agent.maps; +package org.cloud.sonic.agent.maps; import javax.websocket.Session; import java.util.Map; diff --git a/src/main/java/com/sonic/agent/maps/WebSocketSessionMap.java b/src/main/java/org/cloud/sonic/agent/maps/WebSocketSessionMap.java similarity index 96% rename from src/main/java/com/sonic/agent/maps/WebSocketSessionMap.java rename to src/main/java/org/cloud/sonic/agent/maps/WebSocketSessionMap.java index a9078f75..e46ca6f8 100644 --- a/src/main/java/com/sonic/agent/maps/WebSocketSessionMap.java +++ b/src/main/java/org/cloud/sonic/agent/maps/WebSocketSessionMap.java @@ -1,4 +1,4 @@ -package com.sonic.agent.maps; +package org.cloud.sonic.agent.maps; import org.springframework.lang.NonNull; import org.springframework.util.Assert; diff --git a/src/main/java/com/sonic/agent/netty/MarshallingCodeCFactory.java b/src/main/java/org/cloud/sonic/agent/netty/MarshallingCodeCFactory.java similarity index 97% rename from src/main/java/com/sonic/agent/netty/MarshallingCodeCFactory.java rename to src/main/java/org/cloud/sonic/agent/netty/MarshallingCodeCFactory.java index 9a33038a..09bd5d88 100644 --- a/src/main/java/com/sonic/agent/netty/MarshallingCodeCFactory.java +++ b/src/main/java/org/cloud/sonic/agent/netty/MarshallingCodeCFactory.java @@ -1,4 +1,4 @@ -package com.sonic.agent.netty; +package org.cloud.sonic.agent.netty; import io.netty.handler.codec.marshalling.*; import org.jboss.marshalling.MarshallerFactory; diff --git a/src/main/java/com/sonic/agent/netty/NettyClient.java b/src/main/java/org/cloud/sonic/agent/netty/NettyClient.java similarity index 93% rename from src/main/java/com/sonic/agent/netty/NettyClient.java rename to src/main/java/org/cloud/sonic/agent/netty/NettyClient.java index 55245ae0..9292c17b 100644 --- a/src/main/java/com/sonic/agent/netty/NettyClient.java +++ b/src/main/java/org/cloud/sonic/agent/netty/NettyClient.java @@ -1,8 +1,8 @@ -package com.sonic.agent.netty; +package org.cloud.sonic.agent.netty; -import com.sonic.agent.bridge.android.AndroidDeviceBridgeTool; -import com.sonic.agent.bridge.ios.TIDeviceTool; -import com.sonic.agent.tools.SpringTool; +import org.cloud.sonic.agent.bridge.android.AndroidDeviceBridgeTool; +import org.cloud.sonic.agent.bridge.ios.TIDeviceTool; +import org.cloud.sonic.agent.tools.SpringTool; import io.netty.bootstrap.Bootstrap; import io.netty.channel.*; import io.netty.channel.nio.NioEventLoopGroup; @@ -14,7 +14,6 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.ApplicationArguments; import org.springframework.boot.ApplicationRunner; -import org.springframework.context.annotation.DependsOn; import org.springframework.stereotype.Component; import java.util.concurrent.TimeUnit; diff --git a/src/main/java/com/sonic/agent/netty/NettyClientHandler.java b/src/main/java/org/cloud/sonic/agent/netty/NettyClientHandler.java similarity index 93% rename from src/main/java/com/sonic/agent/netty/NettyClientHandler.java rename to src/main/java/org/cloud/sonic/agent/netty/NettyClientHandler.java index 0e80454a..d736984c 100644 --- a/src/main/java/com/sonic/agent/netty/NettyClientHandler.java +++ b/src/main/java/org/cloud/sonic/agent/netty/NettyClientHandler.java @@ -1,25 +1,24 @@ -package com.sonic.agent.netty; +package org.cloud.sonic.agent.netty; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.android.ddmlib.IDevice; -import com.sonic.agent.automation.AndroidStepHandler; -import com.sonic.agent.automation.IOSStepHandler; -import com.sonic.agent.bridge.android.AndroidDeviceBridgeTool; -import com.sonic.agent.bridge.ios.TIDeviceTool; -import com.sonic.agent.interfaces.PlatformType; -import com.sonic.agent.maps.AndroidPasswordMap; -import com.sonic.agent.maps.HandlerMap; -import com.sonic.agent.tests.AndroidTests; -import com.sonic.agent.tests.IOSTests; -import com.sonic.agent.tests.TaskManager; +import org.cloud.sonic.agent.automation.AndroidStepHandler; +import org.cloud.sonic.agent.automation.IOSStepHandler; +import org.cloud.sonic.agent.bridge.android.AndroidDeviceBridgeTool; +import org.cloud.sonic.agent.bridge.ios.TIDeviceTool; +import org.cloud.sonic.agent.interfaces.PlatformType; +import org.cloud.sonic.agent.maps.AndroidPasswordMap; +import org.cloud.sonic.agent.maps.HandlerMap; +import org.cloud.sonic.agent.tests.AndroidTests; +import org.cloud.sonic.agent.tests.IOSTests; +import org.cloud.sonic.agent.tests.TaskManager; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.stereotype.Component; import org.testng.TestNG; import org.testng.xml.XmlClass; import org.testng.xml.XmlSuite; diff --git a/src/main/java/com/sonic/agent/netty/NettyThreadPool.java b/src/main/java/org/cloud/sonic/agent/netty/NettyThreadPool.java similarity index 96% rename from src/main/java/com/sonic/agent/netty/NettyThreadPool.java rename to src/main/java/org/cloud/sonic/agent/netty/NettyThreadPool.java index 14153385..38691c6e 100644 --- a/src/main/java/com/sonic/agent/netty/NettyThreadPool.java +++ b/src/main/java/org/cloud/sonic/agent/netty/NettyThreadPool.java @@ -1,7 +1,7 @@ -package com.sonic.agent.netty; +package org.cloud.sonic.agent.netty; import com.alibaba.fastjson.JSONObject; -import com.sonic.agent.tools.AgentTool; +import org.cloud.sonic.agent.tools.AgentTool; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Bean; diff --git a/src/main/java/com/sonic/agent/netty/SecurityHandler.java b/src/main/java/org/cloud/sonic/agent/netty/SecurityHandler.java similarity index 97% rename from src/main/java/com/sonic/agent/netty/SecurityHandler.java rename to src/main/java/org/cloud/sonic/agent/netty/SecurityHandler.java index ce9f1fb8..52be2215 100644 --- a/src/main/java/com/sonic/agent/netty/SecurityHandler.java +++ b/src/main/java/org/cloud/sonic/agent/netty/SecurityHandler.java @@ -1,8 +1,8 @@ -package com.sonic.agent.netty; +package org.cloud.sonic.agent.netty; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; -import com.sonic.agent.tools.AgentTool; +import org.cloud.sonic.agent.tools.AgentTool; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; diff --git a/src/main/java/com/sonic/agent/tests/AndroidTests.java b/src/main/java/org/cloud/sonic/agent/tests/AndroidTests.java similarity index 88% rename from src/main/java/com/sonic/agent/tests/AndroidTests.java rename to src/main/java/org/cloud/sonic/agent/tests/AndroidTests.java index 3884d7b8..b1c903c9 100644 --- a/src/main/java/com/sonic/agent/tests/AndroidTests.java +++ b/src/main/java/org/cloud/sonic/agent/tests/AndroidTests.java @@ -1,66 +1,66 @@ -package com.sonic.agent.tests; - -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.JSONObject; -import com.sonic.agent.automation.AndroidStepHandler; -import com.sonic.agent.bridge.android.AndroidDeviceBridgeTool; -import com.sonic.agent.interfaces.DeviceStatus; -import com.sonic.agent.tests.android.AndroidTestTaskBootThread; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.testng.ITestContext; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -/** - * @author ZhouYiXun - * @des 安卓测试执行类 - * @date 2021/8/25 20:50 - */ -public class AndroidTests { - private final Logger logger = LoggerFactory.getLogger(AndroidTests.class); - - @DataProvider(name = "testData", parallel = true) - public Object[][] getTestData(ITestContext context) { - JSONObject dataInfo = JSON.parseObject(context.getCurrentXmlTest().getParameter("dataInfo")); - List dataProvider = new ArrayList<>(); - for (JSONObject iDevice : dataInfo.getJSONArray("device").toJavaList(JSONObject.class)) { - String udId = iDevice.getString("udId"); - if (AndroidDeviceBridgeTool.getIDeviceByUdId(udId) == null || !AndroidDeviceBridgeTool.getIDeviceByUdId(udId) - .getState().toString().equals("ONLINE")) { - continue; - } - JSONObject deviceTestData = new JSONObject(); - deviceTestData.put("steps", dataInfo.getJSONArray("steps")); - deviceTestData.put("rid", dataInfo.getInteger("rid")); - deviceTestData.put("cid", dataInfo.getInteger("cid")); - deviceTestData.put("gp", dataInfo.getJSONObject("gp")); - deviceTestData.put("device", iDevice); - dataProvider.add(deviceTestData); - } - Object[][] testDataProvider = new Object[dataProvider.size()][]; - for (int i = 0; i < dataProvider.size(); i++) { - testDataProvider[i] = new Object[]{dataProvider.get(i)}; - } - return testDataProvider; - } - - @Test(dataProvider = "testData") - public void run(JSONObject jsonObject) throws IOException { - AndroidStepHandler androidStepHandler = new AndroidStepHandler(); - int rid = jsonObject.getInteger("rid"); - int cid = jsonObject.getInteger("cid"); - String udId = jsonObject.getJSONObject("device").getString("udId"); - JSONObject gp = jsonObject.getJSONObject("gp"); - androidStepHandler.setGlobalParams(gp); - androidStepHandler.setTestMode(cid, rid, udId, DeviceStatus.TESTING, ""); - - // 启动任务 - AndroidTestTaskBootThread bootThread = new AndroidTestTaskBootThread(jsonObject, androidStepHandler); - TaskManager.startBootThread(bootThread); - } -} +package org.cloud.sonic.agent.tests; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import org.cloud.sonic.agent.automation.AndroidStepHandler; +import org.cloud.sonic.agent.bridge.android.AndroidDeviceBridgeTool; +import org.cloud.sonic.agent.interfaces.DeviceStatus; +import org.cloud.sonic.agent.tests.android.AndroidTestTaskBootThread; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.ITestContext; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * @author ZhouYiXun + * @des 安卓测试执行类 + * @date 2021/8/25 20:50 + */ +public class AndroidTests { + private final Logger logger = LoggerFactory.getLogger(AndroidTests.class); + + @DataProvider(name = "testData", parallel = true) + public Object[][] getTestData(ITestContext context) { + JSONObject dataInfo = JSON.parseObject(context.getCurrentXmlTest().getParameter("dataInfo")); + List dataProvider = new ArrayList<>(); + for (JSONObject iDevice : dataInfo.getJSONArray("device").toJavaList(JSONObject.class)) { + String udId = iDevice.getString("udId"); + if (AndroidDeviceBridgeTool.getIDeviceByUdId(udId) == null || !AndroidDeviceBridgeTool.getIDeviceByUdId(udId) + .getState().toString().equals("ONLINE")) { + continue; + } + JSONObject deviceTestData = new JSONObject(); + deviceTestData.put("steps", dataInfo.getJSONArray("steps")); + deviceTestData.put("rid", dataInfo.getInteger("rid")); + deviceTestData.put("cid", dataInfo.getInteger("cid")); + deviceTestData.put("gp", dataInfo.getJSONObject("gp")); + deviceTestData.put("device", iDevice); + dataProvider.add(deviceTestData); + } + Object[][] testDataProvider = new Object[dataProvider.size()][]; + for (int i = 0; i < dataProvider.size(); i++) { + testDataProvider[i] = new Object[]{dataProvider.get(i)}; + } + return testDataProvider; + } + + @Test(dataProvider = "testData") + public void run(JSONObject jsonObject) throws IOException { + AndroidStepHandler androidStepHandler = new AndroidStepHandler(); + int rid = jsonObject.getInteger("rid"); + int cid = jsonObject.getInteger("cid"); + String udId = jsonObject.getJSONObject("device").getString("udId"); + JSONObject gp = jsonObject.getJSONObject("gp"); + androidStepHandler.setGlobalParams(gp); + androidStepHandler.setTestMode(cid, rid, udId, DeviceStatus.TESTING, ""); + + // 启动任务 + AndroidTestTaskBootThread bootThread = new AndroidTestTaskBootThread(jsonObject, androidStepHandler); + TaskManager.startBootThread(bootThread); + } +} diff --git a/src/main/java/com/sonic/agent/tests/IOSTests.java b/src/main/java/org/cloud/sonic/agent/tests/IOSTests.java similarity index 89% rename from src/main/java/com/sonic/agent/tests/IOSTests.java rename to src/main/java/org/cloud/sonic/agent/tests/IOSTests.java index 01415c56..740310d1 100644 --- a/src/main/java/com/sonic/agent/tests/IOSTests.java +++ b/src/main/java/org/cloud/sonic/agent/tests/IOSTests.java @@ -1,65 +1,65 @@ -package com.sonic.agent.tests; - -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.JSONObject; -import com.sonic.agent.automation.IOSStepHandler; -import com.sonic.agent.bridge.ios.TIDeviceTool; -import com.sonic.agent.interfaces.DeviceStatus; -import com.sonic.agent.tests.ios.IOSTestTaskBootThread; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.testng.ITestContext; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -/** - * @author ZhouYiXun - * @des iOS测试执行类 - * @date 2021/8/25 20:51 - */ -public class IOSTests { - private final Logger logger = LoggerFactory.getLogger(IOSTests.class); - - @DataProvider(name = "testData", parallel = true) - public Object[][] getTestData(ITestContext context) { - JSONObject dataInfo = JSON.parseObject(context.getCurrentXmlTest().getParameter("dataInfo")); - List dataProvider = new ArrayList<>(); - for (JSONObject device : dataInfo.getJSONArray("device").toJavaList(JSONObject.class)) { - String udId = device.getString("udId"); - if (!TIDeviceTool.getDeviceList().contains(udId)) { - continue; - } - JSONObject deviceTestData = new JSONObject(); - deviceTestData.put("steps", dataInfo.getJSONArray("steps")); - deviceTestData.put("rid", dataInfo.getInteger("rid")); - deviceTestData.put("cid", dataInfo.getInteger("cid")); - deviceTestData.put("gp", dataInfo.getJSONObject("gp")); - deviceTestData.put("device", device); - dataProvider.add(deviceTestData); - } - Object[][] testDataProvider = new Object[dataProvider.size()][]; - for (int i = 0; i < dataProvider.size(); i++) { - testDataProvider[i] = new Object[]{dataProvider.get(i)}; - } - return testDataProvider; - } - - @Test(dataProvider = "testData") - public void run(JSONObject jsonObject) throws IOException { - IOSStepHandler iosStepHandler = new IOSStepHandler(); - int rid = jsonObject.getInteger("rid"); - int cid = jsonObject.getInteger("cid"); - String udId = jsonObject.getJSONObject("device").getString("udId"); - JSONObject gp = jsonObject.getJSONObject("gp"); - iosStepHandler.setGlobalParams(gp); - iosStepHandler.setTestMode(cid, rid, udId, DeviceStatus.TESTING, ""); - - // 启动任务 - IOSTestTaskBootThread bootThread = new IOSTestTaskBootThread(jsonObject, iosStepHandler); - TaskManager.startBootThread(bootThread); - } -} +package org.cloud.sonic.agent.tests; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import org.cloud.sonic.agent.automation.IOSStepHandler; +import org.cloud.sonic.agent.bridge.ios.TIDeviceTool; +import org.cloud.sonic.agent.interfaces.DeviceStatus; +import org.cloud.sonic.agent.tests.ios.IOSTestTaskBootThread; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.ITestContext; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * @author ZhouYiXun + * @des iOS测试执行类 + * @date 2021/8/25 20:51 + */ +public class IOSTests { + private final Logger logger = LoggerFactory.getLogger(IOSTests.class); + + @DataProvider(name = "testData", parallel = true) + public Object[][] getTestData(ITestContext context) { + JSONObject dataInfo = JSON.parseObject(context.getCurrentXmlTest().getParameter("dataInfo")); + List dataProvider = new ArrayList<>(); + for (JSONObject device : dataInfo.getJSONArray("device").toJavaList(JSONObject.class)) { + String udId = device.getString("udId"); + if (!TIDeviceTool.getDeviceList().contains(udId)) { + continue; + } + JSONObject deviceTestData = new JSONObject(); + deviceTestData.put("steps", dataInfo.getJSONArray("steps")); + deviceTestData.put("rid", dataInfo.getInteger("rid")); + deviceTestData.put("cid", dataInfo.getInteger("cid")); + deviceTestData.put("gp", dataInfo.getJSONObject("gp")); + deviceTestData.put("device", device); + dataProvider.add(deviceTestData); + } + Object[][] testDataProvider = new Object[dataProvider.size()][]; + for (int i = 0; i < dataProvider.size(); i++) { + testDataProvider[i] = new Object[]{dataProvider.get(i)}; + } + return testDataProvider; + } + + @Test(dataProvider = "testData") + public void run(JSONObject jsonObject) throws IOException { + IOSStepHandler iosStepHandler = new IOSStepHandler(); + int rid = jsonObject.getInteger("rid"); + int cid = jsonObject.getInteger("cid"); + String udId = jsonObject.getJSONObject("device").getString("udId"); + JSONObject gp = jsonObject.getJSONObject("gp"); + iosStepHandler.setGlobalParams(gp); + iosStepHandler.setTestMode(cid, rid, udId, DeviceStatus.TESTING, ""); + + // 启动任务 + IOSTestTaskBootThread bootThread = new IOSTestTaskBootThread(jsonObject, iosStepHandler); + TaskManager.startBootThread(bootThread); + } +} diff --git a/src/main/java/com/sonic/agent/tests/TaskManager.java b/src/main/java/org/cloud/sonic/agent/tests/TaskManager.java similarity index 96% rename from src/main/java/com/sonic/agent/tests/TaskManager.java rename to src/main/java/org/cloud/sonic/agent/tests/TaskManager.java index e24eee4d..bce08587 100644 --- a/src/main/java/com/sonic/agent/tests/TaskManager.java +++ b/src/main/java/org/cloud/sonic/agent/tests/TaskManager.java @@ -1,8 +1,8 @@ -package com.sonic.agent.tests; +package org.cloud.sonic.agent.tests; -import com.sonic.agent.interfaces.PlatformType; -import com.sonic.agent.tests.android.AndroidTestTaskBootThread; -import com.sonic.agent.tests.ios.IOSTestTaskBootThread; +import org.cloud.sonic.agent.interfaces.PlatformType; +import org.cloud.sonic.agent.tests.android.AndroidTestTaskBootThread; +import org.cloud.sonic.agent.tests.ios.IOSTestTaskBootThread; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.util.CollectionUtils; diff --git a/src/main/java/com/sonic/agent/tests/android/AndroidPerfDataThread.java b/src/main/java/org/cloud/sonic/agent/tests/android/AndroidPerfDataThread.java similarity index 94% rename from src/main/java/com/sonic/agent/tests/android/AndroidPerfDataThread.java rename to src/main/java/org/cloud/sonic/agent/tests/android/AndroidPerfDataThread.java index 63b59903..ce31b880 100644 --- a/src/main/java/com/sonic/agent/tests/android/AndroidPerfDataThread.java +++ b/src/main/java/org/cloud/sonic/agent/tests/android/AndroidPerfDataThread.java @@ -1,6 +1,6 @@ -package com.sonic.agent.tests.android; +package org.cloud.sonic.agent.tests.android; -import com.sonic.agent.automation.AndroidStepHandler; +import org.cloud.sonic.agent.automation.AndroidStepHandler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/main/java/com/sonic/agent/tests/android/AndroidRecordThread.java b/src/main/java/org/cloud/sonic/agent/tests/android/AndroidRecordThread.java similarity index 94% rename from src/main/java/com/sonic/agent/tests/android/AndroidRecordThread.java rename to src/main/java/org/cloud/sonic/agent/tests/android/AndroidRecordThread.java index 18e10622..e188bc7b 100644 --- a/src/main/java/com/sonic/agent/tests/android/AndroidRecordThread.java +++ b/src/main/java/org/cloud/sonic/agent/tests/android/AndroidRecordThread.java @@ -1,10 +1,10 @@ -package com.sonic.agent.tests.android; +package org.cloud.sonic.agent.tests.android; import com.android.ddmlib.IDevice; -import com.sonic.agent.automation.AndroidStepHandler; -import com.sonic.agent.bridge.android.AndroidDeviceBridgeTool; -import com.sonic.agent.tools.RecordHandler; -import com.sonic.agent.tools.MiniCapTool; +import org.cloud.sonic.agent.automation.AndroidStepHandler; +import org.cloud.sonic.agent.bridge.android.AndroidDeviceBridgeTool; +import org.cloud.sonic.agent.tools.RecordHandler; +import org.cloud.sonic.agent.tools.MiniCapTool; import org.bytedeco.javacv.FrameRecorder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/main/java/com/sonic/agent/tests/android/AndroidRunStepThread.java b/src/main/java/org/cloud/sonic/agent/tests/android/AndroidRunStepThread.java similarity index 93% rename from src/main/java/com/sonic/agent/tests/android/AndroidRunStepThread.java rename to src/main/java/org/cloud/sonic/agent/tests/android/AndroidRunStepThread.java index 76415c82..3d53cb85 100644 --- a/src/main/java/com/sonic/agent/tests/android/AndroidRunStepThread.java +++ b/src/main/java/org/cloud/sonic/agent/tests/android/AndroidRunStepThread.java @@ -1,7 +1,7 @@ -package com.sonic.agent.tests.android; +package org.cloud.sonic.agent.tests.android; import com.alibaba.fastjson.JSONObject; -import com.sonic.agent.automation.AndroidStepHandler; +import org.cloud.sonic.agent.automation.AndroidStepHandler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/main/java/com/sonic/agent/tests/android/AndroidTemperThread.java b/src/main/java/org/cloud/sonic/agent/tests/android/AndroidTemperThread.java similarity index 88% rename from src/main/java/com/sonic/agent/tests/android/AndroidTemperThread.java rename to src/main/java/org/cloud/sonic/agent/tests/android/AndroidTemperThread.java index 8e287fd6..1e41d45f 100644 --- a/src/main/java/com/sonic/agent/tests/android/AndroidTemperThread.java +++ b/src/main/java/org/cloud/sonic/agent/tests/android/AndroidTemperThread.java @@ -1,10 +1,10 @@ -package com.sonic.agent.tests.android; +package org.cloud.sonic.agent.tests.android; import com.alibaba.fastjson.JSONObject; import com.android.ddmlib.IDevice; -import com.sonic.agent.bridge.android.AndroidDeviceBridgeTool; -import com.sonic.agent.netty.NettyClientHandler; -import com.sonic.agent.netty.NettyThreadPool; +import org.cloud.sonic.agent.bridge.android.AndroidDeviceBridgeTool; +import org.cloud.sonic.agent.netty.NettyClientHandler; +import org.cloud.sonic.agent.netty.NettyThreadPool; import org.springframework.util.StringUtils; import java.util.ArrayList; diff --git a/src/main/java/com/sonic/agent/tests/android/AndroidTestTaskBootThread.java b/src/main/java/org/cloud/sonic/agent/tests/android/AndroidTestTaskBootThread.java similarity index 95% rename from src/main/java/com/sonic/agent/tests/android/AndroidTestTaskBootThread.java rename to src/main/java/org/cloud/sonic/agent/tests/android/AndroidTestTaskBootThread.java index fc919f0b..c81d3438 100644 --- a/src/main/java/com/sonic/agent/tests/android/AndroidTestTaskBootThread.java +++ b/src/main/java/org/cloud/sonic/agent/tests/android/AndroidTestTaskBootThread.java @@ -1,10 +1,10 @@ -package com.sonic.agent.tests.android; +package org.cloud.sonic.agent.tests.android; import com.alibaba.fastjson.JSONObject; -import com.sonic.agent.automation.AndroidStepHandler; -import com.sonic.agent.bridge.android.AndroidDeviceLocalStatus; -import com.sonic.agent.interfaces.ResultDetailStatus; -import com.sonic.agent.tests.TaskManager; +import org.cloud.sonic.agent.automation.AndroidStepHandler; +import org.cloud.sonic.agent.bridge.android.AndroidDeviceLocalStatus; +import org.cloud.sonic.agent.interfaces.ResultDetailStatus; +import org.cloud.sonic.agent.tests.TaskManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/main/java/com/sonic/agent/tests/android/InputSocketThread.java b/src/main/java/org/cloud/sonic/agent/tests/android/InputSocketThread.java similarity index 93% rename from src/main/java/com/sonic/agent/tests/android/InputSocketThread.java rename to src/main/java/org/cloud/sonic/agent/tests/android/InputSocketThread.java index 4cde3f9e..d55b5982 100644 --- a/src/main/java/com/sonic/agent/tests/android/InputSocketThread.java +++ b/src/main/java/org/cloud/sonic/agent/tests/android/InputSocketThread.java @@ -1,9 +1,9 @@ -package com.sonic.agent.tests.android; +package org.cloud.sonic.agent.tests.android; import com.android.ddmlib.IDevice; -import com.sonic.agent.bridge.android.AndroidDeviceBridgeTool; -import com.sonic.agent.maps.MiniCapMap; -import com.sonic.agent.tools.PortTool; +import org.cloud.sonic.agent.bridge.android.AndroidDeviceBridgeTool; +import org.cloud.sonic.agent.maps.MiniCapMap; +import org.cloud.sonic.agent.tools.PortTool; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -11,10 +11,9 @@ import java.io.IOException; import java.io.InputStream; import java.net.Socket; -import java.util.Queue; import java.util.concurrent.BlockingQueue; -import static com.sonic.agent.tools.AgentTool.subByteArray; +import static org.cloud.sonic.agent.tools.AgentTool.subByteArray; /** * minicap socket线程 diff --git a/src/main/java/com/sonic/agent/tests/android/OutputSocketThread.java b/src/main/java/org/cloud/sonic/agent/tests/android/OutputSocketThread.java similarity index 86% rename from src/main/java/com/sonic/agent/tests/android/OutputSocketThread.java rename to src/main/java/org/cloud/sonic/agent/tests/android/OutputSocketThread.java index c0016c1e..907e8f12 100644 --- a/src/main/java/com/sonic/agent/tests/android/OutputSocketThread.java +++ b/src/main/java/org/cloud/sonic/agent/tests/android/OutputSocketThread.java @@ -1,18 +1,16 @@ -package com.sonic.agent.tests.android; +package org.cloud.sonic.agent.tests.android; import com.alibaba.fastjson.JSONObject; +import org.cloud.sonic.agent.tools.AgentTool; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.websocket.Session; import java.util.Arrays; import java.util.List; -import java.util.Queue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.atomic.AtomicReference; -import static com.sonic.agent.tools.AgentTool.*; - /** * 视频流输出线程 * @@ -101,31 +99,31 @@ public void run() { case 3: case 4: case 5: - banner.get()[5] = bytesToLong(buffer, 2) + ""; + banner.get()[5] = AgentTool.bytesToLong(buffer, 2) + ""; break; case 6: case 7: case 8: case 9: - banner.get()[9] = bytesToLong(buffer, 6) + ""; + banner.get()[9] = AgentTool.bytesToLong(buffer, 6) + ""; break; case 10: case 11: case 12: case 13: - banner.get()[13] = bytesToLong(buffer, 10) + ""; + banner.get()[13] = AgentTool.bytesToLong(buffer, 10) + ""; break; case 14: case 15: case 16: case 17: - banner.get()[17] = bytesToLong(buffer, 14) + ""; + banner.get()[17] = AgentTool.bytesToLong(buffer, 14) + ""; break; case 18: case 19: case 20: case 21: - banner.get()[21] = bytesToLong(buffer, 18) + ""; + banner.get()[21] = AgentTool.bytesToLong(buffer, 18) + ""; break; case 22: banner.get()[22] += buffer[cursor] * 90; @@ -144,7 +142,7 @@ public void run() { size.put("msg", "size"); size.put("width", banner.get()[9]); size.put("height", banner.get()[13]); - sendText(session, size.toJSONString()); + AgentTool.sendText(session, size.toJSONString()); } } } else if (readFrameBytes < 4) {//读取并设置图片的大小 @@ -153,13 +151,13 @@ public void run() { readFrameBytes += 1; } else { if (len - cursor >= frameBodyLength) { - byte[] subByte = subByteArray(buffer, cursor, + byte[] subByte = AgentTool.subByteArray(buffer, cursor, cursor + frameBodyLength); - frameBody = addBytes(frameBody, subByte); + frameBody = AgentTool.addBytes(frameBody, subByte); if ((frameBody[0] != -1) || frameBody[1] != -40) { return; } - final byte[] finalBytes = subByteArray(frameBody, + final byte[] finalBytes = AgentTool.subByteArray(frameBody, 0, frameBody.length); if (sessionOpen()) { if (!Arrays.equals(oldBytes, finalBytes)) { @@ -177,7 +175,7 @@ public void run() { if (count % 4 == 0) { count = 0; oldBytes = finalBytes; - sendByte(session, finalBytes); + AgentTool.sendByte(session, finalBytes); } } } @@ -189,8 +187,8 @@ public void run() { readFrameBytes = 0; frameBody = new byte[0]; } else { - byte[] subByte = subByteArray(buffer, cursor, len); - frameBody = addBytes(frameBody, subByte); + byte[] subByte = AgentTool.subByteArray(buffer, cursor, len); + frameBody = AgentTool.addBytes(frameBody, subByte); frameBodyLength -= (len - cursor); readFrameBytes += (len - cursor); cursor = len; diff --git a/src/main/java/com/sonic/agent/tests/android/SonicLocalThread.java b/src/main/java/org/cloud/sonic/agent/tests/android/SonicLocalThread.java similarity index 96% rename from src/main/java/com/sonic/agent/tests/android/SonicLocalThread.java rename to src/main/java/org/cloud/sonic/agent/tests/android/SonicLocalThread.java index af2be4b2..bf77bda4 100644 --- a/src/main/java/com/sonic/agent/tests/android/SonicLocalThread.java +++ b/src/main/java/org/cloud/sonic/agent/tests/android/SonicLocalThread.java @@ -1,9 +1,10 @@ -package com.sonic.agent.tests.android; +package org.cloud.sonic.agent.tests.android; import com.alibaba.fastjson.JSONObject; import com.android.ddmlib.IDevice; import com.android.ddmlib.IShellOutputReceiver; -import com.sonic.agent.bridge.android.AndroidDeviceBridgeTool; +import org.cloud.sonic.agent.bridge.android.AndroidDeviceBridgeTool; +import org.cloud.sonic.agent.tools.AgentTool; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -13,8 +14,6 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; -import static com.sonic.agent.tools.AgentTool.sendText; - /** * 启动minicap等服务的线程 * @@ -178,7 +177,7 @@ public void run() { JSONObject support = new JSONObject(); support.put("msg", "support"); support.put("text", "该设备不兼容MiniCap投屏!"); - sendText(session, support.toJSONString()); + AgentTool.sendText(session, support.toJSONString()); } } diff --git a/src/main/java/com/sonic/agent/tests/ios/IOSPerfDataThread.java b/src/main/java/org/cloud/sonic/agent/tests/ios/IOSPerfDataThread.java similarity index 78% rename from src/main/java/com/sonic/agent/tests/ios/IOSPerfDataThread.java rename to src/main/java/org/cloud/sonic/agent/tests/ios/IOSPerfDataThread.java index b3993396..6bbe667b 100644 --- a/src/main/java/com/sonic/agent/tests/ios/IOSPerfDataThread.java +++ b/src/main/java/org/cloud/sonic/agent/tests/ios/IOSPerfDataThread.java @@ -1,8 +1,5 @@ -package com.sonic.agent.tests.ios; +package org.cloud.sonic.agent.tests.ios; -import com.sonic.agent.automation.AndroidStepHandler; -import com.sonic.agent.tests.android.AndroidRunStepThread; -import com.sonic.agent.tests.android.AndroidTestTaskBootThread; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/main/java/com/sonic/agent/tests/ios/IOSRecordThread.java b/src/main/java/org/cloud/sonic/agent/tests/ios/IOSRecordThread.java similarity index 93% rename from src/main/java/com/sonic/agent/tests/ios/IOSRecordThread.java rename to src/main/java/org/cloud/sonic/agent/tests/ios/IOSRecordThread.java index 2cbc640b..39fcb142 100644 --- a/src/main/java/com/sonic/agent/tests/ios/IOSRecordThread.java +++ b/src/main/java/org/cloud/sonic/agent/tests/ios/IOSRecordThread.java @@ -1,7 +1,6 @@ -package com.sonic.agent.tests.ios; +package org.cloud.sonic.agent.tests.ios; -import com.sonic.agent.automation.IOSStepHandler; -import org.bytedeco.javacv.FrameRecorder; +import org.cloud.sonic.agent.automation.IOSStepHandler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/main/java/com/sonic/agent/tests/ios/IOSRunStepThread.java b/src/main/java/org/cloud/sonic/agent/tests/ios/IOSRunStepThread.java similarity index 86% rename from src/main/java/com/sonic/agent/tests/ios/IOSRunStepThread.java rename to src/main/java/org/cloud/sonic/agent/tests/ios/IOSRunStepThread.java index 976563f1..4b84bdeb 100644 --- a/src/main/java/com/sonic/agent/tests/ios/IOSRunStepThread.java +++ b/src/main/java/org/cloud/sonic/agent/tests/ios/IOSRunStepThread.java @@ -1,9 +1,7 @@ -package com.sonic.agent.tests.ios; +package org.cloud.sonic.agent.tests.ios; import com.alibaba.fastjson.JSONObject; -import com.sonic.agent.automation.AndroidStepHandler; -import com.sonic.agent.automation.IOSStepHandler; -import com.sonic.agent.tests.android.AndroidTestTaskBootThread; +import org.cloud.sonic.agent.automation.IOSStepHandler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/main/java/com/sonic/agent/tests/ios/IOSTestTaskBootThread.java b/src/main/java/org/cloud/sonic/agent/tests/ios/IOSTestTaskBootThread.java similarity index 94% rename from src/main/java/com/sonic/agent/tests/ios/IOSTestTaskBootThread.java rename to src/main/java/org/cloud/sonic/agent/tests/ios/IOSTestTaskBootThread.java index 5861e17e..f6946b44 100644 --- a/src/main/java/com/sonic/agent/tests/ios/IOSTestTaskBootThread.java +++ b/src/main/java/org/cloud/sonic/agent/tests/ios/IOSTestTaskBootThread.java @@ -1,11 +1,11 @@ -package com.sonic.agent.tests.ios; +package org.cloud.sonic.agent.tests.ios; import com.alibaba.fastjson.JSONObject; -import com.sonic.agent.automation.IOSStepHandler; -import com.sonic.agent.bridge.ios.IOSDeviceLocalStatus; -import com.sonic.agent.bridge.ios.TIDeviceTool; -import com.sonic.agent.interfaces.ResultDetailStatus; -import com.sonic.agent.tests.TaskManager; +import org.cloud.sonic.agent.automation.IOSStepHandler; +import org.cloud.sonic.agent.bridge.ios.IOSDeviceLocalStatus; +import org.cloud.sonic.agent.bridge.ios.TIDeviceTool; +import org.cloud.sonic.agent.interfaces.ResultDetailStatus; +import org.cloud.sonic.agent.tests.TaskManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/main/java/com/sonic/agent/tools/AgentTool.java b/src/main/java/org/cloud/sonic/agent/tools/AgentTool.java similarity index 95% rename from src/main/java/com/sonic/agent/tools/AgentTool.java rename to src/main/java/org/cloud/sonic/agent/tools/AgentTool.java index bd158c88..b721b373 100644 --- a/src/main/java/com/sonic/agent/tools/AgentTool.java +++ b/src/main/java/org/cloud/sonic/agent/tools/AgentTool.java @@ -1,62 +1,62 @@ -package com.sonic.agent.tools; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.websocket.Session; -import java.io.IOException; -import java.nio.ByteBuffer; - -/** - * @author ZhouYiXun - * @des 获取id - * @date 2021/8/26 22:23 - */ -public class AgentTool { - private static final Logger log = LoggerFactory.getLogger(AgentTool.class); - - public static int agentId = 0; - - public static byte[] subByteArray(byte[] byte1, int start, int end) { - byte[] byte2; - byte2 = new byte[end - start]; - System.arraycopy(byte1, start, byte2, 0, end - start); - return byte2; - } - - public static long bytesToLong(byte[] src, int offset) { - long value; - value = ((src[offset] & 0xFF) | ((src[offset + 1] & 0xFF) << 8) | ((src[offset + 2] & 0xFF) << 16) - | ((src[offset + 3] & 0xFF) << 24)); - return value; - } - - // java合并两个byte数组 - public static byte[] addBytes(byte[] data1, byte[] data2) { - byte[] data3 = new byte[data1.length + data2.length]; - System.arraycopy(data1, 0, data3, 0, data1.length); - System.arraycopy(data2, 0, data3, data1.length, data2.length); - return data3; - } - - public static void sendByte(Session session, byte[] message) { - synchronized (session) { - try { - session.getBasicRemote().sendBinary(ByteBuffer.wrap(message)); - } catch (IllegalStateException | IOException e) { - log.error("WebSocket发送失败!连接已关闭!"); - } - } - } - - public static void sendText(Session session, String message) { - synchronized (session) { - try { - session.getBasicRemote().sendText(message); - } catch (IllegalStateException | IOException e) { - log.error("WebSocket发送失败!连接已关闭!"); - } - } - } - -} +package org.cloud.sonic.agent.tools; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.websocket.Session; +import java.io.IOException; +import java.nio.ByteBuffer; + +/** + * @author ZhouYiXun + * @des 获取id + * @date 2021/8/26 22:23 + */ +public class AgentTool { + private static final Logger log = LoggerFactory.getLogger(AgentTool.class); + + public static int agentId = 0; + + public static byte[] subByteArray(byte[] byte1, int start, int end) { + byte[] byte2; + byte2 = new byte[end - start]; + System.arraycopy(byte1, start, byte2, 0, end - start); + return byte2; + } + + public static long bytesToLong(byte[] src, int offset) { + long value; + value = ((src[offset] & 0xFF) | ((src[offset + 1] & 0xFF) << 8) | ((src[offset + 2] & 0xFF) << 16) + | ((src[offset + 3] & 0xFF) << 24)); + return value; + } + + // java合并两个byte数组 + public static byte[] addBytes(byte[] data1, byte[] data2) { + byte[] data3 = new byte[data1.length + data2.length]; + System.arraycopy(data1, 0, data3, 0, data1.length); + System.arraycopy(data2, 0, data3, data1.length, data2.length); + return data3; + } + + public static void sendByte(Session session, byte[] message) { + synchronized (session) { + try { + session.getBasicRemote().sendBinary(ByteBuffer.wrap(message)); + } catch (IllegalStateException | IOException e) { + log.error("WebSocket发送失败!连接已关闭!"); + } + } + } + + public static void sendText(Session session, String message) { + synchronized (session) { + try { + session.getBasicRemote().sendText(message); + } catch (IllegalStateException | IOException e) { + log.error("WebSocket发送失败!连接已关闭!"); + } + } + } + +} diff --git a/src/main/java/com/sonic/agent/tools/DownImageTool.java b/src/main/java/org/cloud/sonic/agent/tools/DownImageTool.java similarity index 94% rename from src/main/java/com/sonic/agent/tools/DownImageTool.java rename to src/main/java/org/cloud/sonic/agent/tools/DownImageTool.java index 80689c8b..e031240f 100644 --- a/src/main/java/com/sonic/agent/tools/DownImageTool.java +++ b/src/main/java/org/cloud/sonic/agent/tools/DownImageTool.java @@ -1,49 +1,49 @@ -package com.sonic.agent.tools; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.net.URLConnection; -import java.util.Calendar; - -public class DownImageTool { - public static File download(String urlString) throws IOException { - URL url = new URL(urlString); - URLConnection con = url.openConnection(); - InputStream is = con.getInputStream(); - byte[] bs = new byte[1024]; - int len; - - long time = Calendar.getInstance().getTimeInMillis(); - String tail = ""; - if (urlString.lastIndexOf(".") != -1) { - tail = urlString.substring(urlString.lastIndexOf(".") + 1); - } - String filename = "test-output" + File.separator + "download-" + time + "." + tail; - File file = new File(filename); - FileOutputStream os = null; - try { - os = new FileOutputStream(file, true); - while ((len = is.read(bs)) != -1) { - os.write(bs, 0, len); - } - } catch (Exception e) { - e.printStackTrace(); - throw e; - } finally { - try { - os.close(); - } catch (IOException e) { - e.printStackTrace(); - } - try { - is.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - return file; - } -} +package org.cloud.sonic.agent.tools; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.net.URLConnection; +import java.util.Calendar; + +public class DownImageTool { + public static File download(String urlString) throws IOException { + URL url = new URL(urlString); + URLConnection con = url.openConnection(); + InputStream is = con.getInputStream(); + byte[] bs = new byte[1024]; + int len; + + long time = Calendar.getInstance().getTimeInMillis(); + String tail = ""; + if (urlString.lastIndexOf(".") != -1) { + tail = urlString.substring(urlString.lastIndexOf(".") + 1); + } + String filename = "test-output" + File.separator + "download-" + time + "." + tail; + File file = new File(filename); + FileOutputStream os = null; + try { + os = new FileOutputStream(file, true); + while ((len = is.read(bs)) != -1) { + os.write(bs, 0, len); + } + } catch (Exception e) { + e.printStackTrace(); + throw e; + } finally { + try { + os.close(); + } catch (IOException e) { + e.printStackTrace(); + } + try { + is.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + return file; + } +} diff --git a/src/main/java/com/sonic/agent/tools/EnvCheckTool.java b/src/main/java/org/cloud/sonic/agent/tools/EnvCheckTool.java similarity index 99% rename from src/main/java/com/sonic/agent/tools/EnvCheckTool.java rename to src/main/java/org/cloud/sonic/agent/tools/EnvCheckTool.java index 83b1665e..6f9391b9 100644 --- a/src/main/java/com/sonic/agent/tools/EnvCheckTool.java +++ b/src/main/java/org/cloud/sonic/agent/tools/EnvCheckTool.java @@ -1,4 +1,4 @@ -package com.sonic.agent.tools; +package org.cloud.sonic.agent.tools; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.ConfigurableApplicationContext; diff --git a/src/main/java/com/sonic/agent/tools/LaunchTool.java b/src/main/java/org/cloud/sonic/agent/tools/LaunchTool.java similarity index 85% rename from src/main/java/com/sonic/agent/tools/LaunchTool.java rename to src/main/java/org/cloud/sonic/agent/tools/LaunchTool.java index 35531291..81635b2b 100644 --- a/src/main/java/com/sonic/agent/tools/LaunchTool.java +++ b/src/main/java/org/cloud/sonic/agent/tools/LaunchTool.java @@ -1,47 +1,47 @@ -package com.sonic.agent.tools; - -import com.sonic.agent.automation.AppiumServer; -import com.sonic.agent.automation.RemoteDebugDriver; -import com.sonic.agent.maps.GlobalProcessMap; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.boot.ApplicationArguments; -import org.springframework.boot.ApplicationRunner; -import org.springframework.context.annotation.DependsOn; -import org.springframework.stereotype.Component; - -import javax.annotation.PreDestroy; -import java.io.File; - -@Component -@DependsOn("nettyMsgInit") -public class LaunchTool implements ApplicationRunner { - private final Logger logger = LoggerFactory.getLogger(LaunchTool.class); - - @Override - public void run(ApplicationArguments args) { - File testFile = new File("test-output"); - if (!testFile.exists()) { - testFile.mkdirs(); - } - AppiumServer.start(); - } - - @PreDestroy - public void destroy() throws InterruptedException { - RemoteDebugDriver.close(); - for (String key : GlobalProcessMap.getMap().keySet()) { - Process ps = GlobalProcessMap.getMap().get(key); - ps.children().forEach(ProcessHandle::destroy); - ps.destroy(); - } - AppiumServer.close(); - while (AppiumServer.service != null) { - if (!AppiumServer.service.isRunning()) { - break; - } else { - Thread.sleep(1000); - } - } - } -} +package org.cloud.sonic.agent.tools; + +import org.cloud.sonic.agent.automation.AppiumServer; +import org.cloud.sonic.agent.automation.RemoteDebugDriver; +import org.cloud.sonic.agent.maps.GlobalProcessMap; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.context.annotation.DependsOn; +import org.springframework.stereotype.Component; + +import javax.annotation.PreDestroy; +import java.io.File; + +@Component +@DependsOn("nettyMsgInit") +public class LaunchTool implements ApplicationRunner { + private final Logger logger = LoggerFactory.getLogger(LaunchTool.class); + + @Override + public void run(ApplicationArguments args) { + File testFile = new File("test-output"); + if (!testFile.exists()) { + testFile.mkdirs(); + } + AppiumServer.start(); + } + + @PreDestroy + public void destroy() throws InterruptedException { + RemoteDebugDriver.close(); + for (String key : GlobalProcessMap.getMap().keySet()) { + Process ps = GlobalProcessMap.getMap().get(key); + ps.children().forEach(ProcessHandle::destroy); + ps.destroy(); + } + AppiumServer.close(); + while (AppiumServer.service != null) { + if (!AppiumServer.service.isRunning()) { + break; + } else { + Thread.sleep(1000); + } + } + } +} diff --git a/src/main/java/com/sonic/agent/tools/LogTool.java b/src/main/java/org/cloud/sonic/agent/tools/LogTool.java similarity index 93% rename from src/main/java/com/sonic/agent/tools/LogTool.java rename to src/main/java/org/cloud/sonic/agent/tools/LogTool.java index 577f58f1..c9a3be13 100644 --- a/src/main/java/com/sonic/agent/tools/LogTool.java +++ b/src/main/java/org/cloud/sonic/agent/tools/LogTool.java @@ -1,217 +1,217 @@ -package com.sonic.agent.tools; - -import com.alibaba.fastjson.JSONObject; -import com.sonic.agent.interfaces.DeviceStatus; -import com.sonic.agent.interfaces.StepType; -import com.sonic.agent.maps.WebSocketSessionMap; -import com.sonic.agent.netty.NettyThreadPool; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.websocket.Session; -import java.io.IOException; -import java.text.SimpleDateFormat; -import java.util.Date; - -/** - * @author ZhouYiXun - * @des log工具类,会发送到服务端入库 - * @date 2021/8/16 19:54 - */ -public class LogTool { - private final Logger logger = LoggerFactory.getLogger(LogTool.class); - public String sessionId = ""; - public String type; - public int caseId = 0; - public int resultId = 0; - public String udId = ""; - - /** - * @param message - * @return void - * @author ZhouYiXun - * @des 判断发送到哪个地方 - * @date 2021/8/16 19:57 - */ - public void send(JSONObject message) { - //先加上消息附带信息 - message.put("cid", caseId); - message.put("rid", resultId); - message.put("udId", udId); - if (type.equals(DeviceStatus.DEBUGGING)) { - sendToWebSocket(WebSocketSessionMap.getSession(sessionId), message); - } - if (type.equals(DeviceStatus.TESTING)) { - sendToServer(message); - } - logger.info(message.toJSONString()); - } - - /** - * @param message - * @return void - * @author ZhouYiXun - * @des 发送到服务端 - * @date 2021/8/16 19:57 - */ - private void sendToServer(JSONObject message) { - message.put("time", new Date()); - NettyThreadPool.send(message); - } - - /** - * @param session - * @param message - * @return void - * @author ZhouYiXun - * @des 通过session发送给前端 - * @date 2021/8/16 19:57 - */ - private void sendToWebSocket(Session session, JSONObject message) { - synchronized (session) { - try { - message.put("time", getDateToString()); - session.getBasicRemote().sendText(message.toJSONString()); - } catch (IllegalStateException | IOException e) { - logger.error(e.getMessage()); - } - } - } - - /** - * @return java.lang.String - * @author ZhouYiXun - * @des format一下时间 - * @date 2021/8/16 19:58 - */ - public String getDateToString() { - SimpleDateFormat sf = new SimpleDateFormat("HH:mm:ss"); - return sf.format(new Date()); - } - - /** - * @param totalTime - * @param platform - * @param version - * @return void - * @author ZhouYiXun - * @des 发送运行时长 - * @date 2021/8/16 19:58 - */ - public void sendElapsed(int totalTime, int platform, String version) { - JSONObject log = new JSONObject(); - log.put("msg", "elapsed"); - log.put("pf", platform); - log.put("ver", version); - log.put("run", totalTime); - send(log); - } - - /** - * @param status - * @param des - * @param detail - * @return void - * @author ZhouYiXun - * @des 发送普通步骤log - * @date 2021/8/16 19:58 - */ - public void sendStepLog(int status, String des, String detail) { - JSONObject log = new JSONObject(); - log.put("msg", "step"); - log.put("des", des); - log.put("status", status); - log.put("log", detail); - send(log); - } - - /** - * @param type - * @param detail - * @return void - * @author ZhouYiXun - * @des 发送性能数据 - * @date 2021/8/16 19:58 - */ - public void sendPerLog(String packageName, int type, JSONObject detail) { - JSONObject log = new JSONObject(); - log.put("msg", "perform"); - log.put("des", packageName); - log.put("log", detail.toJSONString()); - log.put("status", type); - send(log); - } - - /** - * @param isSupport 是否支持录像 - * @param url - * @return void - * @author ZhouYiXun - * @des 发送录像数据 - * @date 2021/8/16 19:58 - */ - public void sendRecordLog(boolean isSupport, String fileName, String url) { - JSONObject log = new JSONObject(); - log.put("msg", "record"); - log.put("status", isSupport ? 1 : 0); - log.put("des", fileName); - log.put("log", url); - send(log); - } - - /** - * @param url - * @return void - * @author ZhouYiXun - * @des 发送日志数据 - * @date 2021/8/26 19:58 - */ -// public void sendSelfLog(String fileName, String url) { -// JSONObject log = new JSONObject(); -// log.put("msg", "log"); -// log.put("name", fileName); -// log.put("url", url); -// send(log); -// } - - /** - * @param status - * @return void - * @author ZhouYiXun - * @des 发送测试状态 - * @date 2021/8/16 19:58 - */ - public void sendStatusLog(int status) { - JSONObject log = new JSONObject(); - log.put("msg", "status"); - log.put("des", ""); - log.put("log", ""); - log.put("status", status); - send(log); - } - - /** - * @param platform - * @param version - * @param udId - * @param manufacturer - * @param model - * @param api - * @param size - * @return void - * @author ZhouYiXun - * @des 发送安卓Info - * @date 2021/8/16 19:59 - */ - public void androidInfo(String platform, String version, String udId, String manufacturer, String model, String api, String size) { - sendStepLog(StepType.INFO, "", - "设备操作系统:" + platform - + "
操作系统版本:" + version - + "
设备序列号:" + udId - + "
设备制造商:" + manufacturer - + "
设备型号:" + model - + "
安卓API等级:" + api - + "
设备分辨率:" + size - ); - } +package org.cloud.sonic.agent.tools; + +import com.alibaba.fastjson.JSONObject; +import org.cloud.sonic.agent.interfaces.DeviceStatus; +import org.cloud.sonic.agent.interfaces.StepType; +import org.cloud.sonic.agent.maps.WebSocketSessionMap; +import org.cloud.sonic.agent.netty.NettyThreadPool; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.websocket.Session; +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.Date; + +/** + * @author ZhouYiXun + * @des log工具类,会发送到服务端入库 + * @date 2021/8/16 19:54 + */ +public class LogTool { + private final Logger logger = LoggerFactory.getLogger(LogTool.class); + public String sessionId = ""; + public String type; + public int caseId = 0; + public int resultId = 0; + public String udId = ""; + + /** + * @param message + * @return void + * @author ZhouYiXun + * @des 判断发送到哪个地方 + * @date 2021/8/16 19:57 + */ + public void send(JSONObject message) { + //先加上消息附带信息 + message.put("cid", caseId); + message.put("rid", resultId); + message.put("udId", udId); + if (type.equals(DeviceStatus.DEBUGGING)) { + sendToWebSocket(WebSocketSessionMap.getSession(sessionId), message); + } + if (type.equals(DeviceStatus.TESTING)) { + sendToServer(message); + } + logger.info(message.toJSONString()); + } + + /** + * @param message + * @return void + * @author ZhouYiXun + * @des 发送到服务端 + * @date 2021/8/16 19:57 + */ + private void sendToServer(JSONObject message) { + message.put("time", new Date()); + NettyThreadPool.send(message); + } + + /** + * @param session + * @param message + * @return void + * @author ZhouYiXun + * @des 通过session发送给前端 + * @date 2021/8/16 19:57 + */ + private void sendToWebSocket(Session session, JSONObject message) { + synchronized (session) { + try { + message.put("time", getDateToString()); + session.getBasicRemote().sendText(message.toJSONString()); + } catch (IllegalStateException | IOException e) { + logger.error(e.getMessage()); + } + } + } + + /** + * @return java.lang.String + * @author ZhouYiXun + * @des format一下时间 + * @date 2021/8/16 19:58 + */ + public String getDateToString() { + SimpleDateFormat sf = new SimpleDateFormat("HH:mm:ss"); + return sf.format(new Date()); + } + + /** + * @param totalTime + * @param platform + * @param version + * @return void + * @author ZhouYiXun + * @des 发送运行时长 + * @date 2021/8/16 19:58 + */ + public void sendElapsed(int totalTime, int platform, String version) { + JSONObject log = new JSONObject(); + log.put("msg", "elapsed"); + log.put("pf", platform); + log.put("ver", version); + log.put("run", totalTime); + send(log); + } + + /** + * @param status + * @param des + * @param detail + * @return void + * @author ZhouYiXun + * @des 发送普通步骤log + * @date 2021/8/16 19:58 + */ + public void sendStepLog(int status, String des, String detail) { + JSONObject log = new JSONObject(); + log.put("msg", "step"); + log.put("des", des); + log.put("status", status); + log.put("log", detail); + send(log); + } + + /** + * @param type + * @param detail + * @return void + * @author ZhouYiXun + * @des 发送性能数据 + * @date 2021/8/16 19:58 + */ + public void sendPerLog(String packageName, int type, JSONObject detail) { + JSONObject log = new JSONObject(); + log.put("msg", "perform"); + log.put("des", packageName); + log.put("log", detail.toJSONString()); + log.put("status", type); + send(log); + } + + /** + * @param isSupport 是否支持录像 + * @param url + * @return void + * @author ZhouYiXun + * @des 发送录像数据 + * @date 2021/8/16 19:58 + */ + public void sendRecordLog(boolean isSupport, String fileName, String url) { + JSONObject log = new JSONObject(); + log.put("msg", "record"); + log.put("status", isSupport ? 1 : 0); + log.put("des", fileName); + log.put("log", url); + send(log); + } + + /** + * @param url + * @return void + * @author ZhouYiXun + * @des 发送日志数据 + * @date 2021/8/26 19:58 + */ +// public void sendSelfLog(String fileName, String url) { +// JSONObject log = new JSONObject(); +// log.put("msg", "log"); +// log.put("name", fileName); +// log.put("url", url); +// send(log); +// } + + /** + * @param status + * @return void + * @author ZhouYiXun + * @des 发送测试状态 + * @date 2021/8/16 19:58 + */ + public void sendStatusLog(int status) { + JSONObject log = new JSONObject(); + log.put("msg", "status"); + log.put("des", ""); + log.put("log", ""); + log.put("status", status); + send(log); + } + + /** + * @param platform + * @param version + * @param udId + * @param manufacturer + * @param model + * @param api + * @param size + * @return void + * @author ZhouYiXun + * @des 发送安卓Info + * @date 2021/8/16 19:59 + */ + public void androidInfo(String platform, String version, String udId, String manufacturer, String model, String api, String size) { + sendStepLog(StepType.INFO, "", + "设备操作系统:" + platform + + "
操作系统版本:" + version + + "
设备序列号:" + udId + + "
设备制造商:" + manufacturer + + "
设备型号:" + model + + "
安卓API等级:" + api + + "
设备分辨率:" + size + ); + } } \ No newline at end of file diff --git a/src/main/java/com/sonic/agent/tools/MiniCapTool.java b/src/main/java/org/cloud/sonic/agent/tools/MiniCapTool.java similarity index 82% rename from src/main/java/com/sonic/agent/tools/MiniCapTool.java rename to src/main/java/org/cloud/sonic/agent/tools/MiniCapTool.java index 26e8ae8e..899caf65 100644 --- a/src/main/java/com/sonic/agent/tools/MiniCapTool.java +++ b/src/main/java/org/cloud/sonic/agent/tools/MiniCapTool.java @@ -1,91 +1,91 @@ -package com.sonic.agent.tools; - -import com.android.ddmlib.IDevice; -import com.sonic.agent.bridge.android.AndroidDeviceBridgeTool; -import com.sonic.agent.tests.TaskManager; -import com.sonic.agent.tests.android.AndroidTestTaskBootThread; -import com.sonic.agent.tests.android.InputSocketThread; -import com.sonic.agent.tests.android.OutputSocketThread; -import com.sonic.agent.tests.android.SonicLocalThread; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.websocket.Session; -import java.util.List; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.atomic.AtomicReference; - -import static com.sonic.agent.tests.android.AndroidTestTaskBootThread.ANDROID_TEST_TASK_BOOT_PRE; - -/** - * @author ZhouYiXun - * @des - * @date 2021/8/26 9:20 - */ -public class MiniCapTool { - private final Logger logger = LoggerFactory.getLogger(MiniCapTool.class); - - public Thread start( - String udId, - AtomicReference banner, - AtomicReference> imgList, - String pic, - int tor, - Session session - ) { - // 这里的AndroidTestTaskBootThread仅作为data bean使用,不会启动 - return start(udId, banner, imgList, pic, tor, session, new AndroidTestTaskBootThread().setUdId(udId)); - } - - - public Thread start( - String udId, - AtomicReference banner, - AtomicReference> imgList, - String pic, - int tor, - Session session, - AndroidTestTaskBootThread androidTestTaskBootThread - ) { - IDevice iDevice = AndroidDeviceBridgeTool.getIDeviceByUdId(udId); - String key = androidTestTaskBootThread.formatThreadName(ANDROID_TEST_TASK_BOOT_PRE); - int s; - if (tor == -1) { - s = AndroidDeviceBridgeTool.getScreen(AndroidDeviceBridgeTool.getIDeviceByUdId(udId)); - } else { - s = tor; - } - // 启动minicap服务 - SonicLocalThread miniCapPro = new SonicLocalThread(iDevice, pic, s * 90, session, androidTestTaskBootThread); - TaskManager.startChildThread(key, miniCapPro); - - // 等待启动 - int wait = 0; - while (!miniCapPro.getIsFinish().tryAcquire()) { - wait++; - try { - Thread.sleep(500); - } catch (InterruptedException e) { - e.printStackTrace(); - } - // 启动失败了,强行跳过,保证其它服务可用 - if (wait > 8) { - break; - } - } - - // 启动输入流 - InputSocketThread sendImg = new InputSocketThread( - iDevice, new LinkedBlockingQueue<>(), miniCapPro, session - ); - // 启动输出流 - OutputSocketThread outputSocketThread = new OutputSocketThread( - sendImg, banner, imgList, session, pic - ); - - TaskManager.startChildThread(key, sendImg, outputSocketThread); - - return miniCapPro; // server线程 - } - -} +package org.cloud.sonic.agent.tools; + +import com.android.ddmlib.IDevice; +import org.cloud.sonic.agent.bridge.android.AndroidDeviceBridgeTool; +import org.cloud.sonic.agent.tests.TaskManager; +import org.cloud.sonic.agent.tests.android.AndroidTestTaskBootThread; +import org.cloud.sonic.agent.tests.android.InputSocketThread; +import org.cloud.sonic.agent.tests.android.OutputSocketThread; +import org.cloud.sonic.agent.tests.android.SonicLocalThread; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.websocket.Session; +import java.util.List; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.atomic.AtomicReference; + +import static org.cloud.sonic.agent.tests.android.AndroidTestTaskBootThread.ANDROID_TEST_TASK_BOOT_PRE; + +/** + * @author ZhouYiXun + * @des + * @date 2021/8/26 9:20 + */ +public class MiniCapTool { + private final Logger logger = LoggerFactory.getLogger(MiniCapTool.class); + + public Thread start( + String udId, + AtomicReference banner, + AtomicReference> imgList, + String pic, + int tor, + Session session + ) { + // 这里的AndroidTestTaskBootThread仅作为data bean使用,不会启动 + return start(udId, banner, imgList, pic, tor, session, new AndroidTestTaskBootThread().setUdId(udId)); + } + + + public Thread start( + String udId, + AtomicReference banner, + AtomicReference> imgList, + String pic, + int tor, + Session session, + AndroidTestTaskBootThread androidTestTaskBootThread + ) { + IDevice iDevice = AndroidDeviceBridgeTool.getIDeviceByUdId(udId); + String key = androidTestTaskBootThread.formatThreadName(ANDROID_TEST_TASK_BOOT_PRE); + int s; + if (tor == -1) { + s = AndroidDeviceBridgeTool.getScreen(AndroidDeviceBridgeTool.getIDeviceByUdId(udId)); + } else { + s = tor; + } + // 启动minicap服务 + SonicLocalThread miniCapPro = new SonicLocalThread(iDevice, pic, s * 90, session, androidTestTaskBootThread); + TaskManager.startChildThread(key, miniCapPro); + + // 等待启动 + int wait = 0; + while (!miniCapPro.getIsFinish().tryAcquire()) { + wait++; + try { + Thread.sleep(500); + } catch (InterruptedException e) { + e.printStackTrace(); + } + // 启动失败了,强行跳过,保证其它服务可用 + if (wait > 8) { + break; + } + } + + // 启动输入流 + InputSocketThread sendImg = new InputSocketThread( + iDevice, new LinkedBlockingQueue<>(), miniCapPro, session + ); + // 启动输出流 + OutputSocketThread outputSocketThread = new OutputSocketThread( + sendImg, banner, imgList, session, pic + ); + + TaskManager.startChildThread(key, sendImg, outputSocketThread); + + return miniCapPro; // server线程 + } + +} diff --git a/src/main/java/com/sonic/agent/tools/PortTool.java b/src/main/java/org/cloud/sonic/agent/tools/PortTool.java similarity index 89% rename from src/main/java/com/sonic/agent/tools/PortTool.java rename to src/main/java/org/cloud/sonic/agent/tools/PortTool.java index f4b536e3..77f08b10 100644 --- a/src/main/java/com/sonic/agent/tools/PortTool.java +++ b/src/main/java/org/cloud/sonic/agent/tools/PortTool.java @@ -1,19 +1,19 @@ -package com.sonic.agent.tools; - -import java.io.IOException; -import java.net.ServerSocket; - -public class PortTool { - public static Integer getPort() { - ServerSocket serverSocket; - int port = 0; - try { - serverSocket = new ServerSocket(0); - port = serverSocket.getLocalPort(); - serverSocket.close(); - } catch (IOException e) { - e.printStackTrace(); - } - return port; - } +package org.cloud.sonic.agent.tools; + +import java.io.IOException; +import java.net.ServerSocket; + +public class PortTool { + public static Integer getPort() { + ServerSocket serverSocket; + int port = 0; + try { + serverSocket = new ServerSocket(0); + port = serverSocket.getLocalPort(); + serverSocket.close(); + } catch (IOException e) { + e.printStackTrace(); + } + return port; + } } \ No newline at end of file diff --git a/src/main/java/com/sonic/agent/tools/ProcessCommandTool.java b/src/main/java/org/cloud/sonic/agent/tools/ProcessCommandTool.java similarity index 96% rename from src/main/java/com/sonic/agent/tools/ProcessCommandTool.java rename to src/main/java/org/cloud/sonic/agent/tools/ProcessCommandTool.java index c39ae265..c36f5814 100644 --- a/src/main/java/com/sonic/agent/tools/ProcessCommandTool.java +++ b/src/main/java/org/cloud/sonic/agent/tools/ProcessCommandTool.java @@ -1,63 +1,63 @@ -package com.sonic.agent.tools; - -import java.io.InputStreamReader; -import java.io.LineNumberReader; -import java.util.ArrayList; -import java.util.List; - -public class ProcessCommandTool { - public static List getProcessLocalCommand(String commandLine) { - Process process = null; - InputStreamReader inputStreamReader = null; - InputStreamReader errorStreamReader; - LineNumberReader consoleInput = null; - LineNumberReader consoleError = null; - String consoleInputLine; - String consoleErrorLine; - List sdrResult = new ArrayList(); - List sdrErrorResult = new ArrayList(); - try { - String system = System.getProperty("os.name").toLowerCase(); - if (system.contains("win")) { - process = Runtime.getRuntime().exec(new String[]{"cmd", "/c", commandLine}); - } else { - process = Runtime.getRuntime().exec(new String[]{"sh", "-c", commandLine}); - } - inputStreamReader = new InputStreamReader(process.getInputStream()); - consoleInput = new LineNumberReader(inputStreamReader); - while ((consoleInputLine = consoleInput.readLine()) != null) { - sdrResult.add(consoleInputLine); - } - errorStreamReader = new InputStreamReader(process.getErrorStream()); - consoleError = new LineNumberReader(errorStreamReader); - while ((consoleErrorLine = consoleError.readLine()) != null) { - sdrErrorResult.add(consoleErrorLine); - } - - int resultCode = process.waitFor(); - if (resultCode > 0 && sdrErrorResult.size() > 0) { - return sdrErrorResult; - } else { - return sdrResult; - } - } catch (Exception e) { - return new ArrayList(); - } finally { - try { - if (null != consoleInput) { - consoleInput.close(); - } - if (null != consoleError) { - consoleError.close(); - } - if (null != inputStreamReader) { - inputStreamReader.close(); - } - if (null != process) { - process.destroy(); - } - } catch (Exception e) { - } - } - } -} +package org.cloud.sonic.agent.tools; + +import java.io.InputStreamReader; +import java.io.LineNumberReader; +import java.util.ArrayList; +import java.util.List; + +public class ProcessCommandTool { + public static List getProcessLocalCommand(String commandLine) { + Process process = null; + InputStreamReader inputStreamReader = null; + InputStreamReader errorStreamReader; + LineNumberReader consoleInput = null; + LineNumberReader consoleError = null; + String consoleInputLine; + String consoleErrorLine; + List sdrResult = new ArrayList(); + List sdrErrorResult = new ArrayList(); + try { + String system = System.getProperty("os.name").toLowerCase(); + if (system.contains("win")) { + process = Runtime.getRuntime().exec(new String[]{"cmd", "/c", commandLine}); + } else { + process = Runtime.getRuntime().exec(new String[]{"sh", "-c", commandLine}); + } + inputStreamReader = new InputStreamReader(process.getInputStream()); + consoleInput = new LineNumberReader(inputStreamReader); + while ((consoleInputLine = consoleInput.readLine()) != null) { + sdrResult.add(consoleInputLine); + } + errorStreamReader = new InputStreamReader(process.getErrorStream()); + consoleError = new LineNumberReader(errorStreamReader); + while ((consoleErrorLine = consoleError.readLine()) != null) { + sdrErrorResult.add(consoleErrorLine); + } + + int resultCode = process.waitFor(); + if (resultCode > 0 && sdrErrorResult.size() > 0) { + return sdrErrorResult; + } else { + return sdrResult; + } + } catch (Exception e) { + return new ArrayList(); + } finally { + try { + if (null != consoleInput) { + consoleInput.close(); + } + if (null != consoleError) { + consoleError.close(); + } + if (null != inputStreamReader) { + inputStreamReader.close(); + } + if (null != process) { + process.destroy(); + } + } catch (Exception e) { + } + } + } +} diff --git a/src/main/java/com/sonic/agent/tools/RecordHandler.java b/src/main/java/org/cloud/sonic/agent/tools/RecordHandler.java similarity index 95% rename from src/main/java/com/sonic/agent/tools/RecordHandler.java rename to src/main/java/org/cloud/sonic/agent/tools/RecordHandler.java index a827d2c7..d89c5c16 100644 --- a/src/main/java/com/sonic/agent/tools/RecordHandler.java +++ b/src/main/java/org/cloud/sonic/agent/tools/RecordHandler.java @@ -1,6 +1,5 @@ -package com.sonic.agent.tools; +package org.cloud.sonic.agent.tools; -import com.sonic.agent.tools.UploadTools; import org.bytedeco.ffmpeg.global.avcodec; import org.bytedeco.ffmpeg.global.avutil; import org.bytedeco.javacv.FFmpegFrameRecorder; diff --git a/src/main/java/com/sonic/agent/tools/ScrcpyTool.java b/src/main/java/org/cloud/sonic/agent/tools/ScrcpyTool.java similarity index 98% rename from src/main/java/com/sonic/agent/tools/ScrcpyTool.java rename to src/main/java/org/cloud/sonic/agent/tools/ScrcpyTool.java index 84b07da5..844e966f 100644 --- a/src/main/java/com/sonic/agent/tools/ScrcpyTool.java +++ b/src/main/java/org/cloud/sonic/agent/tools/ScrcpyTool.java @@ -1,4 +1,4 @@ -package com.sonic.agent.tools; +package org.cloud.sonic.agent.tools; import java.io.IOException; import java.io.InputStream; diff --git a/src/main/java/com/sonic/agent/tools/SpringTool.java b/src/main/java/org/cloud/sonic/agent/tools/SpringTool.java similarity index 95% rename from src/main/java/com/sonic/agent/tools/SpringTool.java rename to src/main/java/org/cloud/sonic/agent/tools/SpringTool.java index 9b0a44fe..43f8d8c4 100644 --- a/src/main/java/com/sonic/agent/tools/SpringTool.java +++ b/src/main/java/org/cloud/sonic/agent/tools/SpringTool.java @@ -1,4 +1,4 @@ -package com.sonic.agent.tools; +package org.cloud.sonic.agent.tools; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; diff --git a/src/main/java/com/sonic/agent/tools/UploadTools.java b/src/main/java/org/cloud/sonic/agent/tools/UploadTools.java similarity index 96% rename from src/main/java/com/sonic/agent/tools/UploadTools.java rename to src/main/java/org/cloud/sonic/agent/tools/UploadTools.java index 38f63360..6a5ebccc 100644 --- a/src/main/java/com/sonic/agent/tools/UploadTools.java +++ b/src/main/java/org/cloud/sonic/agent/tools/UploadTools.java @@ -1,147 +1,145 @@ -package com.sonic.agent.tools; - -import com.alibaba.fastjson.JSONObject; -import net.coobird.thumbnailator.Thumbnails; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.core.io.FileSystemResource; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Component; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; -import org.springframework.web.client.RestTemplate; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.RandomAccessFile; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.List; -import java.util.UUID; - -/** - * @author ZhouYiXun - * @des 所有上传方法在这里 - * @date 2021/8/17 23:38 - */ -@Component -public class UploadTools { - private final static Logger logger = LoggerFactory.getLogger(UploadTools.class); - @Value("${sonic.server.host}") - private String host; - @Value("${sonic.server.folder-port}") - private String port; - private static String baseUrl; - - private static RestTemplate restTemplate; - - @Autowired - public void setRestTemplate(RestTemplate restTemplate) { - UploadTools.restTemplate = restTemplate; - baseUrl = "http://" + host + ":" + port + "/api/folder"; - } - - public static String upload(File uploadFile, String type) { - File folder = new File("test-output"); - if (!folder.exists()) {//判断文件目录是否存在 - folder.mkdirs(); - } - File transfer; - if (type.equals("keepFiles") || type.equals("imageFiles")) { - long timeMillis = Calendar.getInstance().getTimeInMillis(); - try { - Thumbnails.of(uploadFile) - .scale(1f) - .outputQuality(0.25f).toFile(folder + File.separator + timeMillis + "transfer.jpg"); - } catch (IOException e) { - logger.error(e.getMessage()); - } - transfer = new File(folder + File.separator + timeMillis + "transfer.jpg"); - } else { - transfer = uploadFile; - } - FileSystemResource resource = new FileSystemResource(transfer); - MultiValueMap param = new LinkedMultiValueMap<>(); - param.add("file", resource); - param.add("type", type); - ResponseEntity responseEntity = - restTemplate.postForEntity(baseUrl + "/upload", param, JSONObject.class); - if (responseEntity.getBody().getInteger("code") == 2000) { - transfer.delete(); - } else { - logger.info("发送失败!" + responseEntity.getBody()); - } - return responseEntity.getBody().getString("data"); - } - - public static String uploadPatchRecord(File uploadFile) { - if (uploadFile.length() == 0) { - uploadFile.delete(); - return null; - } - String url = ""; - long size = 1024 * 1024; - int num = (int) (Math.ceil(uploadFile.length() * 1.0 / size)); - String uuid = UUID.randomUUID().toString(); - File file = new File("test-output/record" + File.separator + uuid); - if (!file.exists()) { - file.mkdirs(); - } - try { - RandomAccessFile before = new RandomAccessFile(uploadFile, "r"); - long beforeSize = uploadFile.length(); - byte[] bytes = new byte[1024]; - int len; - int successNum = 0; - for (int i = 0; i < num; i++) { - File branchFile = new File(file.getPath() + File.separator + uploadFile.getName()); - RandomAccessFile branch = new RandomAccessFile(branchFile, "rw"); - while ((len = before.read(bytes)) != -1) { - if (beforeSize > len) { - branch.write(bytes, 0, len); - beforeSize -= len; - } else { - branch.write(bytes, 0, (int) beforeSize); - } - if (branch.length() >= size) - break; - } - branch.close(); - FileSystemResource resource = new FileSystemResource(uploadFile); - MultiValueMap param = new LinkedMultiValueMap<>(); - param.add("file", resource); - param.add("uuid", uuid); - param.add("index", i + ""); - param.add("total", num + ""); - ResponseEntity responseEntity = restTemplate.postForEntity(baseUrl + "/upload/recordFiles", param, JSONObject.class); - if (responseEntity.getBody().getInteger("code") == 2000) { - successNum++; - } - if (responseEntity.getBody().getString("data") != null) { - url = responseEntity.getBody().getString("data"); - } - branchFile.delete(); - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - before.close(); - file.delete(); - if (successNum == num) { - uploadFile.delete(); - } else { - logger.info("上传缺失!"); - } - } catch (FileNotFoundException e) { - e.printStackTrace(); - } catch (IOException e) { - e.printStackTrace(); - } - return url; - } -} +package org.cloud.sonic.agent.tools; + +import com.alibaba.fastjson.JSONObject; +import net.coobird.thumbnailator.Thumbnails; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.io.FileSystemResource; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Component; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestTemplate; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.util.Calendar; +import java.util.UUID; + +/** + * @author ZhouYiXun + * @des 所有上传方法在这里 + * @date 2021/8/17 23:38 + */ +@Component +public class UploadTools { + private final static Logger logger = LoggerFactory.getLogger(UploadTools.class); + @Value("${sonic.server.host}") + private String host; + @Value("${sonic.server.folder-port}") + private String port; + private static String baseUrl; + + private static RestTemplate restTemplate; + + @Autowired + public void setRestTemplate(RestTemplate restTemplate) { + UploadTools.restTemplate = restTemplate; + baseUrl = "http://" + host + ":" + port + "/api/folder"; + } + + public static String upload(File uploadFile, String type) { + File folder = new File("test-output"); + if (!folder.exists()) {//判断文件目录是否存在 + folder.mkdirs(); + } + File transfer; + if (type.equals("keepFiles") || type.equals("imageFiles")) { + long timeMillis = Calendar.getInstance().getTimeInMillis(); + try { + Thumbnails.of(uploadFile) + .scale(1f) + .outputQuality(0.25f).toFile(folder + File.separator + timeMillis + "transfer.jpg"); + } catch (IOException e) { + logger.error(e.getMessage()); + } + transfer = new File(folder + File.separator + timeMillis + "transfer.jpg"); + } else { + transfer = uploadFile; + } + FileSystemResource resource = new FileSystemResource(transfer); + MultiValueMap param = new LinkedMultiValueMap<>(); + param.add("file", resource); + param.add("type", type); + ResponseEntity responseEntity = + restTemplate.postForEntity(baseUrl + "/upload", param, JSONObject.class); + if (responseEntity.getBody().getInteger("code") == 2000) { + transfer.delete(); + } else { + logger.info("发送失败!" + responseEntity.getBody()); + } + return responseEntity.getBody().getString("data"); + } + + public static String uploadPatchRecord(File uploadFile) { + if (uploadFile.length() == 0) { + uploadFile.delete(); + return null; + } + String url = ""; + long size = 1024 * 1024; + int num = (int) (Math.ceil(uploadFile.length() * 1.0 / size)); + String uuid = UUID.randomUUID().toString(); + File file = new File("test-output/record" + File.separator + uuid); + if (!file.exists()) { + file.mkdirs(); + } + try { + RandomAccessFile before = new RandomAccessFile(uploadFile, "r"); + long beforeSize = uploadFile.length(); + byte[] bytes = new byte[1024]; + int len; + int successNum = 0; + for (int i = 0; i < num; i++) { + File branchFile = new File(file.getPath() + File.separator + uploadFile.getName()); + RandomAccessFile branch = new RandomAccessFile(branchFile, "rw"); + while ((len = before.read(bytes)) != -1) { + if (beforeSize > len) { + branch.write(bytes, 0, len); + beforeSize -= len; + } else { + branch.write(bytes, 0, (int) beforeSize); + } + if (branch.length() >= size) + break; + } + branch.close(); + FileSystemResource resource = new FileSystemResource(uploadFile); + MultiValueMap param = new LinkedMultiValueMap<>(); + param.add("file", resource); + param.add("uuid", uuid); + param.add("index", i + ""); + param.add("total", num + ""); + ResponseEntity responseEntity = restTemplate.postForEntity(baseUrl + "/upload/recordFiles", param, JSONObject.class); + if (responseEntity.getBody().getInteger("code") == 2000) { + successNum++; + } + if (responseEntity.getBody().getString("data") != null) { + url = responseEntity.getBody().getString("data"); + } + branchFile.delete(); + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + before.close(); + file.delete(); + if (successNum == num) { + uploadFile.delete(); + } else { + logger.info("上传缺失!"); + } + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + return url; + } +} diff --git a/src/main/java/com/sonic/agent/websockets/AndroidWSServer.java b/src/main/java/org/cloud/sonic/agent/websockets/AndroidWSServer.java similarity index 93% rename from src/main/java/com/sonic/agent/websockets/AndroidWSServer.java rename to src/main/java/org/cloud/sonic/agent/websockets/AndroidWSServer.java index 0d1ca276..78cddde9 100644 --- a/src/main/java/com/sonic/agent/websockets/AndroidWSServer.java +++ b/src/main/java/org/cloud/sonic/agent/websockets/AndroidWSServer.java @@ -1,625 +1,625 @@ -package com.sonic.agent.websockets; - -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.JSONArray; -import com.alibaba.fastjson.JSONObject; -import com.android.ddmlib.IDevice; -import com.android.ddmlib.IShellOutputReceiver; -import com.android.ddmlib.InstallException; -import com.sonic.agent.automation.AndroidStepHandler; -import com.sonic.agent.automation.HandleDes; -import com.sonic.agent.automation.RemoteDebugDriver; -import com.sonic.agent.bridge.android.AndroidDeviceBridgeTool; -import com.sonic.agent.bridge.android.AndroidDeviceLocalStatus; -import com.sonic.agent.bridge.android.AndroidDeviceThreadPool; -import com.sonic.agent.interfaces.DeviceStatus; -import com.sonic.agent.maps.*; -import com.sonic.agent.netty.NettyThreadPool; -import com.sonic.agent.tools.*; -import org.openqa.selenium.OutputType; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.http.*; -import org.springframework.stereotype.Component; -import org.springframework.web.client.RestTemplate; - -import javax.websocket.*; -import javax.websocket.server.PathParam; -import javax.websocket.server.ServerEndpoint; -import java.io.File; -import java.io.IOException; -import java.io.OutputStream; -import java.net.Socket; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.Semaphore; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicReference; - -import static com.sonic.agent.tools.AgentTool.sendText; - -@Component -@ServerEndpoint(value = "/websockets/android/{key}/{udId}/{token}", configurator = MyEndpointConfigure.class) -public class AndroidWSServer { - - private final Logger logger = LoggerFactory.getLogger(AndroidWSServer.class); - @Value("${sonic.agent.key}") - private String key; - @Value("${modules.android.use-adbkit}") - private boolean isEnableAdbKit; - private Map udIdMap = new ConcurrentHashMap<>(); - private Map> webViewForwardMap = new ConcurrentHashMap<>(); - private Map outputMap = new ConcurrentHashMap<>(); - private Map rotationMap = new ConcurrentHashMap<>(); - private Map rotationStatusMap = new ConcurrentHashMap<>(); - private Map picMap = new ConcurrentHashMap<>(); - @Autowired - private RestTemplate restTemplate; - - @OnOpen - public void onOpen(Session session, @PathParam("key") String secretKey, - @PathParam("udId") String udId, @PathParam("token") String token) throws Exception { - if (secretKey.length() == 0 || (!secretKey.equals(key)) || token.length() == 0) { - logger.info("拦截访问!"); - return; - } - - session.getUserProperties().put("udId", udId); - boolean lockSuccess = DevicesLockMap.lockByUdId(udId, 30L, TimeUnit.SECONDS); - if (!lockSuccess) { - logger.info("30s内获取设备锁失败,请确保设备无人使用"); - return; - } - logger.info("android上锁udId:{}", udId); - AndroidDeviceLocalStatus.startDebug(udId); - JSONObject jsonDebug = new JSONObject(); - jsonDebug.put("msg", "debugUser"); - jsonDebug.put("token", token); - jsonDebug.put("udId", udId); - NettyThreadPool.send(jsonDebug); - WebSocketSessionMap.addSession(session); - IDevice iDevice = AndroidDeviceBridgeTool.getIDeviceByUdId(udId); - if (iDevice == null) { - logger.info("设备未连接,请检查!"); - return; - } - AndroidDeviceBridgeTool.screen(iDevice, "abort"); - AndroidDeviceBridgeTool.pressKey(iDevice, 3); - udIdMap.put(session, iDevice); - - String path = AndroidDeviceBridgeTool.executeCommand(iDevice, "pm path com.sonic.plugins.assist").trim() - .replaceAll("package:", "") - .replaceAll("\n", "") - .replaceAll("\t", ""); - if (path.length() > 0) { - logger.info("已安装Sonic插件"); - } else { - try { - iDevice.installPackage("plugins/sonic-plugin.apk", true, "-t"); - } catch (InstallException e) { - e.printStackTrace(); - logger.info("Sonic插件安装失败!"); - return; - } - path = AndroidDeviceBridgeTool.executeCommand(iDevice, "pm path com.sonic.plugins.assist").trim() - .replaceAll("package:", "") - .replaceAll("\n", "") - .replaceAll("\t", ""); - } - - Semaphore isTouchFinish = new Semaphore(0); - String finalPath = path; - Thread rotationPro = new Thread(() -> { - try { - //开始启动 - iDevice.executeShellCommand(String.format("CLASSPATH=%s exec app_process /system/bin com.sonic.plugins.assist.RotationMonitorService", finalPath) - , new IShellOutputReceiver() { - @Override - public void addOutput(byte[] bytes, int i, int i1) { - String res = new String(bytes, i, i1).replaceAll("\n", "").replaceAll("\r", ""); - logger.info(udId + "旋转到:" + res); - rotationStatusMap.put(session, Integer.parseInt(res)); - JSONObject rotationJson = new JSONObject(); - rotationJson.put("msg", "rotation"); - rotationJson.put("value", Integer.parseInt(res) * 90); - sendText(session, rotationJson.toJSONString()); - Thread old = MiniCapMap.getMap().get(session); - if (old != null) { - old.interrupt(); - do { - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - while (MiniCapMap.getMap().get(session) != null); - } - MiniCapTool miniCapTool = new MiniCapTool(); - AtomicReference banner = new AtomicReference<>(new String[24]); - Thread miniCapThread = miniCapTool.start( - udIdMap.get(session).getSerialNumber(), banner, null, - picMap.get(session) == null ? "high" : picMap.get(session), - Integer.parseInt(res), session - ); - MiniCapMap.getMap().put(session, miniCapThread); - JSONObject picFinish = new JSONObject(); - picFinish.put("msg", "picFinish"); - sendText(session, picFinish.toJSONString()); - } - - @Override - public void flush() { - } - - @Override - public boolean isCancelled() { - return false; - } - }, 0, TimeUnit.MILLISECONDS); - } catch (Exception e) { - logger.info("{} 设备方向监听启动异常!" - , iDevice.getSerialNumber()); - logger.error(e.getMessage()); - } - }); - rotationPro.start(); - rotationMap.put(session, rotationPro); - - Thread touchPro = new Thread(() -> { - try { - //开始启动 - iDevice.executeShellCommand(String.format("CLASSPATH=%s exec app_process /system/bin com.sonic.plugins.assist.SonicTouchService", finalPath) - , new IShellOutputReceiver() { - @Override - public void addOutput(byte[] bytes, int i, int i1) { - String res = new String(bytes, i, i1); - logger.info(res); - if (res.contains("Server start")) { - isTouchFinish.release(); - } - } - - @Override - public void flush() { - } - - @Override - public boolean isCancelled() { - return false; - } - }, 0, TimeUnit.MILLISECONDS); - } catch (Exception e) { - logger.info("{} 设备touch服务启动异常!" - , iDevice.getSerialNumber()); - logger.error(e.getMessage()); - } - }); - touchPro.start(); - - int finalTouchPort = PortTool.getPort(); - AndroidDeviceThreadPool.cachedThreadPool.execute(() -> { - int wait = 0; - while (!isTouchFinish.tryAcquire()) { - wait++; - try { - Thread.sleep(500); - } catch (InterruptedException e) { - e.printStackTrace(); - } - // 超时就不继续等了,保证其它服务可运行 - if (wait > 20) { - return; - } - } - AndroidDeviceBridgeTool.forward(iDevice, finalTouchPort, "sonictouchservice"); - Socket touchSocket = null; - OutputStream outputStream = null; - try { - touchSocket = new Socket("localhost", finalTouchPort); - outputStream = touchSocket.getOutputStream(); - outputMap.put(session, outputStream); - while (outputMap.get(session) != null && (touchPro.isAlive())) { - Thread.sleep(1000); - } - } catch (IOException | InterruptedException e) { - e.printStackTrace(); - } finally { - if (touchPro.isAlive()) { - touchPro.interrupt(); - logger.info("touch thread已关闭"); - } - if (touchSocket != null && touchSocket.isConnected()) { - try { - touchSocket.close(); - logger.info("touch socket已关闭"); - } catch (IOException e) { - e.printStackTrace(); - } - } - if (outputStream != null) { - try { - outputStream.close(); - logger.info("touch output流已关闭"); - } catch (IOException e) { - e.printStackTrace(); - } - } - } - AndroidDeviceBridgeTool.removeForward(iDevice, finalTouchPort, "sonictouchservice"); - }); - - AndroidDeviceThreadPool.cachedThreadPool.execute(() -> AndroidDeviceBridgeTool.pushYadb(udIdMap.get(session))); - - if (isEnableAdbKit) { - String processName = String.format("process-%s-adbkit", udId); - if (GlobalProcessMap.getMap().get(processName) != null) { - Process ps = GlobalProcessMap.getMap().get(processName); - ps.children().forEach(ProcessHandle::destroy); - ps.destroy(); - } - try { - String system = System.getProperty("os.name").toLowerCase(); - Process ps = null; - int port = PortTool.getPort(); - String command = String.format("adbkit usb-device-to-tcp -p %d %s", port, udId); - if (system.contains("win")) { - ps = Runtime.getRuntime().exec(new String[]{"cmd", "/c", command}); - } else if (system.contains("linux") || system.contains("mac")) { - ps = Runtime.getRuntime().exec(new String[]{"sh", "-c", command}); - } - GlobalProcessMap.getMap().put(processName, ps); - JSONObject adbkit = new JSONObject(); - adbkit.put("msg", "adbkit"); - adbkit.put("isEnable", true); - adbkit.put("port", port); - sendText(session, adbkit.toJSONString()); - } catch (IOException e) { - e.printStackTrace(); - } - } else { - JSONObject adbkit = new JSONObject(); - adbkit.put("msg", "adbkit"); - adbkit.put("isEnable", false); - sendText(session, adbkit.toJSONString()); - } - - AndroidDeviceThreadPool.cachedThreadPool.execute(() -> { - AndroidStepHandler androidStepHandler = new AndroidStepHandler(); - androidStepHandler.setTestMode(0, 0, udId, DeviceStatus.DEBUGGING, session.getId()); - JSONObject result = new JSONObject(); - try { - androidStepHandler.startAndroidDriver(udId); - result.put("status", "success"); - result.put("detail", "初始化Driver完成!"); - HandlerMap.getAndroidMap().put(session.getId(), androidStepHandler); - } catch (Exception e) { - logger.error(e.getMessage()); - result.put("status", "error"); - result.put("detail", "初始化Driver失败!部分功能不可用!请联系管理员"); - } finally { - result.put("msg", "openDriver"); - sendText(session, result.toJSONString()); - } - }); - } - - @OnClose - public void onClose(Session session) { - String udId = (String) session.getUserProperties().get("udId"); - try { - exit(session); - } finally { - DevicesLockMap.unlockAndRemoveByUdId(udId); - logger.info("android解锁udId:{}", udId); - } - } - - @OnError - public void onError(Session session, Throwable error) { - logger.error(error.getMessage()); - JSONObject errMsg = new JSONObject(); - errMsg.put("msg", "error"); - sendText(session, errMsg.toJSONString()); - } - - @OnMessage - public void onMessage(String message, Session session) throws InterruptedException { - JSONObject msg = JSON.parseObject(message); - logger.info(session.getId() + " 发送 " + msg); - switch (msg.getString("type")) { - case "forwardView": { - JSONObject forwardView = new JSONObject(); - IDevice iDevice = udIdMap.get(session); - List wList = Arrays.asList("webview", "WebView", "chrome_devtools_remote", "Terrace_devtools_remote"); - 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()) { - webSet.add(w.substring(w.indexOf("@") + 1)); - } - } - List has = webViewForwardMap.get(iDevice); - if (has != null && has.size() > 0) { - for (JSONObject j : has) { - AndroidDeviceBridgeTool.removeForward(iDevice, j.getInteger("port"), j.getString("name")); - } - } - has = new ArrayList<>(); - List result = new ArrayList<>(); - if (webViewList.size() > 0) { - HttpHeaders headers = new HttpHeaders(); - headers.add("Content-Type", "application/json"); - for (String ws : webSet) { - int port = PortTool.getPort(); - AndroidDeviceBridgeTool.forward(iDevice, port, ws); - JSONObject j = new JSONObject(); - j.put("port", port); - j.put("name", ws); - has.add(j); - JSONObject r = new JSONObject(); - r.put("port", port); - 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); - if (responseEntity.getStatusCode() == HttpStatus.OK) { - List child = new ArrayList<>(); - for (Object e : responseEntity.getBody()) { - LinkedHashMap objE = (LinkedHashMap) e; - JSONObject c = new JSONObject(); - c.put("favicon", objE.get("faviconUrl")); - c.put("title", objE.get("title")); - c.put("url", objE.get("url")); - c.put("id", objE.get("id")); - child.add(c); - } - r.put("children", child); - result.add(r); - } - } - webViewForwardMap.put(iDevice, has); - } - forwardView.put("msg", "forwardView"); - if (RemoteDebugDriver.webDriver == null) { - RemoteDebugDriver.startChromeDriver(); - } - forwardView.put("chromePort", RemoteDebugDriver.chromePort); - forwardView.put("detail", result); - sendText(session, forwardView.toJSONString()); - break; - } - case "find": - AndroidDeviceBridgeTool.searchDevice(udIdMap.get(session)); - break; - case "battery": - AndroidDeviceBridgeTool.controlBattery(udIdMap.get(session), msg.getInteger("detail")); - break; - case "scan": - AndroidDeviceBridgeTool.pushToCamera(udIdMap.get(session), msg.getString("url")); - break; - case "text": - ProcessCommandTool.getProcessLocalCommand("adb -s " + udIdMap.get(session).getSerialNumber() - + " shell app_process -Djava.class.path=/data/local/tmp/yadb /data/local/tmp com.ysbing.yadb.Main -keyboard " + msg.getString("detail")); - break; - case "pic": { - Thread old = MiniCapMap.getMap().get(session); - old.interrupt(); - do { - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - while (MiniCapMap.getMap().get(session) != null); - MiniCapTool miniCapTool = new MiniCapTool(); - AtomicReference banner = new AtomicReference<>(new String[24]); - Thread miniCapThread = miniCapTool.start( - udIdMap.get(session).getSerialNumber(), banner, null, msg.getString("detail"), - rotationStatusMap.get(session), session - ); - MiniCapMap.getMap().put(session, miniCapThread); - JSONObject picFinish = new JSONObject(); - picFinish.put("msg", "picFinish"); - sendText(session, picFinish.toJSONString()); - break; - } - case "touch": - OutputStream outputStream = outputMap.get(session); - if (outputStream != null) { - try { - outputStream.write(msg.getString("detail").getBytes()); - outputStream.flush(); - outputStream.write("c\n".getBytes()); - outputStream.flush(); - } catch (IOException e) { - e.printStackTrace(); - } - } - break; - case "keyEvent": - AndroidDeviceBridgeTool.pressKey(udIdMap.get(session), msg.getInteger("detail")); - break; - case "debug": - if (msg.getString("detail").equals("runStep")) { - JSONObject jsonDebug = new JSONObject(); - jsonDebug.put("msg", "findSteps"); - jsonDebug.put("key", key); - jsonDebug.put("udId", udIdMap.get(session).getSerialNumber()); - jsonDebug.put("pwd", msg.getString("pwd")); - jsonDebug.put("sessionId", session.getId()); - jsonDebug.put("caseId", msg.getInteger("caseId")); - NettyThreadPool.send(jsonDebug); - } else { - AndroidStepHandler androidStepHandler = HandlerMap.getAndroidMap().get(session.getId()); - if (androidStepHandler == null || androidStepHandler.getAndroidDriver() == null) { - if (msg.getString("detail").equals("openDriver")) { - androidStepHandler = new AndroidStepHandler(); - androidStepHandler.setTestMode(0, 0, udIdMap.get(session).getSerialNumber(), DeviceStatus.DEBUGGING, session.getId()); - JSONObject result = new JSONObject(); - AndroidStepHandler finalAndroidStepHandler1 = androidStepHandler; - AndroidDeviceThreadPool.cachedThreadPool.execute(() -> { - try { - AndroidDeviceLocalStatus.startDebug(udIdMap.get(session).getSerialNumber()); - finalAndroidStepHandler1.startAndroidDriver(udIdMap.get(session).getSerialNumber()); - result.put("status", "success"); - result.put("detail", "初始化Driver完成!"); - HandlerMap.getAndroidMap().put(session.getId(), finalAndroidStepHandler1); - } catch (Exception e) { - logger.error(e.getMessage()); - result.put("status", "error"); - result.put("detail", "初始化Driver失败!部分功能不可用!请联系管理员"); - } finally { - result.put("msg", "openDriver"); - sendText(session, result.toJSONString()); - } - }); - } - break; - } - try { - if (msg.getString("detail").equals("tap")) { - String xy = msg.getString("point"); - int x = Integer.parseInt(xy.substring(0, xy.indexOf(","))); - int y = Integer.parseInt(xy.substring(xy.indexOf(",") + 1)); - AndroidDeviceBridgeTool.executeCommand(udIdMap.get(session), "input tap " + x + " " + y); - } - if (msg.getString("detail").equals("longPress")) { - String xy = msg.getString("point"); - int x = Integer.parseInt(xy.substring(0, xy.indexOf(","))); - int y = Integer.parseInt(xy.substring(xy.indexOf(",") + 1)); - AndroidDeviceBridgeTool.executeCommand(udIdMap.get(session), "input swipe " + x + " " + y + " " + x + " " + y + " 1500"); - } - if (msg.getString("detail").equals("swipe")) { - String xy1 = msg.getString("pointA"); - String xy2 = msg.getString("pointB"); - int x1 = Integer.parseInt(xy1.substring(0, xy1.indexOf(","))); - 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 + " 200"); - } - } catch (Exception e) { - e.printStackTrace(); - } - if (msg.getString("detail").equals("install")) { - AndroidDeviceThreadPool.cachedThreadPool.execute(() -> { - JSONObject result = new JSONObject(); - result.put("msg", "installFinish"); - try { - File localFile = DownImageTool.download(msg.getString("apk")); - udIdMap.get(session).installPackage(localFile.getAbsolutePath(), true, "-t"); - result.put("status", "success"); - } catch (IOException | InstallException e) { - result.put("status", "fail"); - e.printStackTrace(); - } - sendText(session, result.toJSONString()); - }); - } - if (msg.getString("detail").equals("tree")) { - AndroidStepHandler finalAndroidStepHandler = androidStepHandler; - AndroidDeviceThreadPool.cachedThreadPool.execute(() -> { - try { - JSONObject result = new JSONObject(); - result.put("msg", "tree"); - result.put("detail", finalAndroidStepHandler.getResource()); - HandleDes handleDes = new HandleDes(); - if (!msg.getBoolean("hasScreen")) { - result.put("img", finalAndroidStepHandler.stepScreen(handleDes)); - } - if (handleDes.getE() != null) { - logger.error(handleDes.getE().getMessage()); - JSONObject resultFail = new JSONObject(); - resultFail.put("msg", "treeFail"); - sendText(session, resultFail.toJSONString()); - } else { - result.put("webView", finalAndroidStepHandler.getWebView()); - result.put("activity", finalAndroidStepHandler.getCurrentActivity()); - sendText(session, result.toJSONString()); - } - } catch (Throwable e) { - logger.error(e.getMessage()); - JSONObject result = new JSONObject(); - result.put("msg", "treeFail"); - sendText(session, result.toJSONString()); - } - }); - } - if (msg.getString("detail").equals("eleScreen")) { - AndroidStepHandler finalAndroidStepHandler = androidStepHandler; - AndroidDeviceThreadPool.cachedThreadPool.execute(() -> { - JSONObject result = new JSONObject(); - result.put("msg", "eleScreen"); - try { - result.put("img", UploadTools.upload(finalAndroidStepHandler.findEle("xpath", msg.getString("xpath")).getScreenshotAs(OutputType.FILE), "keepFiles")); - } catch (Exception e) { - result.put("errMsg", "获取元素截图失败!"); - } - sendText(session, result.toJSONString()); - }); - } - } - break; - } - } - - private void exit(Session session) { - AndroidDeviceLocalStatus.finish(session.getUserProperties().get("udId") + ""); - try { - HandlerMap.getAndroidMap().get(session.getId()).closeAndroidDriver(); - } catch (Exception e) { - logger.info("关闭driver异常!"); - } finally { - HandlerMap.getAndroidMap().remove(session.getId()); - } - if (udIdMap.get(session) != null) { - List has = webViewForwardMap.get(udIdMap.get(session)); - if (has != null && has.size() > 0) { - for (JSONObject j : has) { - AndroidDeviceBridgeTool.removeForward(udIdMap.get(session), j.getInteger("port"), j.getString("name")); - } - } - webViewForwardMap.remove(udIdMap.get(session)); - if (isEnableAdbKit) { - String processName = String.format("process-%s-adbkit", udIdMap.get(session).getSerialNumber()); - if (GlobalProcessMap.getMap().get(processName) != null) { - Process ps = GlobalProcessMap.getMap().get(processName); - ps.children().forEach(ProcessHandle::destroy); - ps.destroy(); - } - } - } - outputMap.remove(session); - udIdMap.remove(session); - if (rotationMap.get(session) != null) { - rotationMap.get(session).interrupt(); - } - rotationMap.remove(session); - if (MiniCapMap.getMap().get(session) != null) { - MiniCapMap.getMap().get(session).interrupt(); - } - WebSocketSessionMap.removeSession(session); - try { - session.close(); - } catch (IOException e) { - e.printStackTrace(); - } - logger.info(session.getId() + "退出"); - } -} +package org.cloud.sonic.agent.websockets; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.android.ddmlib.IDevice; +import com.android.ddmlib.IShellOutputReceiver; +import com.android.ddmlib.InstallException; +import org.cloud.sonic.agent.automation.AndroidStepHandler; +import org.cloud.sonic.agent.automation.HandleDes; +import org.cloud.sonic.agent.automation.RemoteDebugDriver; +import org.cloud.sonic.agent.bridge.android.AndroidDeviceBridgeTool; +import org.cloud.sonic.agent.bridge.android.AndroidDeviceLocalStatus; +import org.cloud.sonic.agent.bridge.android.AndroidDeviceThreadPool; +import org.cloud.sonic.agent.interfaces.DeviceStatus; +import org.cloud.sonic.agent.maps.*; +import org.cloud.sonic.agent.maps.*; +import org.cloud.sonic.agent.netty.NettyThreadPool; +import org.cloud.sonic.agent.tools.*; +import org.cloud.sonic.agent.tools.*; +import org.openqa.selenium.OutputType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.*; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestTemplate; + +import javax.websocket.*; +import javax.websocket.server.PathParam; +import javax.websocket.server.ServerEndpoint; +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.net.Socket; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; + +@Component +@ServerEndpoint(value = "/websockets/android/{key}/{udId}/{token}", configurator = MyEndpointConfigure.class) +public class AndroidWSServer { + + private final Logger logger = LoggerFactory.getLogger(AndroidWSServer.class); + @Value("${sonic.agent.key}") + private String key; + @Value("${modules.android.use-adbkit}") + private boolean isEnableAdbKit; + private Map udIdMap = new ConcurrentHashMap<>(); + private Map> webViewForwardMap = new ConcurrentHashMap<>(); + private Map outputMap = new ConcurrentHashMap<>(); + private Map rotationMap = new ConcurrentHashMap<>(); + private Map rotationStatusMap = new ConcurrentHashMap<>(); + private Map picMap = new ConcurrentHashMap<>(); + @Autowired + private RestTemplate restTemplate; + + @OnOpen + public void onOpen(Session session, @PathParam("key") String secretKey, + @PathParam("udId") String udId, @PathParam("token") String token) throws Exception { + if (secretKey.length() == 0 || (!secretKey.equals(key)) || token.length() == 0) { + logger.info("拦截访问!"); + return; + } + + session.getUserProperties().put("udId", udId); + boolean lockSuccess = DevicesLockMap.lockByUdId(udId, 30L, TimeUnit.SECONDS); + if (!lockSuccess) { + logger.info("30s内获取设备锁失败,请确保设备无人使用"); + return; + } + logger.info("android上锁udId:{}", udId); + AndroidDeviceLocalStatus.startDebug(udId); + JSONObject jsonDebug = new JSONObject(); + jsonDebug.put("msg", "debugUser"); + jsonDebug.put("token", token); + jsonDebug.put("udId", udId); + NettyThreadPool.send(jsonDebug); + WebSocketSessionMap.addSession(session); + IDevice iDevice = AndroidDeviceBridgeTool.getIDeviceByUdId(udId); + if (iDevice == null) { + logger.info("设备未连接,请检查!"); + return; + } + AndroidDeviceBridgeTool.screen(iDevice, "abort"); + AndroidDeviceBridgeTool.pressKey(iDevice, 3); + udIdMap.put(session, iDevice); + + String path = AndroidDeviceBridgeTool.executeCommand(iDevice, "pm path org.cloud.sonic.plugins.assist").trim() + .replaceAll("package:", "") + .replaceAll("\n", "") + .replaceAll("\t", ""); + if (path.length() > 0) { + logger.info("已安装Sonic插件"); + } else { + try { + iDevice.installPackage("plugins/sonic-plugin.apk", true, "-t"); + } catch (InstallException e) { + e.printStackTrace(); + logger.info("Sonic插件安装失败!"); + return; + } + path = AndroidDeviceBridgeTool.executeCommand(iDevice, "pm path com.sonic.plugins.assist").trim() + .replaceAll("package:", "") + .replaceAll("\n", "") + .replaceAll("\t", ""); + } + + Semaphore isTouchFinish = new Semaphore(0); + String finalPath = path; + Thread rotationPro = new Thread(() -> { + try { + //开始启动 + iDevice.executeShellCommand(String.format("CLASSPATH=%s exec app_process /system/bin com.sonic.plugins.assist.RotationMonitorService", finalPath) + , new IShellOutputReceiver() { + @Override + public void addOutput(byte[] bytes, int i, int i1) { + String res = new String(bytes, i, i1).replaceAll("\n", "").replaceAll("\r", ""); + logger.info(udId + "旋转到:" + res); + rotationStatusMap.put(session, Integer.parseInt(res)); + JSONObject rotationJson = new JSONObject(); + rotationJson.put("msg", "rotation"); + rotationJson.put("value", Integer.parseInt(res) * 90); + AgentTool.sendText(session, rotationJson.toJSONString()); + Thread old = MiniCapMap.getMap().get(session); + if (old != null) { + old.interrupt(); + do { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + while (MiniCapMap.getMap().get(session) != null); + } + MiniCapTool miniCapTool = new MiniCapTool(); + AtomicReference banner = new AtomicReference<>(new String[24]); + Thread miniCapThread = miniCapTool.start( + udIdMap.get(session).getSerialNumber(), banner, null, + picMap.get(session) == null ? "high" : picMap.get(session), + Integer.parseInt(res), session + ); + MiniCapMap.getMap().put(session, miniCapThread); + JSONObject picFinish = new JSONObject(); + picFinish.put("msg", "picFinish"); + AgentTool.sendText(session, picFinish.toJSONString()); + } + + @Override + public void flush() { + } + + @Override + public boolean isCancelled() { + return false; + } + }, 0, TimeUnit.MILLISECONDS); + } catch (Exception e) { + logger.info("{} 设备方向监听启动异常!" + , iDevice.getSerialNumber()); + logger.error(e.getMessage()); + } + }); + rotationPro.start(); + rotationMap.put(session, rotationPro); + + Thread touchPro = new Thread(() -> { + try { + //开始启动 + iDevice.executeShellCommand(String.format("CLASSPATH=%s exec app_process /system/bin com.sonic.plugins.assist.SonicTouchService", finalPath) + , new IShellOutputReceiver() { + @Override + public void addOutput(byte[] bytes, int i, int i1) { + String res = new String(bytes, i, i1); + logger.info(res); + if (res.contains("Server start")) { + isTouchFinish.release(); + } + } + + @Override + public void flush() { + } + + @Override + public boolean isCancelled() { + return false; + } + }, 0, TimeUnit.MILLISECONDS); + } catch (Exception e) { + logger.info("{} 设备touch服务启动异常!" + , iDevice.getSerialNumber()); + logger.error(e.getMessage()); + } + }); + touchPro.start(); + + int finalTouchPort = PortTool.getPort(); + AndroidDeviceThreadPool.cachedThreadPool.execute(() -> { + int wait = 0; + while (!isTouchFinish.tryAcquire()) { + wait++; + try { + Thread.sleep(500); + } catch (InterruptedException e) { + e.printStackTrace(); + } + // 超时就不继续等了,保证其它服务可运行 + if (wait > 20) { + return; + } + } + AndroidDeviceBridgeTool.forward(iDevice, finalTouchPort, "sonictouchservice"); + Socket touchSocket = null; + OutputStream outputStream = null; + try { + touchSocket = new Socket("localhost", finalTouchPort); + outputStream = touchSocket.getOutputStream(); + outputMap.put(session, outputStream); + while (outputMap.get(session) != null && (touchPro.isAlive())) { + Thread.sleep(1000); + } + } catch (IOException | InterruptedException e) { + e.printStackTrace(); + } finally { + if (touchPro.isAlive()) { + touchPro.interrupt(); + logger.info("touch thread已关闭"); + } + if (touchSocket != null && touchSocket.isConnected()) { + try { + touchSocket.close(); + logger.info("touch socket已关闭"); + } catch (IOException e) { + e.printStackTrace(); + } + } + if (outputStream != null) { + try { + outputStream.close(); + logger.info("touch output流已关闭"); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + AndroidDeviceBridgeTool.removeForward(iDevice, finalTouchPort, "sonictouchservice"); + }); + + AndroidDeviceThreadPool.cachedThreadPool.execute(() -> AndroidDeviceBridgeTool.pushYadb(udIdMap.get(session))); + + if (isEnableAdbKit) { + String processName = String.format("process-%s-adbkit", udId); + if (GlobalProcessMap.getMap().get(processName) != null) { + Process ps = GlobalProcessMap.getMap().get(processName); + ps.children().forEach(ProcessHandle::destroy); + ps.destroy(); + } + try { + String system = System.getProperty("os.name").toLowerCase(); + Process ps = null; + int port = PortTool.getPort(); + String command = String.format("adbkit usb-device-to-tcp -p %d %s", port, udId); + if (system.contains("win")) { + ps = Runtime.getRuntime().exec(new String[]{"cmd", "/c", command}); + } else if (system.contains("linux") || system.contains("mac")) { + ps = Runtime.getRuntime().exec(new String[]{"sh", "-c", command}); + } + GlobalProcessMap.getMap().put(processName, ps); + JSONObject adbkit = new JSONObject(); + adbkit.put("msg", "adbkit"); + adbkit.put("isEnable", true); + adbkit.put("port", port); + AgentTool.sendText(session, adbkit.toJSONString()); + } catch (IOException e) { + e.printStackTrace(); + } + } else { + JSONObject adbkit = new JSONObject(); + adbkit.put("msg", "adbkit"); + adbkit.put("isEnable", false); + AgentTool.sendText(session, adbkit.toJSONString()); + } + + AndroidDeviceThreadPool.cachedThreadPool.execute(() -> { + AndroidStepHandler androidStepHandler = new AndroidStepHandler(); + androidStepHandler.setTestMode(0, 0, udId, DeviceStatus.DEBUGGING, session.getId()); + JSONObject result = new JSONObject(); + try { + androidStepHandler.startAndroidDriver(udId); + result.put("status", "success"); + result.put("detail", "初始化Driver完成!"); + HandlerMap.getAndroidMap().put(session.getId(), androidStepHandler); + } catch (Exception e) { + logger.error(e.getMessage()); + result.put("status", "error"); + result.put("detail", "初始化Driver失败!部分功能不可用!请联系管理员"); + } finally { + result.put("msg", "openDriver"); + AgentTool.sendText(session, result.toJSONString()); + } + }); + } + + @OnClose + public void onClose(Session session) { + String udId = (String) session.getUserProperties().get("udId"); + try { + exit(session); + } finally { + DevicesLockMap.unlockAndRemoveByUdId(udId); + logger.info("android解锁udId:{}", udId); + } + } + + @OnError + public void onError(Session session, Throwable error) { + logger.error(error.getMessage()); + JSONObject errMsg = new JSONObject(); + errMsg.put("msg", "error"); + AgentTool.sendText(session, errMsg.toJSONString()); + } + + @OnMessage + public void onMessage(String message, Session session) throws InterruptedException { + JSONObject msg = JSON.parseObject(message); + logger.info(session.getId() + " 发送 " + msg); + switch (msg.getString("type")) { + case "forwardView": { + JSONObject forwardView = new JSONObject(); + IDevice iDevice = udIdMap.get(session); + List wList = Arrays.asList("webview", "WebView", "chrome_devtools_remote", "Terrace_devtools_remote"); + 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()) { + webSet.add(w.substring(w.indexOf("@") + 1)); + } + } + List has = webViewForwardMap.get(iDevice); + if (has != null && has.size() > 0) { + for (JSONObject j : has) { + AndroidDeviceBridgeTool.removeForward(iDevice, j.getInteger("port"), j.getString("name")); + } + } + has = new ArrayList<>(); + List result = new ArrayList<>(); + if (webViewList.size() > 0) { + HttpHeaders headers = new HttpHeaders(); + headers.add("Content-Type", "application/json"); + for (String ws : webSet) { + int port = PortTool.getPort(); + AndroidDeviceBridgeTool.forward(iDevice, port, ws); + JSONObject j = new JSONObject(); + j.put("port", port); + j.put("name", ws); + has.add(j); + JSONObject r = new JSONObject(); + r.put("port", port); + 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); + if (responseEntity.getStatusCode() == HttpStatus.OK) { + List child = new ArrayList<>(); + for (Object e : responseEntity.getBody()) { + LinkedHashMap objE = (LinkedHashMap) e; + JSONObject c = new JSONObject(); + c.put("favicon", objE.get("faviconUrl")); + c.put("title", objE.get("title")); + c.put("url", objE.get("url")); + c.put("id", objE.get("id")); + child.add(c); + } + r.put("children", child); + result.add(r); + } + } + webViewForwardMap.put(iDevice, has); + } + forwardView.put("msg", "forwardView"); + if (RemoteDebugDriver.webDriver == null) { + RemoteDebugDriver.startChromeDriver(); + } + forwardView.put("chromePort", RemoteDebugDriver.chromePort); + forwardView.put("detail", result); + AgentTool.sendText(session, forwardView.toJSONString()); + break; + } + case "find": + AndroidDeviceBridgeTool.searchDevice(udIdMap.get(session)); + break; + case "battery": + AndroidDeviceBridgeTool.controlBattery(udIdMap.get(session), msg.getInteger("detail")); + break; + case "scan": + AndroidDeviceBridgeTool.pushToCamera(udIdMap.get(session), msg.getString("url")); + break; + case "text": + ProcessCommandTool.getProcessLocalCommand("adb -s " + udIdMap.get(session).getSerialNumber() + + " shell app_process -Djava.class.path=/data/local/tmp/yadb /data/local/tmp com.ysbing.yadb.Main -keyboard " + msg.getString("detail")); + break; + case "pic": { + Thread old = MiniCapMap.getMap().get(session); + old.interrupt(); + do { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + while (MiniCapMap.getMap().get(session) != null); + MiniCapTool miniCapTool = new MiniCapTool(); + AtomicReference banner = new AtomicReference<>(new String[24]); + Thread miniCapThread = miniCapTool.start( + udIdMap.get(session).getSerialNumber(), banner, null, msg.getString("detail"), + rotationStatusMap.get(session), session + ); + MiniCapMap.getMap().put(session, miniCapThread); + JSONObject picFinish = new JSONObject(); + picFinish.put("msg", "picFinish"); + AgentTool.sendText(session, picFinish.toJSONString()); + break; + } + case "touch": + OutputStream outputStream = outputMap.get(session); + if (outputStream != null) { + try { + outputStream.write(msg.getString("detail").getBytes()); + outputStream.flush(); + outputStream.write("c\n".getBytes()); + outputStream.flush(); + } catch (IOException e) { + e.printStackTrace(); + } + } + break; + case "keyEvent": + AndroidDeviceBridgeTool.pressKey(udIdMap.get(session), msg.getInteger("detail")); + break; + case "debug": + if (msg.getString("detail").equals("runStep")) { + JSONObject jsonDebug = new JSONObject(); + jsonDebug.put("msg", "findSteps"); + jsonDebug.put("key", key); + jsonDebug.put("udId", udIdMap.get(session).getSerialNumber()); + jsonDebug.put("pwd", msg.getString("pwd")); + jsonDebug.put("sessionId", session.getId()); + jsonDebug.put("caseId", msg.getInteger("caseId")); + NettyThreadPool.send(jsonDebug); + } else { + AndroidStepHandler androidStepHandler = HandlerMap.getAndroidMap().get(session.getId()); + if (androidStepHandler == null || androidStepHandler.getAndroidDriver() == null) { + if (msg.getString("detail").equals("openDriver")) { + androidStepHandler = new AndroidStepHandler(); + androidStepHandler.setTestMode(0, 0, udIdMap.get(session).getSerialNumber(), DeviceStatus.DEBUGGING, session.getId()); + JSONObject result = new JSONObject(); + AndroidStepHandler finalAndroidStepHandler1 = androidStepHandler; + AndroidDeviceThreadPool.cachedThreadPool.execute(() -> { + try { + AndroidDeviceLocalStatus.startDebug(udIdMap.get(session).getSerialNumber()); + finalAndroidStepHandler1.startAndroidDriver(udIdMap.get(session).getSerialNumber()); + result.put("status", "success"); + result.put("detail", "初始化Driver完成!"); + HandlerMap.getAndroidMap().put(session.getId(), finalAndroidStepHandler1); + } catch (Exception e) { + logger.error(e.getMessage()); + result.put("status", "error"); + result.put("detail", "初始化Driver失败!部分功能不可用!请联系管理员"); + } finally { + result.put("msg", "openDriver"); + AgentTool.sendText(session, result.toJSONString()); + } + }); + } + break; + } + try { + if (msg.getString("detail").equals("tap")) { + String xy = msg.getString("point"); + int x = Integer.parseInt(xy.substring(0, xy.indexOf(","))); + int y = Integer.parseInt(xy.substring(xy.indexOf(",") + 1)); + AndroidDeviceBridgeTool.executeCommand(udIdMap.get(session), "input tap " + x + " " + y); + } + if (msg.getString("detail").equals("longPress")) { + String xy = msg.getString("point"); + int x = Integer.parseInt(xy.substring(0, xy.indexOf(","))); + int y = Integer.parseInt(xy.substring(xy.indexOf(",") + 1)); + AndroidDeviceBridgeTool.executeCommand(udIdMap.get(session), "input swipe " + x + " " + y + " " + x + " " + y + " 1500"); + } + if (msg.getString("detail").equals("swipe")) { + String xy1 = msg.getString("pointA"); + String xy2 = msg.getString("pointB"); + int x1 = Integer.parseInt(xy1.substring(0, xy1.indexOf(","))); + 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 + " 200"); + } + } catch (Exception e) { + e.printStackTrace(); + } + if (msg.getString("detail").equals("install")) { + AndroidDeviceThreadPool.cachedThreadPool.execute(() -> { + JSONObject result = new JSONObject(); + result.put("msg", "installFinish"); + try { + File localFile = DownImageTool.download(msg.getString("apk")); + udIdMap.get(session).installPackage(localFile.getAbsolutePath(), true, "-t"); + result.put("status", "success"); + } catch (IOException | InstallException e) { + result.put("status", "fail"); + e.printStackTrace(); + } + AgentTool.sendText(session, result.toJSONString()); + }); + } + if (msg.getString("detail").equals("tree")) { + AndroidStepHandler finalAndroidStepHandler = androidStepHandler; + AndroidDeviceThreadPool.cachedThreadPool.execute(() -> { + try { + JSONObject result = new JSONObject(); + result.put("msg", "tree"); + result.put("detail", finalAndroidStepHandler.getResource()); + HandleDes handleDes = new HandleDes(); + if (!msg.getBoolean("hasScreen")) { + result.put("img", finalAndroidStepHandler.stepScreen(handleDes)); + } + if (handleDes.getE() != null) { + logger.error(handleDes.getE().getMessage()); + JSONObject resultFail = new JSONObject(); + resultFail.put("msg", "treeFail"); + AgentTool.sendText(session, resultFail.toJSONString()); + } else { + result.put("webView", finalAndroidStepHandler.getWebView()); + result.put("activity", finalAndroidStepHandler.getCurrentActivity()); + AgentTool.sendText(session, result.toJSONString()); + } + } catch (Throwable e) { + logger.error(e.getMessage()); + JSONObject result = new JSONObject(); + result.put("msg", "treeFail"); + AgentTool.sendText(session, result.toJSONString()); + } + }); + } + if (msg.getString("detail").equals("eleScreen")) { + AndroidStepHandler finalAndroidStepHandler = androidStepHandler; + AndroidDeviceThreadPool.cachedThreadPool.execute(() -> { + JSONObject result = new JSONObject(); + result.put("msg", "eleScreen"); + try { + result.put("img", UploadTools.upload(finalAndroidStepHandler.findEle("xpath", msg.getString("xpath")).getScreenshotAs(OutputType.FILE), "keepFiles")); + } catch (Exception e) { + result.put("errMsg", "获取元素截图失败!"); + } + AgentTool.sendText(session, result.toJSONString()); + }); + } + } + break; + } + } + + private void exit(Session session) { + AndroidDeviceLocalStatus.finish(session.getUserProperties().get("udId") + ""); + try { + HandlerMap.getAndroidMap().get(session.getId()).closeAndroidDriver(); + } catch (Exception e) { + logger.info("关闭driver异常!"); + } finally { + HandlerMap.getAndroidMap().remove(session.getId()); + } + if (udIdMap.get(session) != null) { + List has = webViewForwardMap.get(udIdMap.get(session)); + if (has != null && has.size() > 0) { + for (JSONObject j : has) { + AndroidDeviceBridgeTool.removeForward(udIdMap.get(session), j.getInteger("port"), j.getString("name")); + } + } + webViewForwardMap.remove(udIdMap.get(session)); + if (isEnableAdbKit) { + String processName = String.format("process-%s-adbkit", udIdMap.get(session).getSerialNumber()); + if (GlobalProcessMap.getMap().get(processName) != null) { + Process ps = GlobalProcessMap.getMap().get(processName); + ps.children().forEach(ProcessHandle::destroy); + ps.destroy(); + } + } + } + outputMap.remove(session); + udIdMap.remove(session); + if (rotationMap.get(session) != null) { + rotationMap.get(session).interrupt(); + } + rotationMap.remove(session); + if (MiniCapMap.getMap().get(session) != null) { + MiniCapMap.getMap().get(session).interrupt(); + } + WebSocketSessionMap.removeSession(session); + try { + session.close(); + } catch (IOException e) { + e.printStackTrace(); + } + logger.info(session.getId() + "退出"); + } +} diff --git a/src/main/java/com/sonic/agent/websockets/IOSWSServer.java b/src/main/java/org/cloud/sonic/agent/websockets/IOSWSServer.java similarity index 95% rename from src/main/java/com/sonic/agent/websockets/IOSWSServer.java rename to src/main/java/org/cloud/sonic/agent/websockets/IOSWSServer.java index efc65b0b..10ff47a0 100644 --- a/src/main/java/com/sonic/agent/websockets/IOSWSServer.java +++ b/src/main/java/org/cloud/sonic/agent/websockets/IOSWSServer.java @@ -1,20 +1,19 @@ -package com.sonic.agent.websockets; +package org.cloud.sonic.agent.websockets; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; -import com.android.ddmlib.InstallException; -import com.sonic.agent.automation.HandleDes; -import com.sonic.agent.automation.IOSStepHandler; -import com.sonic.agent.bridge.ios.IOSDeviceLocalStatus; -import com.sonic.agent.bridge.ios.IOSDeviceThreadPool; -import com.sonic.agent.bridge.ios.TIDeviceTool; -import com.sonic.agent.interfaces.DeviceStatus; -import com.sonic.agent.maps.DevicesLockMap; -import com.sonic.agent.maps.HandlerMap; -import com.sonic.agent.maps.WebSocketSessionMap; -import com.sonic.agent.netty.NettyThreadPool; -import com.sonic.agent.tools.DownImageTool; -import com.sonic.agent.tools.UploadTools; +import org.cloud.sonic.agent.automation.HandleDes; +import org.cloud.sonic.agent.automation.IOSStepHandler; +import org.cloud.sonic.agent.bridge.ios.IOSDeviceLocalStatus; +import org.cloud.sonic.agent.bridge.ios.IOSDeviceThreadPool; +import org.cloud.sonic.agent.bridge.ios.TIDeviceTool; +import org.cloud.sonic.agent.interfaces.DeviceStatus; +import org.cloud.sonic.agent.maps.DevicesLockMap; +import org.cloud.sonic.agent.maps.HandlerMap; +import org.cloud.sonic.agent.maps.WebSocketSessionMap; +import org.cloud.sonic.agent.netty.NettyThreadPool; +import org.cloud.sonic.agent.tools.DownImageTool; +import org.cloud.sonic.agent.tools.UploadTools; import io.appium.java_client.TouchAction; import io.appium.java_client.touch.WaitOptions; import io.appium.java_client.touch.offset.PointOption; @@ -34,8 +33,6 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; -import static com.sonic.agent.tools.AgentTool.sendText; - @Component @ServerEndpoint(value = "/websockets/ios/{key}/{udId}/{token}", configurator = MyEndpointConfigure.class) public class IOSWSServer { diff --git a/src/main/java/com/sonic/agent/websockets/MyEndpointConfigure.java b/src/main/java/org/cloud/sonic/agent/websockets/MyEndpointConfigure.java similarity index 92% rename from src/main/java/com/sonic/agent/websockets/MyEndpointConfigure.java rename to src/main/java/org/cloud/sonic/agent/websockets/MyEndpointConfigure.java index 18f5d433..ed7ebc3d 100644 --- a/src/main/java/com/sonic/agent/websockets/MyEndpointConfigure.java +++ b/src/main/java/org/cloud/sonic/agent/websockets/MyEndpointConfigure.java @@ -1,23 +1,23 @@ -package com.sonic.agent.websockets; - -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.BeanFactory; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; - -import javax.websocket.server.ServerEndpointConfig; - -public class MyEndpointConfigure extends ServerEndpointConfig.Configurator implements ApplicationContextAware { - - private static volatile BeanFactory context; - - @Override - public T getEndpointInstance(Class clazz){ - return context.getBean(clazz); - } - - @Override - public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { - MyEndpointConfigure.context = applicationContext; - } +package org.cloud.sonic.agent.websockets; + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; + +import javax.websocket.server.ServerEndpointConfig; + +public class MyEndpointConfigure extends ServerEndpointConfig.Configurator implements ApplicationContextAware { + + private static volatile BeanFactory context; + + @Override + public T getEndpointInstance(Class clazz){ + return context.getBean(clazz); + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + MyEndpointConfigure.context = applicationContext; + } } \ No newline at end of file diff --git a/src/main/java/com/sonic/agent/websockets/TerminalWSServer.java b/src/main/java/org/cloud/sonic/agent/websockets/TerminalWSServer.java similarity index 95% rename from src/main/java/com/sonic/agent/websockets/TerminalWSServer.java rename to src/main/java/org/cloud/sonic/agent/websockets/TerminalWSServer.java index b2bc2ad7..96a2327f 100644 --- a/src/main/java/com/sonic/agent/websockets/TerminalWSServer.java +++ b/src/main/java/org/cloud/sonic/agent/websockets/TerminalWSServer.java @@ -1,220 +1,220 @@ -package com.sonic.agent.websockets; - -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.JSONObject; -import com.android.ddmlib.*; -import com.sonic.agent.bridge.android.AndroidDeviceBridgeTool; -import com.sonic.agent.bridge.android.AndroidDeviceThreadPool; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; - -import javax.websocket.*; -import javax.websocket.server.PathParam; -import javax.websocket.server.ServerEndpoint; -import java.io.IOException; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; - -/** - * @author ZhouYiXun - * @des - * @date 2021/10/30 23:35 - */ -@Component -@ServerEndpoint(value = "/websockets/terminal/{key}/{udId}", configurator = MyEndpointConfigure.class) -public class TerminalWSServer { - private final Logger logger = LoggerFactory.getLogger(TerminalWSServer.class); - @Value("${sonic.agent.key}") - private String key; - private Map udIdMap = new ConcurrentHashMap<>(); - private Map> terminalMap = new ConcurrentHashMap<>(); - private Map> logcatMap = new ConcurrentHashMap<>(); - - @OnOpen - public void onOpen(Session session, @PathParam("key") String secretKey, @PathParam("udId") String udId) throws Exception { - if (secretKey.length() == 0 || (!secretKey.equals(key))) { - logger.info("拦截访问!"); - return; - } - IDevice iDevice = AndroidDeviceBridgeTool.getIDeviceByUdId(udId); - udIdMap.put(session, iDevice); - String username = iDevice.getProperty("ro.product.device"); - Future terminal = AndroidDeviceThreadPool.cachedThreadPool.submit(() -> { - logger.info(udId + "开启terminal"); - JSONObject ter = new JSONObject(); - ter.put("msg", "terminal"); - ter.put("user", username); - sendText(session, ter.toJSONString()); - }); - Future logcat = AndroidDeviceThreadPool.cachedThreadPool.submit(() -> { - logger.info(udId + "开启logcat"); - JSONObject ter = new JSONObject(); - ter.put("msg", "logcat"); - sendText(session, ter.toJSONString()); - }); - terminalMap.put(session, terminal); - logcatMap.put(session, logcat); - } - - @OnMessage - public void onMessage(String message, Session session) { - JSONObject msg = JSON.parseObject(message); - logger.info(session.getId() + " 发送 " + msg); - switch (msg.getString("type")) { - case "stopCmd": - Future ter = terminalMap.get(session); - if (!ter.isDone() || !ter.isCancelled()) { - try { - ter.cancel(true); - } catch (Exception e) { - logger.error(e.getMessage()); - } - } - break; - case "command": - if (msg.getString("detail").contains("reboot") - || msg.getString("detail").contains("rm") - || msg.getString("detail").contains("su ")) { - JSONObject done = new JSONObject(); - done.put("msg", "terDone"); - sendText(session, done.toJSONString()); - return; - } - ter = AndroidDeviceThreadPool.cachedThreadPool.submit(() -> { - try { - udIdMap.get(session).executeShellCommand(msg.getString("detail"), new IShellOutputReceiver() { - @Override - public void addOutput(byte[] bytes, int i, int i1) { - String res = new String(bytes, i, i1); - JSONObject resp = new JSONObject(); - resp.put("msg", "terResp"); - resp.put("detail", res); - sendText(session, resp.toJSONString()); - } - - @Override - public void flush() { - } - - @Override - public boolean isCancelled() { - return false; - } - }, 0, TimeUnit.MILLISECONDS); - } catch (Throwable e) { - return; - } - JSONObject done = new JSONObject(); - done.put("msg", "terDone"); - sendText(session, done.toJSONString()); - }); - terminalMap.put(session, ter); - break; - case "stopLogcat": { - Future logcat = logcatMap.get(session); - if (!logcat.isDone() || !logcat.isCancelled()) { - try { - logcat.cancel(true); - } catch (Exception e) { - logger.error(e.getMessage()); - } - } - break; - } - case "logcat": { - Future logcat = logcatMap.get(session); - if (!logcat.isDone() || !logcat.isCancelled()) { - try { - logcat.cancel(true); - } catch (Exception e) { - logger.error(e.getMessage()); - } - } - logcat = AndroidDeviceThreadPool.cachedThreadPool.submit(() -> { - try { - udIdMap.get(session).executeShellCommand("logcat *:" - + msg.getString("level") + - (msg.getString("filter").length() > 0 ? - " | grep " + msg.getString("filter") : ""), new IShellOutputReceiver() { - @Override - public void addOutput(byte[] bytes, int i, int i1) { - String res = new String(bytes, i, i1); - JSONObject resp = new JSONObject(); - resp.put("msg", "logcatResp"); - resp.put("detail", res); - sendText(session, resp.toJSONString()); - } - - @Override - public void flush() { - } - - @Override - public boolean isCancelled() { - return false; - } - }, 0, TimeUnit.MILLISECONDS); - } catch (Throwable e) { - return; - } - }); - logcatMap.put(session, logcat); - break; - } - } - } - - @OnClose - public void onClose(Session session) { - exit(session); - } - - @OnError - public void onError(Session session, Throwable error) { - logger.error(error.getMessage()); - JSONObject errMsg = new JSONObject(); - errMsg.put("msg", "error"); - sendText(session, errMsg.toJSONString()); - } - - private void exit(Session session) { - Future cmd = terminalMap.get(session); - if (!cmd.isDone() || !cmd.isCancelled()) { - try { - cmd.cancel(true); - } catch (Exception e) { - logger.error(e.getMessage()); - } - } - terminalMap.remove(session); - Future logcat = logcatMap.get(session); - if (!logcat.isDone() || !logcat.isCancelled()) { - try { - logcat.cancel(true); - } catch (Exception e) { - logger.error(e.getMessage()); - } - } - logcatMap.remove(session); - try { - session.close(); - } catch (IOException e) { - e.printStackTrace(); - } - logger.info(session.getId() + "退出"); - } - - private void sendText(Session session, String message) { - synchronized (session) { - try { - session.getBasicRemote().sendText(message); - } catch (IllegalStateException | IOException e) { - logger.error("webSocket发送失败!连接已关闭!"); - } - } - } -} +package org.cloud.sonic.agent.websockets; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.android.ddmlib.*; +import org.cloud.sonic.agent.bridge.android.AndroidDeviceBridgeTool; +import org.cloud.sonic.agent.bridge.android.AndroidDeviceThreadPool; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import javax.websocket.*; +import javax.websocket.server.PathParam; +import javax.websocket.server.ServerEndpoint; +import java.io.IOException; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +/** + * @author ZhouYiXun + * @des + * @date 2021/10/30 23:35 + */ +@Component +@ServerEndpoint(value = "/websockets/terminal/{key}/{udId}", configurator = MyEndpointConfigure.class) +public class TerminalWSServer { + private final Logger logger = LoggerFactory.getLogger(TerminalWSServer.class); + @Value("${sonic.agent.key}") + private String key; + private Map udIdMap = new ConcurrentHashMap<>(); + private Map> terminalMap = new ConcurrentHashMap<>(); + private Map> logcatMap = new ConcurrentHashMap<>(); + + @OnOpen + public void onOpen(Session session, @PathParam("key") String secretKey, @PathParam("udId") String udId) throws Exception { + if (secretKey.length() == 0 || (!secretKey.equals(key))) { + logger.info("拦截访问!"); + return; + } + IDevice iDevice = AndroidDeviceBridgeTool.getIDeviceByUdId(udId); + udIdMap.put(session, iDevice); + String username = iDevice.getProperty("ro.product.device"); + Future terminal = AndroidDeviceThreadPool.cachedThreadPool.submit(() -> { + logger.info(udId + "开启terminal"); + JSONObject ter = new JSONObject(); + ter.put("msg", "terminal"); + ter.put("user", username); + sendText(session, ter.toJSONString()); + }); + Future logcat = AndroidDeviceThreadPool.cachedThreadPool.submit(() -> { + logger.info(udId + "开启logcat"); + JSONObject ter = new JSONObject(); + ter.put("msg", "logcat"); + sendText(session, ter.toJSONString()); + }); + terminalMap.put(session, terminal); + logcatMap.put(session, logcat); + } + + @OnMessage + public void onMessage(String message, Session session) { + JSONObject msg = JSON.parseObject(message); + logger.info(session.getId() + " 发送 " + msg); + switch (msg.getString("type")) { + case "stopCmd": + Future ter = terminalMap.get(session); + if (!ter.isDone() || !ter.isCancelled()) { + try { + ter.cancel(true); + } catch (Exception e) { + logger.error(e.getMessage()); + } + } + break; + case "command": + if (msg.getString("detail").contains("reboot") + || msg.getString("detail").contains("rm") + || msg.getString("detail").contains("su ")) { + JSONObject done = new JSONObject(); + done.put("msg", "terDone"); + sendText(session, done.toJSONString()); + return; + } + ter = AndroidDeviceThreadPool.cachedThreadPool.submit(() -> { + try { + udIdMap.get(session).executeShellCommand(msg.getString("detail"), new IShellOutputReceiver() { + @Override + public void addOutput(byte[] bytes, int i, int i1) { + String res = new String(bytes, i, i1); + JSONObject resp = new JSONObject(); + resp.put("msg", "terResp"); + resp.put("detail", res); + sendText(session, resp.toJSONString()); + } + + @Override + public void flush() { + } + + @Override + public boolean isCancelled() { + return false; + } + }, 0, TimeUnit.MILLISECONDS); + } catch (Throwable e) { + return; + } + JSONObject done = new JSONObject(); + done.put("msg", "terDone"); + sendText(session, done.toJSONString()); + }); + terminalMap.put(session, ter); + break; + case "stopLogcat": { + Future logcat = logcatMap.get(session); + if (!logcat.isDone() || !logcat.isCancelled()) { + try { + logcat.cancel(true); + } catch (Exception e) { + logger.error(e.getMessage()); + } + } + break; + } + case "logcat": { + Future logcat = logcatMap.get(session); + if (!logcat.isDone() || !logcat.isCancelled()) { + try { + logcat.cancel(true); + } catch (Exception e) { + logger.error(e.getMessage()); + } + } + logcat = AndroidDeviceThreadPool.cachedThreadPool.submit(() -> { + try { + udIdMap.get(session).executeShellCommand("logcat *:" + + msg.getString("level") + + (msg.getString("filter").length() > 0 ? + " | grep " + msg.getString("filter") : ""), new IShellOutputReceiver() { + @Override + public void addOutput(byte[] bytes, int i, int i1) { + String res = new String(bytes, i, i1); + JSONObject resp = new JSONObject(); + resp.put("msg", "logcatResp"); + resp.put("detail", res); + sendText(session, resp.toJSONString()); + } + + @Override + public void flush() { + } + + @Override + public boolean isCancelled() { + return false; + } + }, 0, TimeUnit.MILLISECONDS); + } catch (Throwable e) { + return; + } + }); + logcatMap.put(session, logcat); + break; + } + } + } + + @OnClose + public void onClose(Session session) { + exit(session); + } + + @OnError + public void onError(Session session, Throwable error) { + logger.error(error.getMessage()); + JSONObject errMsg = new JSONObject(); + errMsg.put("msg", "error"); + sendText(session, errMsg.toJSONString()); + } + + private void exit(Session session) { + Future cmd = terminalMap.get(session); + if (!cmd.isDone() || !cmd.isCancelled()) { + try { + cmd.cancel(true); + } catch (Exception e) { + logger.error(e.getMessage()); + } + } + terminalMap.remove(session); + Future logcat = logcatMap.get(session); + if (!logcat.isDone() || !logcat.isCancelled()) { + try { + logcat.cancel(true); + } catch (Exception e) { + logger.error(e.getMessage()); + } + } + logcatMap.remove(session); + try { + session.close(); + } catch (IOException e) { + e.printStackTrace(); + } + logger.info(session.getId() + "退出"); + } + + private void sendText(Session session, String message) { + synchronized (session) { + try { + session.getBasicRemote().sendText(message); + } catch (IllegalStateException | IOException e) { + logger.error("webSocket发送失败!连接已关闭!"); + } + } + } +} diff --git a/src/main/java/com/sonic/agent/websockets/WebSocketConfig.java b/src/main/java/org/cloud/sonic/agent/websockets/WebSocketConfig.java similarity index 90% rename from src/main/java/com/sonic/agent/websockets/WebSocketConfig.java rename to src/main/java/org/cloud/sonic/agent/websockets/WebSocketConfig.java index 150d715a..e7b4e519 100644 --- a/src/main/java/com/sonic/agent/websockets/WebSocketConfig.java +++ b/src/main/java/org/cloud/sonic/agent/websockets/WebSocketConfig.java @@ -1,18 +1,18 @@ -package com.sonic.agent.websockets; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.web.socket.server.standard.ServerEndpointExporter; - -@Configuration -public class WebSocketConfig { - @Bean - public ServerEndpointExporter serverEndpointExporter() { - return new ServerEndpointExporter(); - } - - @Bean - public MyEndpointConfigure newMyEndpointConfigure (){ - return new MyEndpointConfigure (); - } +package org.cloud.sonic.agent.websockets; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.socket.server.standard.ServerEndpointExporter; + +@Configuration +public class WebSocketConfig { + @Bean + public ServerEndpointExporter serverEndpointExporter() { + return new ServerEndpointExporter(); + } + + @Bean + public MyEndpointConfigure newMyEndpointConfigure (){ + return new MyEndpointConfigure (); + } } \ No newline at end of file diff --git a/src/main/java/com/sonic/agent/websockets/WebViewWSServer.java b/src/main/java/org/cloud/sonic/agent/websockets/WebViewWSServer.java similarity index 94% rename from src/main/java/com/sonic/agent/websockets/WebViewWSServer.java rename to src/main/java/org/cloud/sonic/agent/websockets/WebViewWSServer.java index 5fd1abdc..68cfaa70 100644 --- a/src/main/java/com/sonic/agent/websockets/WebViewWSServer.java +++ b/src/main/java/org/cloud/sonic/agent/websockets/WebViewWSServer.java @@ -1,95 +1,94 @@ -package com.sonic.agent.websockets; - -import com.alibaba.fastjson.JSONObject; -import org.java_websocket.client.WebSocketClient; -import org.java_websocket.handshake.ServerHandshake; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; - -import javax.websocket.*; -import javax.websocket.server.PathParam; -import javax.websocket.server.ServerEndpoint; -import java.io.IOException; -import java.net.URI; -import java.util.HashMap; -import java.util.Map; - -/** - * @author ZhouYiXun - * @des - * @date 2021/10/25 23:03 - */ -@Component -@ServerEndpoint(value = "/websockets/webView/{key}/{port}/{id}", configurator = MyEndpointConfigure.class) -public class WebViewWSServer { - private final Logger logger = LoggerFactory.getLogger(WebViewWSServer.class); - @Value("${sonic.agent.key}") - private String key; - private Map sessionWebSocketClientMap = new HashMap<>(); - - @OnOpen - public void onOpen(Session session, @PathParam("key") String secretKey, @PathParam("port") int port, @PathParam("id") String id) throws Exception { - if (secretKey.length() == 0 || (!secretKey.equals(key))) { - logger.info("拦截访问!"); - return; - } - URI uri = new URI("ws://localhost:" + port + "/devtools/page/" + id); - WebSocketClient webSocketClient = new WebSocketClient(uri) { - @Override - public void onOpen(ServerHandshake serverHandshake) { - logger.info("连接成功!"); - } - - @Override - public void onMessage(String s) { - sendText(session, s); - } - - @Override - public void onClose(int i, String s, boolean b) { - logger.info("连接断开!"); - } - - @Override - public void onError(Exception e) { - - } - }; - webSocketClient.connect(); - sessionWebSocketClientMap.put(session, webSocketClient); - } - - @OnMessage - public void onMessage(String message, Session session) throws InterruptedException { - if (sessionWebSocketClientMap.get(session) != null) { - try { - sessionWebSocketClientMap.get(session).send(message); - } catch (Exception e) { - - } - } - } - - @OnClose - public void onClose(Session session) { - sessionWebSocketClientMap.get(session).close(); - sessionWebSocketClientMap.remove(session); - } - - @OnError - public void onError(Session session, Throwable error) { - logger.error(error.getMessage()); - } - - private void sendText(Session session, String message) { - synchronized (session) { - try { - session.getBasicRemote().sendText(message); - } catch (IllegalStateException | IOException e) { - logger.error("webSocket发送失败!连接已关闭!"); - } - } - } -} +package org.cloud.sonic.agent.websockets; + +import org.java_websocket.client.WebSocketClient; +import org.java_websocket.handshake.ServerHandshake; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import javax.websocket.*; +import javax.websocket.server.PathParam; +import javax.websocket.server.ServerEndpoint; +import java.io.IOException; +import java.net.URI; +import java.util.HashMap; +import java.util.Map; + +/** + * @author ZhouYiXun + * @des + * @date 2021/10/25 23:03 + */ +@Component +@ServerEndpoint(value = "/websockets/webView/{key}/{port}/{id}", configurator = MyEndpointConfigure.class) +public class WebViewWSServer { + private final Logger logger = LoggerFactory.getLogger(WebViewWSServer.class); + @Value("${sonic.agent.key}") + private String key; + private Map sessionWebSocketClientMap = new HashMap<>(); + + @OnOpen + public void onOpen(Session session, @PathParam("key") String secretKey, @PathParam("port") int port, @PathParam("id") String id) throws Exception { + if (secretKey.length() == 0 || (!secretKey.equals(key))) { + logger.info("拦截访问!"); + return; + } + URI uri = new URI("ws://localhost:" + port + "/devtools/page/" + id); + WebSocketClient webSocketClient = new WebSocketClient(uri) { + @Override + public void onOpen(ServerHandshake serverHandshake) { + logger.info("连接成功!"); + } + + @Override + public void onMessage(String s) { + sendText(session, s); + } + + @Override + public void onClose(int i, String s, boolean b) { + logger.info("连接断开!"); + } + + @Override + public void onError(Exception e) { + + } + }; + webSocketClient.connect(); + sessionWebSocketClientMap.put(session, webSocketClient); + } + + @OnMessage + public void onMessage(String message, Session session) throws InterruptedException { + if (sessionWebSocketClientMap.get(session) != null) { + try { + sessionWebSocketClientMap.get(session).send(message); + } catch (Exception e) { + + } + } + } + + @OnClose + public void onClose(Session session) { + sessionWebSocketClientMap.get(session).close(); + sessionWebSocketClientMap.remove(session); + } + + @OnError + public void onError(Session session, Throwable error) { + logger.error(error.getMessage()); + } + + private void sendText(Session session, String message) { + synchronized (session) { + try { + session.getBasicRemote().sendText(message); + } catch (IllegalStateException | IOException e) { + logger.error("webSocket发送失败!连接已关闭!"); + } + } + } +}