You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

310 lines
15 KiB

"""
增强的ADB服务模块 - 提供业务逻辑层
"""
import asyncio
from typing import List, Optional, AsyncGenerator
from app.core.adb import AdbClient
from app.models.adb_models import (
DeviceInfo, DeviceEvent, ForwardInfo, ServerInfo,
ShellCommand, ShellResponse, ForwardRequest, DeviceStatus
)
from app.utils.log import get_enhanced_logger, LogLevel
from app.core.exceptions import AdbConnectionError, AdbProtocolError
logger = get_enhanced_logger(__name__, LogLevel.DEBUG)
class EnhancedAdbService:
"""增强的ADB服务类"""
def __init__(self, host: Optional[str] = None, port: Optional[int] = None):
self._client = AdbClient()
self._host = host
self._port = port
self._device_monitor_task: Optional[asyncio.Task] = None
self._monitoring_started = False
# 移除自动启动监控,改为延迟启动
async def start_monitoring(self):
"""启动设备监控(需要手动调用)"""
if not self._monitoring_started and (not self._device_monitor_task or self._device_monitor_task.done()):
logger.info("开始设备监控", host=self._host, port=self._port)
try:
self._device_monitor_task = asyncio.create_task(self._monitor_devices())
self._monitoring_started = True
logger.info("设备监控任务创建成功", task_id=id(self._device_monitor_task))
except Exception as e:
logger.error("创建设备监控任务失败", error=str(e))
self._monitoring_started = False
raise
else:
logger.info("设备监控已在运行中", monitoring_started=self._monitoring_started, task_done=self._device_monitor_task.done() if self._device_monitor_task else None)
async def stop_monitoring(self):
"""停止设备监控"""
if self._device_monitor_task and not self._device_monitor_task.done():
logger.info("正在停止设备监控", task_id=id(self._device_monitor_task))
self._device_monitor_task.cancel()
try:
await self._device_monitor_task
logger.info("设备监控任务已成功停止")
except asyncio.CancelledError:
logger.info("设备监控任务被取消")
except Exception as e:
logger.error("停止设备监控任务时发生错误", error=str(e))
finally:
self._monitoring_started = False
logger.info("设备监控已完全停止")
else:
logger.info("设备监控未在运行,无需停止")
async def get_server_info(self) -> ServerInfo:
"""获取ADB服务器信息"""
try:
logger.debug("获取ADB服务器信息")
version = await self._client.server_version()
logger.info("获取ADB服务器信息成功", version=version, host=self._host, port=self._port)
return ServerInfo(version=version, host=self._host or "localhost", port=self._port or 5037, status="running")
except (AdbConnectionError, AdbProtocolError) as e:
logger.error("获取ADB服务器信息失败", error=str(e))
raise
except Exception as e:
logger.error("获取ADB服务器信息时发生未知错误", error=str(e))
raise
async def list_devices(self, status_filter: Optional[List[str]] = None) -> List[DeviceInfo]:
"""获取设备列表"""
try:
logger.debug("获取设备列表", status_filter=status_filter)
devices = await self._client.devices()
# 过滤设备状态
if status_filter:
devices = [d for d in devices if d.status in status_filter]
# 转换为DeviceInfo对象
device_infos = []
for d in devices:
try:
# 尝试将字符串状态转换为枚举
if hasattr(d.status, 'value'):
status_str = d.status.value
else:
status_str = str(d.status)
# 尝试转换为已知的状态枚举
try:
status = DeviceStatus(status_str)
except ValueError:
# 如果状态不在已知枚举中,使用默认状态
logger.warning(f"未知设备状态: {status_str}, 使用默认状态", serial=d.serial)
status = DeviceStatus.OFFLINE
device_infos.append(DeviceInfo(serial=d.serial, status=status))
except Exception as e:
logger.error(f"处理设备信息时出错: {d.serial}, 错误: {e}")
# 跳过有问题的设备
continue
logger.info("获取设备列表成功", device_count=len(device_infos))
return device_infos
except (AdbConnectionError, AdbProtocolError) as e:
logger.error("获取设备列表失败", error=str(e))
raise
except Exception as e:
logger.error("获取设备列表时发生未知错误", error=str(e))
raise
async def execute_shell_command(self, device_serial: str, command: str, timeout: int = 30) -> ShellResponse:
"""执行Shell命令"""
try:
logger.debug("执行Shell命令", device_serial=device_serial, command=command, timeout=timeout)
output = await asyncio.wait_for(
self._client.shell(device_serial, command),
timeout=timeout
)
logger.info("Shell命令执行成功", device_serial=device_serial, command=command, output_length=len(output))
return ShellResponse(success=True, output=output, error=None, exit_code=0)
except asyncio.TimeoutError:
logger.warning("Shell命令执行超时", device_serial=device_serial, command=command, timeout=timeout)
return ShellResponse(success=False, output="", error="命令执行超时", exit_code=-1)
except (AdbConnectionError, AdbProtocolError) as e:
logger.error("Shell命令执行失败", device_serial=device_serial, command=command, error=str(e))
return ShellResponse(success=False, output="", error=str(e), exit_code=-1)
except Exception as e:
logger.error("Shell命令执行时发生未知错误", device_serial=device_serial, command=command, error=str(e))
return ShellResponse(success=False, output="", error=f"未知错误: {str(e)}", exit_code=-1)
async def _monitor_devices(self) -> None:
"""设备监控内部方法"""
logger.info("设备监控循环开始", host=self._host, port=self._port)
try:
logger.debug("开始设备监控循环")
async for event in self._client.track_devices():
logger.info("设备事件",
present=event.present,
serial=event.serial,
status=event.status)
await self._handle_device_event(event)
except asyncio.CancelledError:
logger.info("设备监控被取消")
except Exception as e:
logger.error("设备监控异常", error=str(e), host=self._host, port=self._port)
# 监控异常时不再自动重启,需要手动重新启动
self._monitoring_started = False
logger.warning("设备监控已停止,需要手动重新启动")
async def _handle_device_event(self, event: DeviceEvent) -> None:
"""处理设备事件"""
if event.present:
logger.info("设备连接", serial=event.serial, status=event.status)
else:
logger.info("设备断开", serial=event.serial, status=event.status)
async def list_forward_ports(self) -> List[ForwardInfo]:
"""获取端口转发列表"""
try:
logger.debug("获取端口转发列表")
forwards = []
async for forward in self._client.forward_list():
forwards.append(ForwardInfo(
serial=forward.serial,
local=forward.local,
remote=forward.remote
))
logger.info("获取端口转发列表成功", forward_count=len(forwards))
return forwards
except Exception as e:
logger.error("获取端口转发列表失败", error=str(e))
raise
async def create_forward_port(self, request: ForwardRequest) -> None:
"""创建端口转发"""
try:
logger.debug("创建端口转发",
serial=request.serial,
local=request.local,
remote=request.remote,
norebind=request.norebind)
await self._client.create_forward_port(
request.serial,
request.local,
request.remote,
request.norebind
)
logger.info("端口转发创建成功",
serial=request.serial,
local=request.local,
remote=request.remote)
except Exception as e:
logger.error("创建端口转发失败",
serial=request.serial,
local=request.local,
remote=request.remote,
error=str(e))
raise
async def remove_forward_port(self, local: Optional[str] = None) -> None:
"""移除端口转发"""
try:
logger.debug("移除端口转发", local=local)
await self._client.remove_forward_port(local)
logger.info("端口转发移除成功", local=local or "all")
except Exception as e:
logger.error("移除端口转发失败", local=local, error=str(e))
raise
async def ping_device(self, device_serial: str) -> bool:
"""ping设备检查连接状态"""
try:
logger.debug("ping设备", device_serial=device_serial)
result = await self.execute_shell_command(device_serial, "echo 'ping'")
success = result.success
logger.info("ping设备完成", device_serial=device_serial, success=success)
return success
except Exception as e:
logger.error("ping设备失败", device_serial=device_serial, error=str(e))
return False
async def get_device_info(self, device_serial: str) -> Optional[dict]:
"""获取设备详细信息"""
try:
logger.debug("获取设备详细信息", device_serial=device_serial)
# 获取基本设备信息
devices = await self.list_devices()
device = next((d for d in devices if d.serial == device_serial), None)
if not device:
logger.warning("设备不存在", device_serial=device_serial)
return None
# 获取设备属性
properties = {}
property_commands = [
("model", "getprop ro.product.model"),
("manufacturer", "getprop ro.product.manufacturer"),
("android_version", "getprop ro.build.version.release"),
("sdk_version", "getprop ro.build.version.sdk"),
("build_id", "getprop ro.build.id"),
("cpu_abi", "getprop ro.product.cpu.abi")
]
for prop_name, command in property_commands:
try:
result = await self.execute_shell_command(device_serial, command)
if result.success and result.output.strip():
properties[prop_name] = result.output.strip()
except Exception as e:
logger.warning(f"获取设备属性失败: {prop_name}",
device_serial=device_serial, error=str(e))
device_info = {
"serial": device_serial,
"status": device.status.value,
"properties": properties
}
logger.info("获取设备详细信息成功", device_serial=device_serial)
return device_info
except Exception as e:
logger.error("获取设备详细信息失败", device_serial=device_serial, error=str(e))
return None
async def install_apk(self, device_serial: str, apk_path: str) -> ShellResponse:
"""安装APK文件"""
try:
logger.debug("安装APK", device_serial=device_serial, apk_path=apk_path)
command = f"pm install -r {apk_path}"
result = await self.execute_shell_command(device_serial, command)
logger.info("APK安装完成", device_serial=device_serial, apk_path=apk_path, success=result.success)
return result
except Exception as e:
logger.error("APK安装失败", device_serial=device_serial, apk_path=apk_path, error=str(e))
return ShellResponse(success=False, output="", error=str(e), exit_code=-1)
async def uninstall_app(self, device_serial: str, package_name: str) -> ShellResponse:
"""卸载应用"""
try:
logger.debug("卸载应用", device_serial=device_serial, package_name=package_name)
command = f"pm uninstall {package_name}"
result = await self.execute_shell_command(device_serial, command)
logger.info("应用卸载完成", device_serial=device_serial, package_name=package_name, success=result.success)
return result
except Exception as e:
logger.error("应用卸载失败", device_serial=device_serial, package_name=package_name, error=str(e))
return ShellResponse(success=False, output="", error=str(e), exit_code=-1)
async def get_installed_apps(self, device_serial: str) -> List[str]:
"""获取已安装应用列表"""
try:
logger.debug("获取已安装应用列表", device_serial=device_serial)
result = await self.execute_shell_command(device_serial, "pm list packages -3")
if result.success:
packages = [line.replace("package:", "").strip() for line in result.output.splitlines() if line.strip()]
logger.info("获取已安装应用列表成功", device_serial=device_serial, app_count=len(packages))
return packages
else:
logger.error("获取已安装应用列表失败", device_serial=device_serial, error=result.error)
return []
except Exception as e:
logger.error("获取已安装应用列表异常", device_serial=device_serial, error=str(e))
return []