autool/utils_android/download_app/adb_helper.py
2026-06-17 19:44:18 +08:00

173 lines
5.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# -*- coding: utf-8 -*-
"""
ADB Helper Module
封装 DroidBot 的 ADB 适配器,提供简化的接口供 download_app 模块使用
"""
import logging
from DroidBot.platforms.android.adapters.adb import ADB
from DroidBot.exceptions import ADBException
from utils_android.device_config import ensure_android_wireless_connected, get_android_device_serial
logger = logging.getLogger(__name__)
class SimpleDevice:
"""简化的设备对象,用于初始化 ADB
ADB 类需要一个 device 参数,但 download_app 模块不需要完整的设备对象。
这个类提供最小化的接口满足 ADB 初始化需求。
"""
def __init__(self, serial=None):
serial = serial or get_android_device_serial()
if serial:
ensure_android_wireless_connected(serial=serial)
# 如果没有指定 serial自动获取第一个可用设备
if serial is None:
from DroidBot.platforms.android.utils import get_available_devices
all_devices = get_available_devices()
if len(all_devices) == 0:
logger.error("ERROR: No device connected.")
raise ADBException("No device connected")
serial = all_devices[0]
logger.info(f"Auto-detected device: {serial}")
self.serial = serial
self.logger = logger
class ADBHelper:
"""ADB 辅助类,封装常用的 ADB 命令
提供统一的 ADB 接口,确保所有 ADB 命令失败时都能抛出 ADBException。
这样主进程 batch_run.py 可以捕获并处理 ADB 相关错误。
使用示例:
helper = ADBHelper()
output = helper.shell("pm list packages")
helper.install("/path/to/app.apk")
"""
def __init__(self, serial=None):
"""初始化 ADB Helper
Args:
serial: 设备序列号None 表示使用默认设备
"""
device = SimpleDevice(serial)
self.adb = ADB(device)
def shell(self, cmd):
"""执行 adb shell 命令
Args:
cmd: shell 命令字符串,例如 "pm list packages"
Returns:
命令输出字符串
Raises:
ADBException: ADB 命令执行失败时抛出
"""
return self.adb.shell(cmd)
def run_cmd(self, args):
"""执行 adb 命令
Args:
args: 命令参数列表或字符串,例如 ['install', '-r', 'app.apk']
Returns:
命令输出字符串
Raises:
ADBException: ADB 命令执行失败时抛出
"""
return self.adb.run_cmd(args)
def install(self, apk_path):
"""安装单个 APK
Args:
apk_path: APK 文件路径
Returns:
命令输出字符串
Raises:
ADBException: 安装失败时抛出
"""
import os, shutil, tempfile
need_copy = apk_path.startswith('\\\\')
if need_copy:
tmp_dir = tempfile.mkdtemp(prefix='apk_install_')
local_path = os.path.join(tmp_dir, os.path.basename(apk_path))
shutil.copy2(apk_path, local_path)
install_path = local_path
else:
tmp_dir = None
install_path = apk_path
try:
output = self.adb.run_cmd(['install', '-r', install_path])
if 'Success' not in output:
detail = getattr(self.adb, 'last_error', '') or ''
raise ADBException(f"Install failed: {detail}")
return output
finally:
if tmp_dir:
shutil.rmtree(tmp_dir, ignore_errors=True)
def install_multiple(self, apk_paths):
"""安装多个 APKsplit APKs
Args:
apk_paths: APK 文件路径列表
Returns:
命令输出字符串
Raises:
ADBException: 安装失败时抛出
"""
import os, shutil, tempfile
need_copy = any(p.startswith('\\\\') for p in apk_paths)
if need_copy:
tmp_dir = tempfile.mkdtemp(prefix='apk_install_')
local_paths = []
for p in apk_paths:
dst = os.path.join(tmp_dir, os.path.basename(p))
shutil.copy2(p, dst)
local_paths.append(dst)
apk_paths = local_paths
else:
tmp_dir = None
try:
args = ['install-multiple', '-r'] + apk_paths
output = self.adb.run_cmd(args)
if 'Success' not in output:
detail = getattr(self.adb, 'last_error', '') or ''
raise ADBException(f"Install-multiple failed: {detail}")
return output
finally:
if tmp_dir:
shutil.rmtree(tmp_dir, ignore_errors=True)
def uninstall(self, package_name):
"""卸载应用
Args:
package_name: 应用包名
Returns:
bool: True 表示卸载成功False 表示命令执行了但卸载失败
Raises:
ADBException: ADB 连接异常时抛出
"""
output = self.adb.run_cmd(['uninstall', package_name])
return 'Success' in output