# 权限处理 import subprocess import logging import time import os # 配置日志 logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s: %(message)s") def check_device_connection(device_id: str) -> bool: """检查设备连接状态""" try: check_cmd = ["adb", "-s", device_id, "shell", "getprop", "ro.product.model"] result = subprocess.run(check_cmd, capture_output=True, text=True, timeout=10) if result.returncode == 0: logging.info(f"设备 {device_id} 连接正常,型号: {result.stdout.strip()}") return True else: logging.error(f"设备 {device_id} 连接失败: {result.stderr.strip()}") return False except subprocess.TimeoutExpired: logging.error(f"设备 {device_id} 连接超时") return False except Exception as e: logging.error(f"检查设备 {device_id} 时发生错误: {str(e)}") return False def is_package_installed(device_id: str, package_name: str) -> bool: """检查包是否已安装""" try: check_cmd = ["adb", "-s", device_id, "shell", "pm", "list", "packages", package_name] result = subprocess.run(check_cmd, capture_output=True, text=True, timeout=10) return result.returncode == 0 and package_name in result.stdout except Exception as e: logging.error(f"检查包 {package_name} 时发生错误: {str(e)}") return False def grant_single_permission(device_id: str, package: str, permission: str) -> bool: """ 为单个包授予单个权限 :return: 是否成功授予 """ try: grant_cmd = [ "adb", "-s", device_id, "shell", "pm", "grant", package, permission ] result = subprocess.run(grant_cmd, capture_output=True, text=True, timeout=15) if result.returncode == 0: logging.info(f"设备 {device_id}:已成功授予 {package} 权限: {permission}") return True else: error_msg = result.stderr.strip() logging.warning(f"设备 {device_id}:授予 {package} 权限 {permission} 失败: {error_msg}") # 尝试使用root权限 if "security" in error_msg.lower() or "permission" in error_msg.lower(): logging.info(f"设备 {device_id}:尝试使用root权限授予 {package} 权限") # 重启adb为root模式 root_cmd = ["adb", "-s", device_id, "root"] subprocess.run(root_cmd, capture_output=True, text=True, timeout=10) time.sleep(2) # 等待root权限生效 # 再次尝试授予权限 result = subprocess.run(grant_cmd, capture_output=True, text=True, timeout=15) if result.returncode == 0: logging.info(f"设备 {device_id}:使用root权限成功授予 {package} 权限: {permission}") return True else: logging.error(f"设备 {device_id}:即使使用root权限也无法授予 {package} 权限 {permission}: {result.stderr.strip()}") return False else: return False except subprocess.CalledProcessError as e: logging.error(f"设备 {device_id}:ADB 命令执行失败,返回码 {e.returncode}") logging.error(f"标准输出:{e.stdout.strip()}") logging.error(f"错误输出:{e.stderr.strip()}") return False except Exception as e: logging.error(f"设备 {device_id}:处理 {package} 时发生未知错误:{str(e)}") return False # def grant_appium_permissions(device_id: str, require_all: bool = False) -> bool: # """ # 为 Appium UiAutomator2 服务授予权限 # :param device_id: 设备 ID # :param require_all: 是否要求所有权限都成功授予 # :return: 权限授予是否成功(根据require_all参数判断) # """ # # 首先检查设备连接 # if not check_device_connection(device_id): # return False # packages_to_grant = [ # "io.appium.settings", # "io.appium.uiautomator2.server", # "io.appium.uiautomator2.server.test" # ] # # 权限列表(按优先级排序) # permissions_to_grant = [ # "android.permission.SET_ANIMATION_SCALE", # "android.permission.CHANGE_CONFIGURATION", # "android.permission.WRITE_SETTINGS", # "android.permission.DISABLE_KEYGUARD", # ] # success_count = 0 # total_attempted = 0 # package_results = {} # # 检查并授予权限 # for package in packages_to_grant: # package_results[package] = {"installed": False, "permissions": {}} # if not is_package_installed(device_id, package): # logging.warning(f"设备 {device_id}:包 {package} 未安装,跳过权限授予") # package_results[package]["installed"] = False # continue # package_results[package]["installed"] = True # package_success = 0 # package_attempted = 0 # for permission in permissions_to_grant: # total_attempted += 1 # package_attempted += 1 # result = grant_single_permission(device_id, package, permission) # package_results[package]["permissions"][permission] = result # if result: # success_count += 1 # package_success += 1 # # 记录每个包的授权结果 # logging.info(f"设备 {device_id}:包 {package} 权限授予结果: {package_success}/{package_attempted}") # # 统计和报告 # logging.info(f"设备 {device_id}:权限授予完成") # logging.info(f"总计: 尝试 {total_attempted} 次,成功 {success_count} 次") # # 检查每个包的关键权限 # critical_permission = "android.permission.WRITE_SECURE_SETTINGS" # critical_failures = [] # for package, info in package_results.items(): # if info["installed"] and critical_permission in info["permissions"]: # if not info["permissions"][critical_permission]: # critical_failures.append(package) # if critical_failures: # logging.warning(f"设备 {device_id}:以下包的关键权限 {critical_permission} 授予失败: {', '.join(critical_failures)}") # logging.warning("这可能会影响某些自动化功能,但基础测试通常不受影响") # # 根据require_all参数返回结果 # if require_all: # # 要求所有权限都成功 # if critical_failures: # logging.error("关键权限授予失败,无法继续(require_all=True)") # return False # return success_count == total_attempted # else: # # 不要求所有权限,只要设备连接正常就返回True # logging.info(f"设备 {device_id}:权限授予过程完成,建议重启设备或Appium服务使更改生效") # return True def grant_appium_permissions(device_id: str, require_all: bool = False) -> bool: """ 修复版:为 Appium 授予权限(使用正确的方法) """ logging.info(f"设备 {device_id}:开始设置Appium权限") # 1. 使用系统设置命令(替代原来的pm grant尝试) logging.info("使用系统设置命令...") system_commands = [ ["adb", "-s", device_id, "shell", "settings", "put", "global", "window_animation_scale", "0"], ["adb", "-s", device_id, "shell", "settings", "put", "global", "transition_animation_scale", "0"], ["adb", "-s", device_id, "shell", "settings", "put", "global", "animator_duration_scale", "0"], ["adb", "-s", device_id, "shell", "settings", "put", "system", "screen_off_timeout", "86400000"], ] success_count = 0 for cmd in system_commands: try: result = subprocess.run(cmd, capture_output=True, text=True, timeout=10) if result.returncode == 0: success_count += 1 logging.info(f" 成功: {' '.join(cmd[3:])}") else: logging.warning(f" 失败: {' '.join(cmd[3:])}") except: logging.warning(f" 异常: {' '.join(cmd[3:])}") # 2. 授予可自动授予的权限 logging.info("授予基础权限...") grantable = [ "android.permission.INTERNET", "android.permission.ACCESS_NETWORK_STATE", "android.permission.ACCESS_WIFI_STATE", ] for perm in grantable: cmd = ["adb", "-s", device_id, "shell", "pm", "grant", "io.appium.settings", perm] result = subprocess.run(cmd, capture_output=True, text=True, timeout=10) if result.returncode == 0: success_count += 1 logging.info(f" 成功授予: {perm.split('.')[-1]}") else: logging.debug(f" 跳过: {perm.split('.')[-1]}") # 3. 返回结果 logging.info(f"设置完成,成功项数: {success_count}") if require_all: return success_count == (len(system_commands) + len(grantable)) else: return success_count > 0 # 只要有成功项就返回True def check_appium_compatibility(device_id: str) -> dict: """ 检查Appium兼容性 :return: 兼容性报告字典 """ try: # 获取Android版本 version_cmd = ["adb", "-s", device_id, "shell", "getprop", "ro.build.version.release"] result = subprocess.run(version_cmd, capture_output=True, text=True, timeout=10) android_version = result.stdout.strip() if result.returncode == 0 else "未知" report = { "device_id": device_id, "android_version": android_version, "compatibility": "unknown", "notes": [], "suggestions": [] } try: version_num = float(android_version.split('.')[0]) if version_num >= 11: report["compatibility"] = "limited" report["notes"].append("Android 11+ 对WRITE_SECURE_SETTINGS权限限制非常严格") report["suggestions"].append("使用--no-reset参数启动Appium") report["suggestions"].append("设置autoGrantPermissions=false") elif version_num >= 10: report["compatibility"] = "moderate" report["notes"].append("Android 10 限制了WRITE_SECURE_SETTINGS权限") report["suggestions"].append("可尝试使用root权限的设备") elif version_num >= 9: report["compatibility"] = "good" report["notes"].append("Android 9 兼容性较好") else: report["compatibility"] = "excellent" report["notes"].append("Android 8或以下版本完全兼容") except (ValueError, IndexError): report["notes"].append("无法解析Android版本") return report except Exception as e: logging.error(f"检查兼容性时出错: {str(e)}") return {"device_id": device_id, "error": str(e)} # 使用示例 if __name__ == "__main__": # 获取设备ID(示例) devices_cmd = ["adb", "devices"] result = subprocess.run(devices_cmd, capture_output=True, text=True) if result.returncode == 0: lines = result.stdout.strip().split('\n')[1:] # 跳过第一行标题 for line in lines: if line.strip() and "device" in line: device_id = line.split('\t')[0] logging.info(f"找到设备: {device_id}") # 1. 检查兼容性 report = check_appium_compatibility(device_id) logging.info(f"兼容性报告: Android {report.get('android_version', '未知')} - {report.get('compatibility', '未知')}") # 2. 授予权限(不要求全部成功) success = grant_appium_permissions(device_id, require_all=False) if success: logging.info(f"设备 {device_id} 设置完成,可以开始测试") else: logging.warning(f"设备 {device_id} 权限授予有失败,但可能仍可进行基础测试") # 3. 提供建议 if "suggestions" in report: logging.info("建议:") for suggestion in report["suggestions"]: logging.info(f" - {suggestion}") else: logging.error("无法获取设备列表,请确保ADB已正确安装且设备已连接")