commit 187f4d90ea43addbdb65c6b3498c0f368d0440f0 Author: sakurai Date: Mon Dec 22 23:32:37 2025 +0800 feat: 添加自动手机双清脚本,初始化存储库 diff --git a/README.md b/README.md new file mode 100644 index 0000000..fdc75d2 --- /dev/null +++ b/README.md @@ -0,0 +1,155 @@ +# 自动手机双清脚本 + +一个模块化、易于维护和扩展的Python脚本,用于自动执行Android手机的双清操作。 + +## 功能特性 + +- ✅ **模块化设计**:遵循SOLID原则,代码结构清晰,便于维护和扩展 +- ✅ **设备检测**:自动检测已连接的Android设备 +- ✅ **Recovery模式**:自动重启设备到Recovery模式 +- ✅ **双清操作**:自动执行清除数据/恢复出厂设置和清除缓存分区 +- ✅ **日志记录**:详细的日志记录,便于问题排查 +- ✅ **可配置**:通过配置文件轻松调整参数 +- ✅ **多设备支持**:支持同时连接多个设备时选择操作目标 + +## 环境要求 + +- Windows 10/11 +- Python 3.10+ +- Android SDK Platform Tools(ADB工具) +- 已开启USB调试的Android设备 + +## 安装步骤 + +### 1. 安装Python + +从[Python官网](https://www.python.org/downloads/windows/)下载并安装Python 3.10或更高版本。 + +### 2. 安装ADB工具 + +1. 下载[Android SDK Platform Tools](https://developer.android.com/studio/releases/platform-tools) +2. 解压到任意目录(例如:`C:\platform-tools`) +3. 将该目录添加到系统环境变量PATH中 + +### 3. 配置设备 + +1. 在Android设备上开启开发者选项 +2. 开启USB调试 +3. 允许USB调试授权 + +## 使用方法 + +### 基本使用 + +```bash +python main.py +``` + +### 命令行参数(可选) + +```bash +# 指定ADB路径 +ADB_PATH="C:\platform-tools\adb.exe" python main.py + +# 调整日志级别 +LOG_LEVEL="DEBUG" python main.py +``` + +### 操作流程 + +1. 脚本启动后会自动检测已连接的设备 +2. 选择要操作的设备 +3. 确认是否开始双清操作 +4. 脚本自动重启设备到Recovery模式 +5. 执行清除数据和清除缓存操作 +6. 自动重启设备 + +## 项目结构 + +``` +auto2Clean/ +├── main.py # 主脚本文件 +├── config.py # 配置文件 +├── logger_setup.py # 日志配置模块 +├── auto_clean.log # 日志文件(自动生成) +└── README.md # 项目说明文档 +``` + +## 配置说明 + +### 主要配置项(config.py) + +| 配置项 | 类型 | 默认值 | 说明 | +|--------|------|--------|------| +| ADB_PATH | str | "adb" | ADB工具路径 | +| ADB_TIMEOUT | int | 30 | ADB命令超时时间(秒) | +| RECOVERY_WAIT_TIMEOUT | int | 60 | 等待Recovery模式超时时间(秒) | +| LOG_LEVEL | str | "INFO" | 日志级别 | +| LOG_FILE | str | "auto_clean.log" | 日志文件路径 | +| AUTO_REBOOT_AFTER_CLEAN | bool | True | 双清完成后自动重启 | +| CONFIRM_BEFORE_OPERATION | bool | True | 操作前确认 | + +## 维护指南 + +### 添加新功能 + +1. **遵循模块化原则**:每个功能模块独立 +2. **使用配置文件**:将可配置参数添加到config.py +3. **添加日志**:在关键步骤添加日志记录 +4. **错误处理**:完善异常处理机制 + +### 扩展操作 + +可以通过继承或扩展现有类来添加新功能: + +```python +# 示例:添加新的Recovery操作 +class ExtendedRecoveryOperations(RecoveryOperations): + def wipe_dalvik_cache(self, serial: Optional[str] = None) -> None: + """清除Dalvik缓存""" + logger.info("开始清除Dalvik缓存...") + command = ["shell", "wipe dalvik-cache"] + if serial: + command = ["-s", serial] + command + + self.adb_tool.run_command(command) + time.sleep(15) +``` + +### 常见问题 + +**Q: 检测不到设备** +- 确保USB调试已开启 +- 确保已安装设备驱动 +- 尝试重新插拔USB线 +- 检查ADB是否正常工作(`adb devices`) + +**Q: 无法进入Recovery模式** +- 确保设备已解锁Bootloader +- 检查Recovery模式是否可用 +- 尝试手动进入Recovery模式验证 + +**Q: 双清操作失败** +- 检查Recovery模式是否支持ADB命令 +- 确保设备已正确进入Recovery模式 +- 查看日志文件获取详细错误信息 + +## 版本历史 + +### v1.0.0 +- 初始版本 +- 支持基本双清操作 +- 模块化设计 +- 日志记录功能 + +## 许可证 + +MIT License + +## 贡献 + +欢迎提交Issue和Pull Request来改进这个项目。 + +## 联系方式 + +如有问题或建议,请通过Issue联系。 \ No newline at end of file diff --git a/__pycache__/adb_tool.cpython-310.pyc b/__pycache__/adb_tool.cpython-310.pyc new file mode 100644 index 0000000..ed8df56 Binary files /dev/null and b/__pycache__/adb_tool.cpython-310.pyc differ diff --git a/__pycache__/config.cpython-310.pyc b/__pycache__/config.cpython-310.pyc new file mode 100644 index 0000000..c17751b Binary files /dev/null and b/__pycache__/config.cpython-310.pyc differ diff --git a/__pycache__/config.cpython-312.pyc b/__pycache__/config.cpython-312.pyc new file mode 100644 index 0000000..5657c31 Binary files /dev/null and b/__pycache__/config.cpython-312.pyc differ diff --git a/__pycache__/logger_setup.cpython-310.pyc b/__pycache__/logger_setup.cpython-310.pyc new file mode 100644 index 0000000..15e27aa Binary files /dev/null and b/__pycache__/logger_setup.cpython-310.pyc differ diff --git a/__pycache__/logger_setup.cpython-312.pyc b/__pycache__/logger_setup.cpython-312.pyc new file mode 100644 index 0000000..fb4c81f Binary files /dev/null and b/__pycache__/logger_setup.cpython-312.pyc differ diff --git a/adb_tool.py b/adb_tool.py new file mode 100644 index 0000000..bed35c1 --- /dev/null +++ b/adb_tool.py @@ -0,0 +1,146 @@ +#!/usr/bin/env python3 +""" +ADB工具封装模块 + +该模块提供了ADB工具的封装,用于与Android设备进行通信。 +""" + +import os +import sys +import time +import subprocess +import logging +from typing import List, Optional +from dataclasses import dataclass + +from config import Config + +# 初始化日志 +logger = logging.getLogger("auto_clean") + + +@dataclass +class DeviceInfo: + """设备信息数据类""" + serial: str + model: str + product: str + device: str + + +class ADBTool: + """ADB工具封装类""" + + def __init__(self, adb_path: str = Config.ADB_PATH): + self.adb_path = adb_path + logger.debug(f"初始化ADBTool,ADB路径: {self.adb_path}") + self._check_adb_exists() + + def _check_adb_exists(self) -> None: + """检查ADB工具是否存在""" + logger.debug("开始检查ADB工具是否存在") + try: + result = subprocess.run([self.adb_path, "version"], capture_output=True, text=True, check=True) + logger.debug(f"ADB版本信息: {result.stdout.strip()}") + logger.info("ADB工具检测成功") + except (subprocess.CalledProcessError, FileNotFoundError): + logger.error("ADB工具未找到,请确保ADB已安装并添加到系统PATH") + sys.exit(1) + + def run_command(self, command: List[str], timeout: int = Config.ADB_TIMEOUT) -> str: + """运行ADB命令""" + full_command = [self.adb_path] + command + logger.debug(f"执行命令: {' '.join(full_command)}, 超时时间: {timeout}秒") + + try: + result = subprocess.run( + full_command, + capture_output=True, + text=True, + timeout=timeout, + check=True + ) + logger.debug(f"命令执行成功,输出: {result.stdout.strip()}") + return result.stdout.strip() + except subprocess.CalledProcessError as e: + logger.error(f"命令执行失败: {e.stderr}") + logger.debug(f"失败命令: {' '.join(full_command)}") + raise + except subprocess.TimeoutExpired: + logger.error(f"命令执行超时({timeout}秒)") + logger.debug(f"超时命令: {' '.join(full_command)}") + raise + + def get_connected_devices(self) -> List[DeviceInfo]: + """获取已连接的设备列表""" + logger.debug("开始获取已连接的设备列表") + try: + output = self.run_command(["devices", "-l"]) + logger.debug(f"设备列表原始输出: {output}") + + devices = [] + + for line in output.split('\n')[1:]: + if line.strip(): + parts = line.split() + serial = parts[0] + device_info = {} + + for part in parts[1:]: + if ':' in part: + key, value = part.split(':', 1) + device_info[key] = value + + device = DeviceInfo( + serial=serial, + model=device_info.get('model', 'unknown'), + product=device_info.get('product', 'unknown'), + device=device_info.get('device', 'unknown') + ) + devices.append(device) + logger.debug(f"发现设备: {serial} - {device.model}") + + logger.info(f"共检测到 {len(devices)} 台设备") + return devices + except Exception as e: + logger.error(f"获取设备列表失败: {e}") + logger.debug(f"获取设备列表失败详情: {str(e)}") + return [] + + def reboot_to_recovery(self, serial: Optional[str] = None) -> None: + """重启设备到Recovery模式""" + logger.debug(f"重启设备到Recovery模式,设备序列号: {serial}") + command = ["reboot", "recovery"] + if serial: + command = ["-s", serial] + command + + self.run_command(command) + logger.info("设备正在重启到Recovery模式...") + + def wait_for_recovery_mode(self, serial: Optional[str] = None, timeout: int = Config.RECOVERY_WAIT_TIMEOUT) -> bool: + """等待设备进入Recovery模式""" + logger.debug(f"等待设备进入Recovery模式,超时时间: {timeout}秒,设备序列号: {serial}") + start_time = time.time() + + while time.time() - start_time < timeout: + try: + command = ["shell", "getprop", "ro.bootmode"] + if serial: + command = ["-s", serial] + command + + output = self.run_command(command, timeout=5) + logger.debug(f"Bootmode检查输出: {output}") + + if "recovery" in output.lower(): + logger.info("设备已进入Recovery模式") + return True + except Exception as e: + logger.debug(f"检查Recovery模式时出现异常: {str(e)}") + pass + + time.sleep(2) + elapsed = int(time.time() - start_time) + logger.debug(f"等待Recovery模式中... 已等待 {elapsed}/{timeout}秒") + + logger.error(f"等待Recovery模式超时({timeout}秒)") + return False \ No newline at end of file diff --git a/auto_clean.log b/auto_clean.log new file mode 100644 index 0000000..f470530 --- /dev/null +++ b/auto_clean.log @@ -0,0 +1,14 @@ +2025-12-22 22:45:11,312 - INFO - ADB߼ɹ +2025-12-22 22:45:11,312 - INFO - ִ: adb devices -l +2025-12-22 23:00:08 - auto_clean - INFO - ADB工具检测成功 +2025-12-22 23:00:08 - auto_clean - INFO - 共检测到 0 台设备 +2025-12-22 23:00:08 - auto_clean - WARNING - 未检测到任何已连接的设备 +2025-12-22 23:02:04 - auto_clean - INFO - ADB工具检测成功 +2025-12-22 23:02:04 - auto_clean - INFO - 共检测到 0 台设备 +2025-12-22 23:02:04 - auto_clean - WARNING - 未检测到任何已连接的设备 +2025-12-22 23:02:37 - auto_clean - INFO - ADB工具检测成功 +2025-12-22 23:02:37 - auto_clean - INFO - 共检测到 0 台设备 +2025-12-22 23:02:37 - auto_clean - WARNING - 未检测到任何已连接的设备 +2025-12-22 23:06:20 - auto_clean - INFO - ADB工具检测成功 +2025-12-22 23:06:20 - auto_clean - INFO - 共检测到 0 台设备 +2025-12-22 23:06:20 - auto_clean - WARNING - 未检测到任何已连接的设备 diff --git a/config.py b/config.py new file mode 100644 index 0000000..f46d9e0 --- /dev/null +++ b/config.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 +""" +配置文件 + +该文件包含脚本的所有可配置参数,便于后期维护和调整。 +""" + +import os +from typing import Optional + + +class Config: + """配置类""" + + # ADB相关配置 + ADB_PATH: str = "adb" # ADB工具路径,默认使用系统PATH中的adb + ADB_TIMEOUT: int = 30 # ADB命令超时时间(秒) + + # 操作超时配置 + RECOVERY_WAIT_TIMEOUT: int = 60 # 等待Recovery模式超时时间(秒) + WIPE_DATA_WAIT_TIME: int = 30 # 等待清除数据完成时间(秒) + WIPE_CACHE_WAIT_TIME: int = 15 # 等待清除缓存完成时间(秒) + + # 日志配置 + LOG_LEVEL: str = "INFO" # 日志级别: DEBUG, INFO, WARNING, ERROR, CRITICAL + LOG_FILE: str = "auto_clean.log" # 日志文件路径 + LOG_MAX_SIZE: int = 10 * 1024 * 1024 # 单个日志文件最大大小(10MB) + LOG_BACKUP_COUNT: int = 5 # 保留的日志备份文件数 + + # 设备配置 + DEFAULT_DEVICE_SERIAL: Optional[str] = None # 默认设备序列号 + + # 操作配置 + AUTO_REBOOT_AFTER_CLEAN: bool = True # 双清完成后自动重启 + CONFIRM_BEFORE_OPERATION: bool = True # 操作前确认 + + # 重试配置 + MAX_RETRY_COUNT: int = 3 # 最大重试次数 + RETRY_DELAY: int = 2 # 重试间隔时间(秒) + + +# 环境变量覆盖配置 +if os.getenv("ADB_PATH"): + Config.ADB_PATH = os.getenv("ADB_PATH") + +if os.getenv("LOG_LEVEL"): + Config.LOG_LEVEL = os.getenv("LOG_LEVEL") + +if os.getenv("LOG_FILE"): + Config.LOG_FILE = os.getenv("LOG_FILE") \ No newline at end of file diff --git a/logger_setup.py b/logger_setup.py new file mode 100644 index 0000000..e2afc1c --- /dev/null +++ b/logger_setup.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +""" +日志配置模块 + +该模块负责配置和初始化日志系统,提供统一的日志接口。 +""" + +import logging +import os +from logging.handlers import RotatingFileHandler +from config import Config + + +def setup_logger() -> logging.Logger: + """配置并返回日志记录器""" + logger = logging.getLogger("auto_clean") + logger.setLevel(getattr(logging, Config.LOG_LEVEL)) + + # 避免重复添加处理器 + if logger.handlers: + return logger + + # 创建格式化器 + formatter = logging.Formatter( + '%(asctime)s - %(name)s - %(levelname)s - %(message)s', + datefmt='%Y-%m-%d %H:%M:%S' + ) + + # 控制台处理器 + console_handler = logging.StreamHandler() + console_handler.setFormatter(formatter) + logger.addHandler(console_handler) + + # 文件处理器(支持日志轮转) + file_handler = RotatingFileHandler( + Config.LOG_FILE, + maxBytes=Config.LOG_MAX_SIZE, + backupCount=Config.LOG_BACKUP_COUNT, + encoding='utf-8' + ) + file_handler.setFormatter(formatter) + logger.addHandler(file_handler) + + logger.debug("日志系统初始化完成") + return logger \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 0000000..2dcf435 --- /dev/null +++ b/main.py @@ -0,0 +1,211 @@ +#!/usr/bin/env python3 +""" +自动手机双清脚本 + +该脚本提供了一个模块化的框架,用于自动执行Android手机的双清操作(清除数据/恢复出厂设置和清除缓存分区)。 +设计遵循SOLID原则,便于后期维护和功能扩展。 +""" + +import os +import sys +import time +import subprocess +import logging +from typing import List, Optional + +# 导入配置、日志和ADB工具 +from config import Config +from logger_setup import setup_logger +from adb_tool import ADBTool, DeviceInfo + +# 初始化日志 +logger = setup_logger() +logger.debug("主脚本初始化完成") + + + + + +class RecoveryOperations: + """Recovery模式操作类""" + + def __init__(self, adb_tool: ADBTool): + self.adb_tool = adb_tool + logger.debug("RecoveryOperations初始化完成") + + def wipe_data(self, serial: Optional[str] = None) -> None: + """清除数据/恢复出厂设置""" + logger.debug(f"开始清除数据/恢复出厂设置,设备序列号: {serial}") + logger.info("开始清除数据/恢复出厂设置...") + command = ["shell", "wipe data"] + if serial: + command = ["-s", serial] + command + + self.adb_tool.run_command(command) + logger.debug(f"等待清除数据完成,等待时间: {Config.WIPE_DATA_WAIT_TIME}秒") + time.sleep(Config.WIPE_DATA_WAIT_TIME) # 等待操作完成 + logger.debug("清除数据操作完成") + + def wipe_cache(self, serial: Optional[str] = None) -> None: + """清除缓存分区""" + logger.debug(f"开始清除缓存分区,设备序列号: {serial}") + logger.info("开始清除缓存分区...") + command = ["shell", "wipe cache"] + if serial: + command = ["-s", serial] + command + + self.adb_tool.run_command(command) + logger.debug(f"等待清除缓存完成,等待时间: {Config.WIPE_CACHE_WAIT_TIME}秒") + time.sleep(Config.WIPE_CACHE_WAIT_TIME) # 等待操作完成 + logger.debug("清除缓存操作完成") + + def reboot_system(self, serial: Optional[str] = None) -> None: + """重启系统""" + logger.debug(f"开始重启系统,设备序列号: {serial}") + logger.info("开始重启系统...") + command = ["shell", "reboot"] + if serial: + command = ["-s", serial] + command + + self.adb_tool.run_command(command) + logger.debug("重启系统命令已发送") + + +class AutoCleanController: + """自动双清控制器类""" + + def __init__(self): + logger.debug("初始化AutoCleanController") + self.adb_tool = ADBTool() + self.recovery_ops = RecoveryOperations(self.adb_tool) + logger.debug("AutoCleanController初始化完成") + + def run_auto_clean(self, serial: Optional[str] = None) -> None: + """执行自动双清流程""" + logger.debug("开始执行自动双清流程") + try: + # 检查设备连接 + devices = self.adb_tool.get_connected_devices() + if not devices: + logger.error("未检测到已连接的设备") + return + + logger.info(f"检测到 {len(devices)} 台设备") + for device in devices: + logger.info(f"设备: {device.serial} - {device.model}") + logger.debug(f"设备详细信息: {device}") + + # 选择设备 + target_serial = serial if serial else devices[0].serial + logger.info(f"选择设备: {target_serial}") + logger.debug(f"目标设备序列号: {target_serial}") + + # 重启到Recovery模式 + logger.debug("步骤1: 重启设备到Recovery模式") + self.adb_tool.reboot_to_recovery(target_serial) + + # 等待Recovery模式 + logger.debug("步骤2: 等待设备进入Recovery模式") + if not self.adb_tool.wait_for_recovery_mode(target_serial): + logger.error("设备未进入Recovery模式,操作终止") + return + + # 执行双清 + logger.debug("步骤3: 执行清除数据操作") + self.recovery_ops.wipe_data(target_serial) + + logger.debug("步骤4: 执行清除缓存操作") + self.recovery_ops.wipe_cache(target_serial) + + # 重启系统 + if Config.AUTO_REBOOT_AFTER_CLEAN: + logger.debug("步骤5: 重启系统") + self.recovery_ops.reboot_system(target_serial) + + logger.info("双清操作完成,设备正在重启...") + logger.debug("自动双清流程执行完毕") + + except Exception as e: + logger.error(f"自动双清过程中发生错误: {e}") + logger.debug(f"错误详情: {str(e)}") + raise + + +def main(): + """主函数""" + logger.debug("主函数开始执行") + try: + controller = AutoCleanController() + logger.debug("AutoCleanController创建成功") + + # 显示欢迎信息 + print("=" * 60) + print("自动手机双清脚本") + print("=" * 60) + + # 检查设备 + devices = controller.adb_tool.get_connected_devices() + logger.debug(f"设备列表: {devices}") + + if not devices: + print("未检测到已连接的Android设备") + print("请确保设备已开启USB调试并连接到电脑") + logger.warning("未检测到任何已连接的设备") + return + + # 选择设备 + if len(devices) == 1: + print(f"检测到设备: {devices[0].serial} - {devices[0].model}") + logger.debug(f"单设备模式,设备: {devices[0].serial}") + + if Config.CONFIRM_BEFORE_OPERATION: + choice = input("是否开始双清操作? (y/N): ") + if choice.lower() == 'y': + controller.run_auto_clean(devices[0].serial) + else: + print("操作已取消") + logger.info("用户取消操作") + else: + print("自动开始双清操作...") + controller.run_auto_clean(devices[0].serial) + else: + print("检测到多个设备:") + for i, device in enumerate(devices): + print(f"{i+1}. {device.serial} - {device.model}") + + logger.debug(f"多设备模式,共{len(devices)}台设备") + + try: + selection = int(input("请选择要操作的设备编号: ")) - 1 + if 0 <= selection < len(devices): + print(f"选择设备: {devices[selection].serial} - {devices[selection].model}") + + if Config.CONFIRM_BEFORE_OPERATION: + choice = input("是否开始双清操作? (y/N): ") + if choice.lower() == 'y': + controller.run_auto_clean(devices[selection].serial) + else: + print("操作已取消") + logger.info("用户取消操作") + else: + print("自动开始双清操作...") + controller.run_auto_clean(devices[selection].serial) + else: + print("无效的选择") + logger.warning("用户输入无效的设备编号") + except ValueError: + print("请输入有效的数字") + logger.error("用户输入非数字字符") + + except KeyboardInterrupt: + print("\n操作已取消") + logger.info("用户通过Ctrl+C取消操作") + except Exception as e: + logger.error(f"程序执行失败: {e}") + print(f"错误: {e}") + finally: + logger.debug("主函数执行完毕") + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..2eaabce --- /dev/null +++ b/requirements.txt @@ -0,0 +1,7 @@ +# 项目依赖 +# 该项目主要使用Python标准库,无需额外安装依赖 +# 如果需要添加第三方库,可以在此列出 + +# 示例: +# requests>=2.26.0 +# pyserial>=3.5 \ No newline at end of file