173 lines
5.2 KiB
Python
173 lines
5.2 KiB
Python
# -*- 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):
|
||
"""安装多个 APK(split 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
|