feat: publish workphone SDK deployment and API docs

This commit is contained in:
Manus AI
2026-07-14 18:10:52 +08:00
commit 021d633cc1
534 changed files with 122391 additions and 0 deletions

View File

@@ -0,0 +1,428 @@
"""
连接守护技能 - 确保设备与服务器始终保持连接
职责:
1. 自动处理系统弹窗USB调试授权、权限请求、系统更新提示等
2. 监控 ADB / WiFi / 网络连通性
3. 检测并恢复 uiautomator2 服务
4. 检测屏幕状态,必要时亮屏
5. 与 Hawk 网络层协同做断网恢复
这是一个「常驻」技能Agent 启动后以后台循环方式持续运行。
"""
import logging
import time
import re
from typing import Dict, Any, List, Optional
import sys
import os
_agent_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
if _agent_dir not in sys.path:
sys.path.insert(0, _agent_dir)
try:
from skills.base import BaseSkill
except ImportError:
from ..base import BaseSkill
logger = logging.getLogger(__name__)
# ═══════════════════════════════════════════════════════════════
# 弹窗规则库 — 覆盖主流安卓品牌:小米/华为/OPPO/vivo/三星/一加/荣耀/realme
#
# 每条规则: detect = 界面关键词(正则), click = 要点击的按钮文案
# 规则按优先级排列,匹配到即执行,不会重复点击
# ═══════════════════════════════════════════════════════════════
_POPUP_DISMISS_RULES: List[Dict[str, Any]] = [
# ─── 1. USB 调试授权(所有品牌) ───
{"detect": [
"USB调试", "USB debugging", "允许USB调试", "Allow USB debugging",
"USB 偵錯", # 繁体
"USB调试安全设置", # MIUI 特有
"RSA.*密钥指纹", "RSA.*key fingerprint", # 弹窗正文
"允许通过USB调试", # 华为
"이 컴퓨터에서 USB 디버깅", # 三星韩语
],
"click": [
"确定", "允许", "OK", "Allow", "始终允许", "Always allow",
"확인", # 三星韩语
"允許", # 繁体
]},
# ─── 1b. USB 连接模式(仅充电 → 文件传输,防 MIUI 间歇掉 ADB ───
{"detect": [
"USB 用于", "USB用途", "USB 用途", "连接方式",
"仅充电", "充电模式", "Charging only", "Charge only",
"USB 偏好设置", "Use USB for",
"传输文件", "文件传输", "Transfer files",
],
"click": [
"传输文件", "文件传输", "Transfer files", "File transfer",
"传输照片", "Photo transfer", "MTP",
]},
# ─── 2. MIUI 安全弹窗(小米/红米特有) ───
{"detect": [
"安全警告", "此操作具有风险",
"模拟点击", "模拟输入", # MIUI「检测到模拟操作」
"检测到.*模拟", "监控.*辅助",
"开发者选项.*安全", "安全设置已关闭",
],
"click": ["确定", "继续", "我已知晓", "允许", "仍然允许"]},
# ─── 3. 华为/荣耀 特有弹窗 ───
{"detect": [
"允许HiSuite通过", "HiSuite",
"仅充电", "传输文件", "传输照片", # USB 模式选择
"华为手机助手",
"应用.*未通过安全检测", # 华为安全中心
"可能会损坏您的设备",
],
"click": ["允许", "继续安装", "我已知晓", "确定", "仍然安装"]},
# ─── 4. OPPO/realme/一加 特有弹窗 ───
{"detect": [
"ColorOS", "USB 传输",
"允许.*调试", "您要允许.*USB调试",
"开发者模式.*已开启",
"USB用途", "连接方式",
"充电模式.*传输", # OPPO USB 模式选择
],
"click": ["确定", "允许", "OK", "传输文件"]},
# ─── 5. vivo/iQOO 特有弹窗 ───
{"detect": [
"Funtouch", "OriginOS",
"是否允许USB调试",
"开启USB调试.*安全风险",
"vivo.*安全",
],
"click": ["确定", "允许", "OK", "已了解风险"]},
# ─── 6. 三星 特有弹窗 ───
{"detect": [
"One UI", "Samsung",
"您是否要允许USB调试", "Allow USB debugging",
"Knox.*安全",
"设备保护",
],
"click": ["确定", "允许", "OK", "Allow"]},
# ─── 7. ADB 通用权限 ───
{"detect": ["允许通过", "ADB", "adb"],
"click": ["确定", "允许", "OK"]},
# ─── 8. 安装应用(所有品牌) ───
{"detect": [
"要安装", "允许安装", "安装应用",
"安装未知应用", "Install unknown apps",
"来源不明", "Unknown sources",
"是否安装该应用", "Install this app",
"外部来源", "第三方来源",
"继续安装.*可能有风险", # 各品牌安全提示
"仍要安装",
],
"click": [
"继续安装", "允许", "安装", "Install",
"仍然安装", "仍要安装", "我已了解",
]},
# ─── 9. 权限请求弹窗(通用) ───
{"detect": [
"允许.*访问", "允许.*使用", "Allow.*access",
"请求.*权限", "needs.*permission",
"允许.*读取", "允许.*写入",
"允许.*拍照", "允许.*录音",
"允许.*定位", "Allow.*location",
"允许.*通知", "Allow.*notification",
"允许.*悬浮窗", "允许.*显示在其他应用",
"Display over other apps",
"允许.*存储", "Allow.*storage",
],
"click": [
"允许", "始终允许", "Allow", "While using the app",
"仅在使用时允许", "仅使用期间",
"始终", "Always",
]},
# ─── 10. 系统更新/升级 ───
{"detect": [
"系统更新", "System update", "升级",
"发现新版本", "New version",
"现在更新", "Update now",
"MIUI.*更新", "EMUI.*更新", "ColorOS.*更新",
"OTA.*更新",
],
"click": ["稍后", "以后", "取消", "Later", "Cancel", "Not now", "稍后再说"]},
# ─── 11. 电池优化(各品牌措辞不同) ───
{"detect": [
"电池优化", "Battery optimization", "忽略电池优化",
"省电策略", "后台耗电", "电量管理",
"自启动管理", "自启动",
"是否允许.*后台运行",
"关闭.*省电",
"Ignore battery optimizations",
"不再受限", # 华为
],
"click": ["允许", "", "Yes", "Allow", "不受限制", "确定"]},
# ─── 12. 后台运行保活 ───
{"detect": [
"后台运行", "后台活动", "Background",
"锁定.*后台", "保持运行",
"允许.*自启动", "允许.*后台",
"关闭后台清理",
"电池管理.*允许",
],
"click": ["允许", "Allow", "确定", "开启"]},
# ─── 13. 通知权限 ───
{"detect": [
"通知权限", "发送通知", "Post notifications",
"允许.*推送", "开启通知",
"Notification access",
],
"click": ["允许", "Allow", "确定"]},
# ─── 14. 悬浮窗/无障碍 ───
{"detect": [
"悬浮窗", "Display over", "draw over",
"无障碍", "Accessibility",
"辅助功能",
],
"click": ["允许", "Allow", "确定", "开启"]},
# ─── 15. Termux / 终端相关 ───
{"detect": ["Termux", "终端", "com.termux"],
"click": ["允许", "Allow", "确定"]},
# ─── 16. 应用崩溃/ANR ───
{"detect": [
"ANR", "没有响应", "not responding", "停止运行",
"已停止", "has stopped", "keeps stopping",
"无响应.*关闭", "close app",
],
"click": ["等待", "Wait", "再次打开", "Reopen"]},
# ─── 17. 隐私合规弹窗国内APP常见 ───
{"detect": [
"隐私政策", "用户协议", "Privacy policy",
"个人信息保护", "我已阅读",
"同意并继续", "Agree.*continue",
],
"click": ["同意", "同意并继续", "Agree", "我已阅读并同意", "确定"]},
# ─── 18. 广告/推广弹窗 ───
{"detect": [
"领取.*红包", "签到.*奖励",
"今日推荐", "猜你喜欢",
"开通.*会员", "免费试用",
"跳过", "Skip",
],
"click": ["跳过", "关闭", "Skip", "Close", "×", ""]},
# ─── 19. SIM卡/运营商弹窗 ───
{"detect": [
"SIM.*未插入", "SIM.*not inserted",
"无SIM卡", "No SIM",
"紧急呼叫", "Emergency",
],
"click": ["确定", "OK", "关闭"]},
]
class ConnectionGuardSkill(BaseSkill):
"""
连接守护技能 — 常驻后台,确保设备与服务器始终连接
核心能力:
1. dismiss_popups() — 自动关闭系统弹窗USB授权、权限请求等
2. ensure_screen_on() — 确保屏幕亮起
3. check_u2_alive() — 检测 uiautomator2 是否存活
4. check_network() — 检测网络连通性(委托 Hawk
5. full_guard_cycle() — 执行一轮完整守护(上述全部)
"""
PACKAGE = ""
NAME = "连接守护"
def _shell(self, cmd: str) -> str:
"""安全执行 shell 并返回输出"""
try:
r = self.d.shell(cmd)
return (getattr(r, "output", None) or str(r) or "").strip()
except Exception:
return ""
# ========== 1. 弹窗自动处理 ==========
def dismiss_popups(self) -> Dict[str, Any]:
"""
扫描当前界面,自动点击 USB 授权/权限请求/系统弹窗的确认按钮。
返回本轮处理了哪些弹窗。
"""
dismissed = []
if not self.d:
return {"dismissed": dismissed, "count": 0}
try:
xml = self.d.dump_hierarchy()
except Exception as e:
logger.debug(f"dump_hierarchy 失败: {e}")
return {"dismissed": dismissed, "count": 0, "error": str(e)}
for rule in _POPUP_DISMISS_RULES:
detected = False
for keyword in rule["detect"]:
if re.search(keyword, xml, re.IGNORECASE):
detected = True
break
if not detected:
continue
for btn_text in rule["click"]:
try:
if self.d(text=btn_text).exists(timeout=0.5):
self.d(text=btn_text).click()
dismissed.append({"popup": rule["detect"][0], "clicked": btn_text})
logger.info(f"🛡️ 自动处理弹窗: 检测到「{rule['detect'][0]}」→ 点击「{btn_text}")
time.sleep(0.5)
break
except Exception:
continue
# textContains 兜底
try:
if self.d(textContains=btn_text).exists(timeout=0.3):
self.d(textContains=btn_text).click()
dismissed.append({"popup": rule["detect"][0], "clicked": btn_text})
logger.info(f"🛡️ 自动处理弹窗: 检测到「{rule['detect'][0]}」→ 点击「{btn_text}」(contains)")
time.sleep(0.5)
break
except Exception:
continue
return {"dismissed": dismissed, "count": len(dismissed)}
# ========== 2. 屏幕状态 ==========
def ensure_screen_on(self) -> Dict[str, Any]:
"""确保屏幕亮起(某些 ADB 操作需要屏幕点亮)"""
if not self.d:
return {"screen_on": False, "action": "no_device"}
try:
info = self.d.info
if info.get("screenOn"):
return {"screen_on": True, "action": "already_on"}
self.d.press("power")
time.sleep(0.5)
# 如果有锁屏,向上滑动解锁
self.d.swipe_ext("up", scale=0.8)
time.sleep(0.3)
return {"screen_on": True, "action": "woke_up"}
except Exception as e:
logger.debug(f"ensure_screen_on 异常: {e}")
return {"screen_on": False, "action": "error", "error": str(e)}
# ========== 3. uiautomator2 存活检测 ==========
def check_u2_alive(self) -> Dict[str, Any]:
"""检测 uiautomator2 是否正常工作,异常时尝试重启"""
if not self.d:
return {"alive": False, "action": "no_device"}
try:
info = self.d.info
return {"alive": True, "package": info.get("currentPackageName", ""), "action": "ok"}
except Exception as e:
logger.warning(f"u2 异常: {e},尝试重启服务")
try:
self._shell("am instrument -w -r -e debug false "
"-e class androidx.test.runner.AndroidJUnitRunner "
"io.appium.uiautomator2.server.test/"
"androidx.test.runner.AndroidJUnitRunner 2>/dev/null &")
time.sleep(3)
info = self.d.info
return {"alive": True, "action": "restarted"}
except Exception as e2:
return {"alive": False, "action": "restart_failed", "error": str(e2)}
# ========== 4. 网络检测(委托 Hawk ==========
def check_network(self) -> Dict[str, Any]:
"""检测网络连通性,断网时委托 Hawk 尝试恢复"""
try:
from hawk import is_network_available, try_reconnect_network
if is_network_available(self.d):
return {"network": True, "action": "ok"}
logger.info("🌐 检测到断网,启动 Hawk 网络恢复...")
result = try_reconnect_network(self.d)
return {
"network": result.get("success", False),
"action": "hawk_reconnect",
"detail": result,
}
except ImportError:
# Hawk 不可用时仅做 ping 检测
out = self._shell("ping -c 1 -W 2 8.8.8.8 2>/dev/null && echo ok || echo fail")
return {"network": "ok" in out, "action": "ping_only"}
except Exception as e:
return {"network": False, "action": "error", "error": str(e)}
# ========== 5. ADB 连接检测 ==========
def check_adb_connection(self) -> Dict[str, Any]:
"""检测 ADB 连接状态"""
out = self._shell("getprop ro.serialno 2>/dev/null || echo unknown")
if out and out != "unknown":
return {"adb": True, "serial": out}
return {"adb": False, "serial": ""}
# ========== 6. 完整守护周期 ==========
def full_guard_cycle(self) -> Dict[str, Any]:
"""
执行一轮完整守护检查Agent 心跳循环中调用):
1. 自动处理弹窗USB授权等
2. 确保屏幕亮起
3. 检测 u2 存活
4. 检测网络
5. 检测 ADB
"""
results = {}
results["popups"] = self.dismiss_popups()
results["screen"] = self.ensure_screen_on()
results["u2"] = self.check_u2_alive()
results["network"] = self.check_network()
results["adb"] = self.check_adb_connection()
all_ok = (
results["u2"].get("alive", False)
and results["network"].get("network", False)
)
results["all_ok"] = all_ok
results["timestamp"] = int(time.time())
if not all_ok:
issues = []
if not results["u2"].get("alive"):
issues.append("u2不可用")
if not results["network"].get("network"):
issues.append("网络断开")
logger.warning(f"⚠️ 守护检查发现问题: {', '.join(issues)}")
else:
popup_count = results["popups"].get("count", 0)
if popup_count > 0:
logger.info(f"🛡️ 守护周期完成: 处理了 {popup_count} 个弹窗,连接正常")
return results