package com.zy.asrs.service.impl;
|
|
import com.baomidou.mybatisplus.mapper.EntityWrapper;
|
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
|
import com.zy.asrs.entity.TvDevice;
|
import com.zy.asrs.mapper.TvDeviceMapper;
|
import com.zy.asrs.service.TvDeviceService;
|
import org.slf4j.Logger;
|
import org.slf4j.LoggerFactory;
|
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.stereotype.Service;
|
|
import java.io.BufferedReader;
|
import java.io.ByteArrayOutputStream;
|
import java.io.File;
|
import java.io.InputStream;
|
import java.io.InputStreamReader;
|
import java.util.ArrayList;
|
import java.util.Arrays;
|
import java.util.Base64;
|
import java.util.Date;
|
import java.util.List;
|
|
/**
|
* 电视机设备Service实现类
|
*/
|
@Service
|
public class TvDeviceServiceImpl extends ServiceImpl<TvDeviceMapper, TvDevice> implements TvDeviceService {
|
|
private static final Logger log = LoggerFactory.getLogger(TvDeviceServiceImpl.class);
|
|
@Value("${adb.path}")
|
private String adbPath;
|
|
@Value("${adb.default-package:com.zy.monitor}")
|
private String defaultPackage;
|
|
@Override
|
public String testConnection(Long id) throws Exception {
|
TvDevice device = this.selectById(id);
|
if (device == null) {
|
throw new RuntimeException("设备不存在");
|
}
|
|
StringBuilder result = new StringBuilder();
|
String adbAddress = device.getAdbAddress();
|
|
try {
|
// 断开已有连接
|
executeAdbCommand("disconnect", adbAddress);
|
Thread.sleep(500);
|
|
// 尝试连接
|
String connectResult = executeAdbCommand("connect", adbAddress);
|
result.append("连接: ").append(connectResult).append("\n");
|
|
// 检查连接状态
|
boolean connected = connectResult.contains("connected") && !connectResult.contains("failed");
|
|
if (connected) {
|
// 获取设备信息
|
String devicesResult = executeAdbCommand("devices");
|
result.append("设备列表: ").append(devicesResult);
|
|
// 更新设备状态
|
device.setStatus((short) 1);
|
device.setLastConnectTime(new Date());
|
} else {
|
device.setStatus((short) 0);
|
}
|
|
device.setUpdateTime(new Date());
|
this.updateById(device);
|
|
return result.toString();
|
} catch (Exception e) {
|
device.setStatus((short) 0);
|
device.setUpdateTime(new Date());
|
this.updateById(device);
|
throw e;
|
}
|
}
|
|
@Override
|
public int refreshAllStatus() {
|
List<TvDevice> devices = this.selectList(null);
|
int count = 0;
|
for (TvDevice device : devices) {
|
try {
|
testConnection(device.getId());
|
count++;
|
} catch (Exception e) {
|
log.error("测试设备 {} 连接失败: {}", device.getName(), e.getMessage());
|
}
|
}
|
return count;
|
}
|
|
@Override
|
public String installApk(Long deviceId, String apkPath) throws Exception {
|
TvDevice device = this.selectById(deviceId);
|
if (device == null) {
|
throw new RuntimeException("设备不存在");
|
}
|
|
File apkFile = new File(apkPath);
|
if (!apkFile.exists()) {
|
throw new RuntimeException("APK文件不存在: " + apkPath);
|
}
|
|
StringBuilder result = new StringBuilder();
|
String adbAddress = device.getAdbAddress();
|
|
// 先连接设备
|
String connectResult = executeAdbCommand("connect", adbAddress);
|
result.append("连接: ").append(connectResult).append("\n");
|
|
if (connectResult.contains("failed")) {
|
device.setStatus((short) 0);
|
device.setUpdateTime(new Date());
|
this.updateById(device);
|
throw new RuntimeException("连接设备失败: " + connectResult);
|
}
|
|
// 等待连接稳定
|
Thread.sleep(1000);
|
|
// 安装APK
|
String installResult = executeAdbCommand("-s", adbAddress, "install", "-r", apkPath);
|
result.append("安装: ").append(installResult);
|
|
// 更新设备状态
|
device.setStatus((short) 1);
|
device.setLastConnectTime(new Date());
|
device.setUpdateTime(new Date());
|
this.updateById(device);
|
|
log.info("设备 {} 安装APK结果: {}", device.getName(), installResult);
|
return result.toString();
|
}
|
|
@Override
|
public List<String> batchInstallApk(List<Long> deviceIds, String apkPath) {
|
List<String> results = new ArrayList<>();
|
for (Long deviceId : deviceIds) {
|
TvDevice device = this.selectById(deviceId);
|
String deviceName = device != null ? device.getName() : "ID:" + deviceId;
|
try {
|
String result = installApk(deviceId, apkPath);
|
results.add(deviceName + ": 安装成功\n" + result);
|
} catch (Exception e) {
|
results.add(deviceName + ": 安装失败 - " + e.getMessage());
|
}
|
}
|
return results;
|
}
|
|
@Override
|
public List<TvDevice> getOnlineDevices() {
|
return this.selectList(new EntityWrapper<TvDevice>().eq("status", 1));
|
}
|
|
@Override
|
public String launchApp(Long deviceId, String packageName) throws Exception {
|
TvDevice device = this.selectById(deviceId);
|
if (device == null) {
|
throw new RuntimeException("设备不存在");
|
}
|
|
// 使用默认包名如果未指定
|
String pkg = (packageName != null && !packageName.trim().isEmpty()) ? packageName.trim() : defaultPackage;
|
|
StringBuilder result = new StringBuilder();
|
String adbAddress = device.getAdbAddress();
|
|
// 先连接设备
|
String connectResult = executeAdbCommand("connect", adbAddress);
|
result.append("连接: ").append(connectResult).append("\n");
|
|
if (connectResult.contains("failed")) {
|
device.setStatus((short) 0);
|
device.setUpdateTime(new Date());
|
this.updateById(device);
|
throw new RuntimeException("连接设备失败: " + connectResult);
|
}
|
|
// 等待连接稳定
|
Thread.sleep(500);
|
|
// 方法1: 使用cmd package resolve-activity获取启动Activity(Android 7.0+)
|
String launchResult = null;
|
boolean launched = false;
|
|
try {
|
// 尝试使用dumpsys获取启动Activity
|
String dumpResult = executeAdbCommand("-s", adbAddress, "shell",
|
"cmd", "package", "resolve-activity", "--brief", pkg);
|
|
if (dumpResult != null && dumpResult.contains("/")) {
|
// 解析出Activity名称
|
String[] lines = dumpResult.split("\n");
|
for (String line : lines) {
|
line = line.trim();
|
if (line.contains("/") && !line.startsWith("priority") && !line.startsWith("#")) {
|
// 找到类似 com.zy.app/.MainActivity 的格式
|
launchResult = executeAdbCommand("-s", adbAddress, "shell",
|
"am", "start", "-n", line);
|
launched = true;
|
break;
|
}
|
}
|
}
|
} catch (Exception e) {
|
log.warn("cmd package方式失败,尝试备用方式: {}", e.getMessage());
|
}
|
|
// 方法2: 如果方法1失败,使用am start -a android.intent.action.MAIN
|
if (!launched) {
|
launchResult = executeAdbCommand("-s", adbAddress, "shell",
|
"am", "start", "-a", "android.intent.action.MAIN",
|
"-c", "android.intent.category.LAUNCHER",
|
"-n", pkg + "/.MainActivity");
|
|
// 如果还是失败,尝试常见的Activity名称
|
if (launchResult != null && launchResult.contains("Error")) {
|
// 尝试 .ui.MainActivity
|
launchResult = executeAdbCommand("-s", adbAddress, "shell",
|
"am", "start", "-a", "android.intent.action.MAIN",
|
"-c", "android.intent.category.LAUNCHER",
|
"--package", pkg);
|
}
|
}
|
|
result.append("启动: ").append(launchResult);
|
|
// 检查是否启动成功
|
if (launchResult != null && (launchResult.contains("Starting:") || launchResult.contains("cmp="))) {
|
log.info("设备 {} 启动应用 {} 成功", device.getName(), pkg);
|
} else {
|
log.warn("设备 {} 启动应用 {} 可能失败: {}", device.getName(), pkg, launchResult);
|
}
|
|
// 更新设备状态
|
device.setStatus((short) 1);
|
device.setLastConnectTime(new Date());
|
device.setUpdateTime(new Date());
|
this.updateById(device);
|
|
return result.toString();
|
}
|
|
@Override
|
public List<String> batchLaunchApp(List<Long> deviceIds, String packageName) {
|
List<String> results = new ArrayList<>();
|
for (Long deviceId : deviceIds) {
|
TvDevice device = this.selectById(deviceId);
|
String deviceName = device != null ? device.getName() : "ID:" + deviceId;
|
try {
|
String result = launchApp(deviceId, packageName);
|
results.add(deviceName + ": 启动成功\n" + result);
|
} catch (Exception e) {
|
results.add(deviceName + ": 启动失败 - " + e.getMessage());
|
}
|
}
|
return results;
|
}
|
|
@Override
|
public String captureScreen(Long deviceId) throws Exception {
|
TvDevice device = this.selectById(deviceId);
|
if (device == null) {
|
throw new RuntimeException("设备不存在");
|
}
|
|
String adbAddress = device.getAdbAddress();
|
|
// 先确保连接
|
String connectResult = executeAdbCommand("connect", adbAddress);
|
if (connectResult.contains("failed")) {
|
device.setStatus((short) 0);
|
device.setUpdateTime(new Date());
|
this.updateById(device);
|
throw new RuntimeException("连接设备失败: " + connectResult);
|
}
|
|
// 使用exec-out直接获取PNG二进制数据
|
List<String> command = new ArrayList<>();
|
command.add(adbPath);
|
command.add("-s");
|
command.add(adbAddress);
|
command.add("exec-out");
|
command.add("screencap");
|
command.add("-p");
|
|
log.info("执行截图命令: {}", String.join(" ", command));
|
|
ProcessBuilder pb = new ProcessBuilder(command);
|
Process process = pb.start();
|
|
// 读取二进制PNG数据
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
InputStream is = process.getInputStream();
|
byte[] buffer = new byte[8192];
|
int len;
|
while ((len = is.read(buffer)) != -1) {
|
baos.write(buffer, 0, len);
|
}
|
|
int exitCode = process.waitFor();
|
byte[] pngData = baos.toByteArray();
|
|
if (exitCode != 0 || pngData.length < 100) {
|
// 读取错误信息
|
BufferedReader errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
|
StringBuilder errorMsg = new StringBuilder();
|
String line;
|
while ((line = errorReader.readLine()) != null) {
|
errorMsg.append(line);
|
}
|
log.error("截图失败,退出码: {}, 数据长度: {}, 错误: {}", exitCode, pngData.length, errorMsg);
|
throw new RuntimeException("截图失败: " + errorMsg);
|
}
|
|
// 更新设备状态
|
device.setStatus((short) 1);
|
device.setLastConnectTime(new Date());
|
device.setUpdateTime(new Date());
|
this.updateById(device);
|
|
// 转换为Base64
|
String base64Image = Base64.getEncoder().encodeToString(pngData);
|
log.info("设备 {} 截图成功,图片大小: {} bytes", device.getName(), pngData.length);
|
|
return base64Image;
|
}
|
|
@Override
|
public String stopApp(Long deviceId, String packageName) throws Exception {
|
TvDevice device = this.selectById(deviceId);
|
if (device == null) {
|
throw new RuntimeException("设备不存在");
|
}
|
|
String pkg = (packageName != null && !packageName.trim().isEmpty()) ? packageName.trim() : defaultPackage;
|
StringBuilder result = new StringBuilder();
|
String adbAddress = device.getAdbAddress();
|
|
// 先连接设备
|
String connectResult = executeAdbCommand("connect", adbAddress);
|
result.append("连接: ").append(connectResult).append("\n");
|
|
if (connectResult.contains("failed")) {
|
device.setStatus((short) 0);
|
device.setUpdateTime(new Date());
|
this.updateById(device);
|
throw new RuntimeException("连接设备失败: " + connectResult);
|
}
|
|
Thread.sleep(500);
|
|
// 使用am force-stop关闭应用
|
String stopResult = executeAdbCommand("-s", adbAddress, "shell", "am", "force-stop", pkg);
|
result.append("关闭: ").append(stopResult.isEmpty() ? "成功" : stopResult);
|
|
// 更新设备状态
|
device.setStatus((short) 1);
|
device.setLastConnectTime(new Date());
|
device.setUpdateTime(new Date());
|
this.updateById(device);
|
|
log.info("设备 {} 关闭应用 {} 成功", device.getName(), pkg);
|
return result.toString();
|
}
|
|
@Override
|
public String restartApp(Long deviceId, String packageName) throws Exception {
|
StringBuilder result = new StringBuilder();
|
|
// 先关闭应用
|
String stopResult = stopApp(deviceId, packageName);
|
result.append("【关闭应用】\n").append(stopResult).append("\n\n");
|
|
// 等待1秒确保应用完全关闭
|
Thread.sleep(1000);
|
|
// 再启动应用
|
String launchResult = launchApp(deviceId, packageName);
|
result.append("【启动应用】\n").append(launchResult);
|
|
return result.toString();
|
}
|
|
@Override
|
public List<String> batchRestartApp(List<Long> deviceIds, String packageName) {
|
List<String> results = new ArrayList<>();
|
for (Long deviceId : deviceIds) {
|
TvDevice device = this.selectById(deviceId);
|
String deviceName = device != null ? device.getName() : "ID:" + deviceId;
|
try {
|
String result = restartApp(deviceId, packageName);
|
results.add(deviceName + ": 重启成功\n" + result);
|
} catch (Exception e) {
|
results.add(deviceName + ": 重启失败 - " + e.getMessage());
|
}
|
}
|
return results;
|
}
|
|
/**
|
* 执行ADB命令
|
*/
|
private String executeAdbCommand(String... args) throws Exception {
|
List<String> command = new ArrayList<>();
|
command.add(adbPath);
|
command.addAll(Arrays.asList(args));
|
|
log.info("执行ADB命令: {}", String.join(" ", command));
|
|
ProcessBuilder pb = new ProcessBuilder(command);
|
pb.redirectErrorStream(true);
|
|
Process process = pb.start();
|
|
StringBuilder output = new StringBuilder();
|
try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
|
String line;
|
while ((line = reader.readLine()) != null) {
|
output.append(line).append("\n");
|
}
|
}
|
|
int exitCode = process.waitFor();
|
String result = output.toString().trim();
|
|
if (exitCode != 0 && !result.contains("Success") && !result.contains("connected")) {
|
log.warn("ADB命令执行返回码: {}, 输出: {}", exitCode, result);
|
}
|
|
return result;
|
}
|
}
|