android设备创建driver时指定固定的端口

master
yineng.huang 2024-08-12 00:06:57 +08:00
parent 2fa3e721df
commit 5de13aaf46
12 changed files with 270 additions and 57 deletions

View File

@ -24,5 +24,7 @@ public class DeviceInfo{
private String resolution; //分辨率
private int forwardPort; //android在appium自动化时的adb转发端口
private Map<String,String> otherInfo;
}

View File

@ -230,15 +230,23 @@ public class MobileDeviceConnection extends AbstractDeviceConnection {
return desiredCapabilities;
}
private AndroidMobileDeviceDriver createAndroidDriver(URL appiumServerUrl, String deviceId, Map<String, String> others) {
AndroidMobileDeviceDriver driver = null;
private AndroidMobileDeviceDriver createAndroidDriver(URL appiumServerUrl, String upperUrl, String deviceId, Map<String, String> others) {
DesiredCapabilities capabilities = initAndroidCapabilities(deviceId, others);
AndroidMobileDeviceDriver driver = null;
try {
driver = new AndroidMobileDeviceDriver(appiumServerUrl, capabilities);
return driver;
} catch (SessionNotCreatedException e) {
log.error("设备未连接到上位机", e);
throw new ExecuteException("设备无法初始化,请将设备重新拔插后再试");
log.error("Driver创建失败设备连接失败", e);
String errorMessage = String.format("the local port #%s is busy", deviceInfo.getForwardPort());
if (e.getMessage().contains(errorMessage)) {
log.warn("设备【{}】adb转发的端口【{}】被占用了,释放了重新来", deviceId, deviceInfo.getForwardPort());
boolean success = AndroidUtil.releaseAdbForwardPort(this.remoteAddress, deviceId);
if (success) {
driver = new AndroidMobileDeviceDriver(appiumServerUrl, capabilities);
} else {
log.warn("设备【{}】adb转发的端口【{}】被占用了,没有释放成功", deviceId, deviceInfo.getForwardPort());
}
}
} catch (WebDriverException e) {
log.warn("设备【{}】Driver创建失败重新试一次", deviceId, e);
try {
@ -254,6 +262,30 @@ public class MobileDeviceConnection extends AbstractDeviceConnection {
return driver;
}
// private AndroidMobileDeviceDriver createAndroidDriver(URL appiumServerUrl, String deviceId, Map<String, String> others) {
// AndroidMobileDeviceDriver driver = null;
// DesiredCapabilities capabilities = initAndroidCapabilities(deviceId, others);
// try {
// driver = new AndroidMobileDeviceDriver(appiumServerUrl, capabilities);
// return driver;
// } catch (SessionNotCreatedException e) {
// log.error("设备未连接到上位机", e);
// throw new ExecuteException("设备无法初始化,请将设备重新拔插后再试");
// } catch (WebDriverException e) {
// log.warn("设备【{}】Driver创建失败重新试一次", deviceId, e);
// try {
// Thread.sleep(1500);
// } catch (InterruptedException interruptedException) {
// log.warn("设备【{}】移除应用时driver出现问题,重新执行。。。。。", deviceInfo.getDeviceId());
// throw new ExecuteException("执行取消");
// }
// driver = new AndroidMobileDeviceDriver(appiumServerUrl, capabilities);
// } catch (Exception e) {
// log.error("设备驱动创建失败", e);
// }
// return driver;
// }
private DesiredCapabilities initAndroidCapabilities(String deviceId, Map<String, String> others) {
DesiredCapabilities desiredCapabilities = new DesiredCapabilities();
desiredCapabilities.setCapability(DEVICE_NAME, deviceId); //设备id
@ -262,6 +294,7 @@ public class MobileDeviceConnection extends AbstractDeviceConnection {
desiredCapabilities.setCapability("appium:noSign", true);
desiredCapabilities.setCapability("appium:skipServerInstallation", false);
desiredCapabilities.setCapability("appium:skipUnlock", true);
desiredCapabilities.setCapability("appium:systemPort", deviceInfo.getForwardPort());
boolean noReset = true;
boolean unicodeKeyboard = false;
boolean resetKeyboard = true;
@ -769,7 +802,7 @@ public class MobileDeviceConnection extends AbstractDeviceConnection {
*/
private void appRestart(String appPackage, String appName) {
if (Thread.currentThread().isInterrupted()) {
log.warn("设备【{}】app时前停止。。。。。。。。。。", deviceInfo.getDeviceId());
log.warn("设备【{}】启app时前停止。。。。。。。。。。", deviceInfo.getDeviceId());
throw new ExecuteException("执行停止");
}
boolean appInstalled = isAppInstalled(appPackage);

View File

@ -51,8 +51,17 @@ public class DeviceConnectionManager implements DeviceConnectionService {
deviceInfo.setSystemVersion(((Map)(data.get("cdMobileDevice"))).get("platformVersion").toString());
deviceInfo.setResolution(((Map)(data.get("cdDeviceModel"))).get("resolution").toString());
deviceInfo.setPort(((Map)(data.get("cdDeviceCtrl"))).get("ctrlPort").toString());
if ("1".equals(deviceInfo.getPlatform())) {
deviceInfo.setWdaAddress(((Map)(data.get("deviceInfo"))).get("wdaUrl").toString());
if ("1".equals(deviceInfo.getPlatform())) { //ios
Map deviceMap = (Map) data.get("deviceInfo");
Object wdaUrl = deviceMap.get("wdaUrl");
if (null != wdaUrl) {
deviceInfo.setWdaAddress(wdaUrl + "");
}
} else if ("0".equals(deviceInfo.getPlatform())){ //android
Map deviceMap = (Map) data.get("deviceInfo");
Object forwardPort = deviceMap.get("forwardPort");
log.debug("拿到设备【{}】在上位机用于adb转发的端口{}",deviceInfo.getDeviceId(),forwardPort);
deviceInfo.setForwardPort(Integer.parseInt(forwardPort+""));
}
} else { // pc设备
deviceInfo.setMobile(false);

View File

@ -21,6 +21,19 @@ public class AndroidUtil {
private static final String END_RECORD_URL = "http://%s/engine/endRecord";
private static final String INSTALL_APP = "http://%s/engine/installApp";
private static final String ACTIVE_APP = "http://%s/engine/activeApp";
private static final String RELEASE_ADB_FORWARD_PORT = "http://%s/engine/releaseAdbForwardPort";
public static boolean releaseAdbForwardPort(String remoteAddress,String deviceId){
try {
DebuggerDeviceInfo info = new DebuggerDeviceInfo();
info.setDeviceId(deviceId);
HttpEntity<DebuggerDeviceInfo> entity = new HttpEntity<>(info);
return HttpUtils.doPost(String.format(RELEASE_ADB_FORWARD_PORT, remoteAddress),entity, Boolean.class);
} catch (Exception e) {
logger.error("清除数据失败,原因:{}",e);
return false;
}
}
public static boolean cleanAppData(String remoteAddress,String deviceId,String appPackage){
try {

View File

@ -314,4 +314,9 @@ public class EngineController {
return iosService.terminateApp(info.getDeviceId(), info.getAppPackage());
}
@PostMapping("/releaseAdbForwardPort")
public boolean releaseAdbForwardPort(@RequestBody DebuggerDeviceInfo info){
return androidService.releaseAdbForwardPort(info.getDeviceId());
}
}

View File

@ -9,9 +9,10 @@ import net.northking.cctp.upperComputer.driver.adb.AndroidDeviceListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import javax.websocket.Session;
import java.io.IOException;
import java.io.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
@ -33,6 +34,15 @@ public class AndroidDeviceManager extends AbstractDeviceManager {
private Adb adb;
/**
*
*/
private int initPort = 8200;
private Properties phonePortProperties = new Properties();
private final String portFileName = "androidPhonePort.properties";
private final static Logger logger = LoggerFactory.getLogger(AndroidDeviceManager.class);
/**
@ -74,10 +84,6 @@ public class AndroidDeviceManager extends AbstractDeviceManager {
deviceSystemIsReady.put(serial, status);
}
@Override
public boolean getDeviceSystemStatus(String serial) {
return deviceSystemIsReady.get(serial);
}
@Override
public void shutdown() {
@ -108,6 +114,7 @@ public class AndroidDeviceManager extends AbstractDeviceManager {
@Override
public void run() {
readAndroidForwardPortProperty();
while (!isInterrupted()) {
startAdbServer();
try {
@ -141,8 +148,9 @@ public class AndroidDeviceManager extends AbstractDeviceManager {
for (AdbDevice adbDevice : onlineDevices) {
logger.info("新上来设备:{}", JSON.toJSONString(adbDevice));
if (AdbDevice.State.Device == adbDevice.state) {
Integer forwardPort = saveDeviceForwardPort(adbDevice.getSerial());
//注册并启动agent
AndroidDeviceInitThread androidDeviceInitThread = new AndroidDeviceInitThread(adb,adbDevice);
AndroidDeviceInitThread androidDeviceInitThread = new AndroidDeviceInitThread(adb,adbDevice,forwardPort);
androidDeviceInitThread.start();
onlineDeviceInitMap.put(adbDevice.getSerial(), androidDeviceInitThread);
publishDeviceOnlineToWebConnection(adbDevice.getSerial());
@ -164,7 +172,17 @@ public class AndroidDeviceManager extends AbstractDeviceManager {
}
}
private Integer saveDeviceForwardPort(String serial) {
String value = this.phonePortProperties.getProperty(serial);
if (StringUtils.hasText(value)) {
logger.info("设备【】已经上过线分配过adb转发端口端口为{}.............", serial, value);
} else {
doPropertiesWrite(serial);
value = this.phonePortProperties.getProperty(serial);
logger.info("设备【】第一次来分配adb转发端口端口为{}.............", serial,value);
}
return Integer.parseInt(value);
}
/**
* 线线
@ -232,16 +250,6 @@ public class AndroidDeviceManager extends AbstractDeviceManager {
return list;
}
@Override
public void offlineDevice(String serial) {
synchronized (onlineDeviceInitMap){
AndroidDeviceInitThread initThread = onlineDeviceInitMap.remove(serial);
if (null != initThread && initThread.isAlive() && !initThread.isInterrupted()) {
logger.info("设备【{}】初始化线程准备退出",serial);
initThread.exit();
}
}
}
@Override
public void stopWebScreen(String udid, Session session) {
@ -251,4 +259,74 @@ public class AndroidDeviceManager extends AbstractDeviceManager {
}
}
private void readAndroidForwardPortProperty() {
String property = System.getProperty("user.dir");
File file = new File(property + File.separatorChar + portFileName);
logger.info("开始读取已经存在的android手机对应的端口读取文件{}", file.getAbsolutePath());
if (!file.exists()) {
logger.warn("android手机端口映射文件不存在新建《{}》文件",portFileName);
try {
boolean newFile = file.createNewFile();
if (!newFile) {
logger.warn("android端口映射文件【{}】创建失败", file.getAbsolutePath());
}
} catch (IOException e) {
logger.warn("新建android手机端口映射文件失败", e);
}
} else {
FileInputStream inputStream = null;
try {
inputStream = new FileInputStream(file);
phonePortProperties.load(inputStream);
} catch (FileNotFoundException e) {
logger.warn("android手机端口映射文件不存在");
} catch (IOException e) {
logger.warn("读取android手机端口映射配置文件失败");
} finally {
if (null != inputStream) {
try {
inputStream.close();
} catch (IOException e) {
logger.warn("关闭流出错", e);
}
}
}
logger.info("android手机端口映射文件加载完成加载内容");
for (Object key : phonePortProperties.keySet()) {
String port = phonePortProperties.getProperty(key + "");
int forwardPort = Integer.parseInt(port);
logger.info("手机:{},adb映射的端口为:{}", key, port);
if (initPort < forwardPort) {
initPort = forwardPort;
}
}
}
}
private synchronized int getForwardPort() {
this.initPort++;
return this.initPort;
}
private synchronized void doPropertiesWrite(String deviceId) {
this.phonePortProperties.setProperty(deviceId, getForwardPort()+""); //存好分配的端口
String property = System.getProperty("user.dir");
File file = new File(property + File.separatorChar + portFileName);
FileOutputStream fileOutputStream = null;
try {
fileOutputStream = new FileOutputStream(file);
this.phonePortProperties.store(fileOutputStream, "android设备adb转发端口的映射关系");
} catch (IOException e) {
logger.warn("写入端口映射文件失败");
}finally {
if (null != fileOutputStream) {
try {
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}

View File

@ -44,6 +44,8 @@ public class IOSDeviceManager extends AbstractDeviceManager {
*/
public String defaultXcodePath = "/Applications/Xcode.app";
private final String portFileName = "iosPhonePort.properties";
/**
*
*/
@ -165,17 +167,17 @@ public class IOSDeviceManager extends AbstractDeviceManager {
private void readIosPhoneProperty() {
String property = System.getProperty("user.dir");
File file = new File(property + "/phonePort.properties");
logger.info("开始读取已经存在的手机端口和wda端口映射读取文件{}", file.getAbsolutePath());
File file = new File(property + File.separatorChar + portFileName);
logger.info("开始读取已经存在的ios手机端口和wda端口映射读取文件{}", file.getAbsolutePath());
if (!file.exists()) {
logger.warn("手机端口映射文件不存在新建《phonePort.properties》文件");
logger.warn("ios手机端口映射文件不存在新建《{}》文件", portFileName);
try {
boolean newFile = file.createNewFile();
if (!newFile) {
logger.warn("端口映射文件【{}】创建失败", file.getAbsolutePath());
logger.warn("ios手机端口映射文件【{}】创建失败", file.getAbsolutePath());
}
} catch (IOException e) {
logger.warn("新建端口映射文件失败", e);
logger.warn("新建ios手机端口映射文件失败", e);
}
} else {
FileInputStream inputStream = null;
@ -183,9 +185,9 @@ public class IOSDeviceManager extends AbstractDeviceManager {
inputStream = new FileInputStream(file);
phonePortProperties.load(inputStream);
} catch (FileNotFoundException e) {
logger.warn("手机端口映射文件不存在");
logger.warn("ios手机端口映射文件不存在");
} catch (IOException e) {
logger.warn("读取手机端口映射配置文件失败");
logger.warn("读取ios手机端口映射配置文件失败");
} finally {
if (null != inputStream) {
try {
@ -195,7 +197,7 @@ public class IOSDeviceManager extends AbstractDeviceManager {
}
}
}
logger.info("手机端口映射文件加载完成,加载内容:");
logger.info("ios手机端口映射文件加载完成,加载内容:");
for (Object key : phonePortProperties.keySet()) {
String port = phonePortProperties.getProperty(key + "");
PhoneEntity entity = new PhoneEntity();
@ -216,6 +218,7 @@ public class IOSDeviceManager extends AbstractDeviceManager {
/**
*
*
@ -245,11 +248,21 @@ public class IOSDeviceManager extends AbstractDeviceManager {
private synchronized void doPropertiesWrite(String key, String value) {
this.phonePortProperties.setProperty(key, value); //存好分配的端口
String property = System.getProperty("user.dir");
File file = new File(property + "/phonePort.properties");
File file = new File(property + File.separatorChar + portFileName);
FileOutputStream fileOutputStream = null;
try {
this.phonePortProperties.store(new FileOutputStream(file), "IOS设备WDA映射关系");
fileOutputStream = new FileOutputStream(file);
this.phonePortProperties.store(fileOutputStream, "IOS设备WDA端口映射关系");
} catch (IOException e) {
logger.warn("写入端口映射文件失败");
}finally {
if (null != fileOutputStream) {
try {
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

View File

@ -53,11 +53,14 @@ public class AndroidDeviceInitThread extends Thread {
private AgentThread agentThread;
private Integer forwardPort;
private int time = 1;
public AndroidDeviceInitThread(Adb adb, AdbDevice adbDevice) {
public AndroidDeviceInitThread(Adb adb, AdbDevice adbDevice, Integer forwardPort) {
this.adb = adb;
this.adbDevice = adbDevice;
this.forwardPort = forwardPort;
setName(adbDevice.getSerial());
}
@ -415,6 +418,7 @@ public class AndroidDeviceInitThread extends Thread {
wifiTransport.close();
deviceInfo.put("wifiStatus", wifiStatus);
deviceInfo.put("scale", 1.0);
deviceInfo.put("forwardPort", forwardPort);
return deviceInfo;
}
@ -445,6 +449,10 @@ public class AndroidDeviceInitThread extends Thread {
return agentStatus;
}
public Integer getForwardPort(){
return forwardPort;
}
private class AgentThread extends Thread {
@Override

View File

@ -129,4 +129,9 @@ public abstract class AbstractDebuggerService implements DebuggerService {
public String[] stopRecordDevicePer(DebuggerDeviceInfo info) {
return new String[0];
}
@Override
public boolean releaseAdbForwardPort(String deviceId) {
return false;
}
}

View File

@ -168,6 +168,50 @@ public class AndroidDebuggerServiceImpl extends AbstractDebuggerService {
return result;
}
@Override
public boolean releaseAdbForwardPort(String deviceId) {
AndroidDeviceInitThread androidInitThread = AndroidDeviceManager.getInstance().getAndroidInitThread(deviceId);
if (null == androidInitThread || !androidInitThread.isAlive() || androidInitThread.isInterrupted()) {
logger.warn("当前设备【{}】不在线", deviceId);
return false;
}
Integer forwardPort = androidInitThread.getForwardPort();
List<String> cmd = new ArrayList<>();
cmd.add("adb");
cmd.add("-s");
cmd.add(deviceId);
cmd.add("forward");
cmd.add("--remove");
cmd.add("tcp:"+forwardPort);
try {
Process start = new ProcessBuilder(cmd).redirectErrorStream(true).start();
InputStream inputStream = start.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
StringBuilder builder = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
logger.debug("执行设备【{}】释放adb转发端口打印{}",deviceId,line);
builder.append(line);
}
String result = builder.toString();
String notUseFlag = String.format("listener 'tcp:%s' not found", forwardPort);
if (StringUtils.isBlank(result)) {
logger.info("设备【{}】adb端口{}释放成功", deviceId, forwardPort);
return true;
} else if (result.contains(notUseFlag)) {
logger.info("设备【{}】adb端口{}没有被adb占用", deviceId, forwardPort);
return true;
} else {
logger.info("设备【{}】adb端口{}被其他程序占用,请杀掉无关的程序", deviceId, forwardPort);
logger.debug("详情:{}",result);
return false;
}
} catch (IOException e) {
logger.error("执行移除设备【{}】adb转发端口报错了",deviceId,e);
return false;
}
}
@Override
public boolean terminateApp(String deviceId, String appPackage) {
return false;

View File

@ -51,4 +51,6 @@ public interface DebuggerService {
String[] stopRecordDevicePer(DebuggerDeviceInfo info);
boolean terminateApp(String deviceId, String appPackage);
boolean releaseAdbForwardPort(String deviceId);
}

View File

@ -737,27 +737,28 @@ public class AndroidMessageHandlerThread extends AbstractMessageHandler {
logger.info("应用总大小:{}", totalSpace);
FileInputStream fileInputStream = new FileInputStream(appFile);
DecimalFormat decimalFormat = new DecimalFormat("#.00");
boolean push = AndroidDeviceManager.getInstance().getAdb().push(adbDevice, fileInputStream, Integer.parseInt(appFile.lastModified() / 1000 + ""), 777, "/data/local/tmp/tmp_install.apk", new PushBytesCountCallback() {
private double percentOld = 0;
private long oldTime = System.currentTimeMillis();
@Override
public void onSendBytes(long totalCount) {
Double percent = totalCount / totalSpace.doubleValue() * 100;
String format = decimalFormat.format(percent);
double percentNew = Double.parseDouble(format);
logger.debug("传输大小:{},总大小:{}", totalCount, totalSpace);
long nowTime = System.currentTimeMillis();
if (percentNew - percentOld > 5 && nowTime - oldTime > 1000) {
logger.debug("传输进度:{}", format);
Map<String, Object> result = new HashMap<>();
result.put("percent", percentNew / 2);
percentOld = percentNew;
oldTime = nowTime;
SessionUtils.sendContinuousData(session, request, result, "应用安装进度");
}
}
});
boolean push = AndroidDeviceManager.getInstance().getAdb().push(adbDevice, fileInputStream, Integer.parseInt(appFile.lastModified() / 1000 + ""), 777, "/data/local/tmp/tmp_install.apk", null);
// new PushBytesCountCallback() {
// private double percentOld = 0;
// private long oldTime = System.currentTimeMillis();
//
// @Override
// public void onSendBytes(long totalCount) {
// Double percent = totalCount / totalSpace.doubleValue() * 100;
// String format = decimalFormat.format(percent);
// double percentNew = Double.parseDouble(format);
// logger.debug("传输大小:{},总大小:{}", totalCount, totalSpace);
// long nowTime = System.currentTimeMillis();
// if (percentNew - percentOld > 5 && nowTime - oldTime > 1000) {
// logger.debug("传输进度:{}", format);
// Map<String, Object> result = new HashMap<>();
// result.put("percent", percentNew / 2);
// percentOld = percentNew;
// oldTime = nowTime;
// SessionUtils.sendContinuousData(session, request, result, "应用安装进度");
// }
// }
// });
if (push) {
Map<String, Object> result = new HashMap<>();
result.put("percent", 50);