权限与能力管理
简介
本文件系统化梳理 iOS/macOS 平台的"权限与能力管理"体系,覆盖相机、麦克风、位置、运动追踪、日历、联系人、屏幕录制、通知、AppleScript、无障碍等权限的申请、运行时检查、状态监控与降级策略。文档同时给出权限配置清单、隐私政策链接与数据保护建议,并总结最佳实践与用户体验优化策略。
项目结构
围绕权限与能力管理的关键模块分布如下:
- iOS 端
- 网关连接控制器负责聚合当前设备权限状态并下发到网关
- 相机访问封装在共享模块中,供 iOS/macOS 复用
- 位置服务封装了授权请求与定位获取流程
- macOS 端
- 权限管理器统一处理各类权限的检查与交互式授权
- 权限监控器周期性轮询权限状态变化
- 设置界面支持逐项触发授权并刷新状态
- 共享模块
- 相机权限封装提供跨平台一致的授权判断与请求
- 配置与清单
- Info.plist 中声明各权限的用途说明文案
- entitlements 与签名脚本定义应用能力与运行时权限
聚合权限状态并下发"] CamCtl["CameraController
相机访问封装"] LocSvc["LocationService
位置授权与定位"] Ent["OpenClaw.entitlements
能力清单"] PlistI["Info.plist(iOS)
权限用途说明"] end subgraph "macOS" PermMgr["PermissionManager
权限检查/授权/状态"] PermMon["PermissionMonitor
状态轮询监控"] PermSet["PermissionsSettings
设置页交互"] PlistM["Info.plist(macOS)
权限用途说明"] end subgraph "共享" CamAuth["CameraAuthorization
跨平台相机授权"] end GConn --> CamAuth CamCtl --> CamAuth LocSvc --> GConn PermMgr --> PermMon PermSet --> PermMgr Ent --> GConn PlistI --> GConn PlistM --> PermMgr
核心组件
- iOS 网关连接控制器:动态收集相机、麦克风、语音识别、位置、屏幕录制、相册、通讯录、日历、提醒、运动追踪等权限状态,作为"permissions"参数随连接上报。
- macOS 权限管理器:集中实现各类权限的检查、交互式授权与状态查询;提供批量授权与单个能力授权接口。
- 相机权限封装:跨平台通用的相机/麦克风授权判断与请求逻辑。
- 位置服务:封装 iOS 位置授权流程,支持按需或始终授权模式。
- 权限监控与设置:macOS 提供定时轮询监控权限状态,设置页支持逐项触发授权并延时刷新以等待系统设置稳定。
架构总览
下图展示 iOS/macOS 权限与能力管理的整体交互:
详细组件分析
iOS 权限状态采集与下发
- 采集范围:相机、麦克风、语音识别、位置、屏幕录制、相册、通讯录、日历、提醒、运动追踪、手表支持状态等。
- 采集方式:直接调用系统 API 查询授权状态,部分能力通过组合状态判断(如日历/提醒的全权/写入权限)。
- 下发策略:作为"permissions"键值对随连接配置发送至网关,便于后端按权限启用相应能力。
macOS 权限管理器
- 统一入口:批量授权与单个能力授权,内部根据能力类型分派到对应子流程。
- 授权流程:
- 通知:请求授权选项并检查最终状态。
- AppleScript:执行轻量脚本验证自动化权限,必要时打开系统设置对应面板。
- 无障碍:通过系统信任接口触发提示。
- 屏幕录制:预检与请求授权。
- 相机/麦克风:调用系统 API 请求授权。
- 语音识别:调用系统 API 请求授权。
- 位置:若未开启系统定位服务则引导打开系统设置;否则发起当用/始终授权请求,并在超时后引导打开系统设置。
- 状态查询:提供批量状态查询接口,用于 UI 或监控器刷新。
相机权限封装(跨平台)
- 功能:判断相机/麦克风是否已授权;若未授权且允许交互,则触发系统授权请求。
- 适用:iOS 的相机控制器与 macOS 的相机捕获服务均复用该封装。
位置权限(iOS)
- 流程:若当前为"未决定",先请求当用授权;若需要"始终",而当前为"当用",则进一步请求始终授权。
- 定位获取:通过统一请求器封装一次性定位与超时控制,避免 UI 卡死。
权限状态监控与设置页
- 监控器:注册/注销计数,首次注册启动定时器,周期性轮询最新权限状态;最小轮询间隔保障性能。
- 设置页:逐项触发授权,授权完成后延时多次刷新以等待系统设置稳定生效。
依赖关系分析
- iOS 端
- 网关连接控制器依赖系统框架(AVFoundation、Contacts、CoreLocation、EventKit、Photos、ReplayKit、Speech)进行权限状态判断。
- 相机控制器与相机捕获服务依赖共享的相机授权封装。
- 位置服务依赖 CoreLocation。
- macOS 端
- 权限管理器依赖 AVFoundation、CoreLocation、Speech、UserNotifications、ApplicationServices 等。
- 设置页依赖权限管理器与监控器。
- 配置与清单
- Info.plist 中的 Usage Description 文案用于系统弹窗说明用途。
- entitlements 与签名脚本定义应用运行所需的系统能力键。
性能考量
- 轮询节流:macOS 权限监控器设置最小轮询间隔,避免频繁查询导致性能损耗。
- 异步与并发:授权与状态查询采用异步方式,避免阻塞主线程。
- 延迟刷新:设置页在触发授权后分阶段延迟刷新,减少 UI 抖动与无效重绘。
- 位置请求超时:位置服务对一次性定位请求设置超时,防止长时间等待。
故障排查指南
- 位置授权失败
- 现象:位置授权无响应或立即拒绝。
- 排查:确认系统定位服务已开启;若授权超时,系统会自动打开系统设置;检查位置权限文案与 Info.plist 配置。
- 语音唤醒权限
- 现象:无法使用语音唤醒。
- 排查:确保麦克风与语音识别权限均已授权;可通过权限管理器批量检查。
- 相机/麦克风被拒
- 现象:相机/麦克风调用抛出权限错误。
- 排查:调用相机授权封装进行授权;若被拒,引导用户前往系统设置开启。
- 日历/提醒/通讯录权限
- 现象:相关能力不可用。
- 排查:iOS 端通过 EventKit/Contacts 判断授权状态;macOS 端通过权限管理器检查并触发授权。
- 权限状态不一致
- 现象:UI 显示与实际行为不符。
- 排查:使用权限监控器强制刷新;设置页触发授权后等待系统设置稳定再刷新。
结论
本系统在 iOS 与 macOS 上实现了统一的权限与能力管理:iOS 侧通过网关连接控制器聚合权限状态并下发,macOS 侧通过权限管理器与监控器实现可观察的状态更新与交互式授权。共享模块保证跨平台一致性,配置清单确保系统弹窗具备清晰的用途说明。整体设计兼顾用户体验与合规要求,提供完善的降级与回退路径。
附录
权限配置清单与用途说明
- iOS
- Info.plist 中包含相机、麦克风、语音识别、位置、运动追踪、本地网络、照片库等用途说明。
- entitlements 用于声明推送环境等能力。
- macOS
- Info.plist 中包含通知、屏幕录制、相机、位置、麦克风、语音识别、Apple Events 等用途说明。
- 签名脚本定义运行时所需系统能力键。
隐私政策与数据保护
- 隐私政策链接:iOS 应用元数据中提供隐私政策地址。
- 数据保护建议:
- 仅在获得明确授权后采集与传输敏感数据。
- 对本地缓存的位置、媒体等数据进行最小化存储与及时清理。
- 在 UI 中清晰说明权限用途,尊重用户选择并提供便捷的系统设置入口。
最佳实践与用户体验优化
- 权限申请时机
- 在功能首次使用前或用户主动触发时申请,避免冷启动即弹窗。
- 对于位置、相机等敏感权限,提供简短说明与"稍后"选项。
- 状态反馈
- 使用设置页直观展示权限状态与操作按钮,授权后延时刷新。
- 对于系统设置变更,提供手动刷新入口。
- 降级与容错
- 当权限被拒时,提供替代方案或禁用相关功能,避免崩溃。
- 对位置请求设置合理超时与错误提示。
设备配对与连接
iOS 配对与连接相关代码主要位于以下模块:
- Onboarding(引导与配对 UI):OnboardingWizardView、QRScannerView
- Gateway(连接控制与发现):GatewayConnectionController、GatewayDiscoveryModel、GatewaySettingsStore、GatewayTLSPinning
- Model(应用状态与会话):NodeAppModel
- 平台对比参考:Android TLS 实现、通用重连策略、配对请求存储
引导与配对 UI"] QR["QRScannerView
二维码扫描"] end subgraph "iOS 连接层" GDC["GatewayConnectionController
连接控制器"] GDM["GatewayDiscoveryModel
发现模型"] GST["GatewaySettingsStore
配置与凭据存储"] TLS["GatewayTLSPinning
TLS 严格校验"] end subgraph "iOS 应用状态" NAM["NodeAppModel
全局状态与会话"] end OW --> QR OW --> GDC GDC --> GDM GDC --> GST GDC --> TLS GDC --> NAM
核心组件
- 引导与配对 UI(OnboardingWizardView)
- 负责展示配对步骤、触发 QR 扫描、处理扫描结果、显示连接状态与错误提示
- 自动检测上次保存的网关与凭据,必要时自动弹出 QR 扫描
- 连接控制器(GatewayConnectionController)
- 统一协调发现、解析、TLS 探测、信任提示与自动连接
- 处理手动连接、自动连接与重连逻辑
- 发现模型(GatewayDiscoveryModel)
- 基于 Bonjour 的网关发现,聚合多域结果并维护状态文本与调试日志
- 配置与凭据存储(GatewaySettingsStore)
- 使用 Keychain 存储实例 ID、令牌、密码、最近一次连接信息等
- 支持迁移与清理
- TLS 严格校验(GatewayTLSPinning)
- 对服务器证书进行指纹比对,支持首次信任(TOFU)与持久化
- 应用状态(NodeAppModel)
- 维护连接状态、自动重连开关、配对暂停标志与会话任务
架构总览
下图展示了 iOS 端从扫描到连接的关键交互路径,包括配对、发现、信任与连接。
详细组件分析
二维码扫描与配对流程
- 扫描入口与错误处理
- 引导界面提供"扫描二维码"入口;当扫描失败或无有效内容时,显示错误提示并保持 UI 可用
- 深度链接解析
- 支持 setup code(base64url JSON)与通用网关链接两种格式
- 解析后填充主机、端口、TLS 与可选令牌/密码,并自动发起连接
- 图片导入回退
- 允许从相册选择图片,通过 CoreImage 提取 QR 文本并重复上述流程
网关发现机制
- 多域发现与聚合
- 在多个 Bonjour 域启动浏览器,收集服务结果,按名称排序并去重
- 维护状态文本与调试日志,支持开启/关闭调试输出
- 端点解析
- 优先解析 SRV/A/AAAA 记录,避免使用未鉴权的 TXT 记录作为路由
- 自动连接策略
- 根据首选/上次发现的稳定 ID 或单个网关,在满足"已信任指纹"的前提下自动连接
信任建立与 TLS 校验
- 首次连接的指纹探测
- 若目标为明文且无已存指纹,则探测远端 TLS 指纹并通过信任提示展示给用户
- 严格校验与持久化
- 严格比对证书指纹;若允许 TOFU 且用户确认,将指纹写入持久化存储
- 平台一致性
- Android 端采用类似的指纹比对与 TOFU 流程,确保跨平台一致的安全体验
连接状态管理与自动重连
- 状态驱动的 UI 更新
- 通过 NodeAppModel 的状态文本与标识位(如配对暂停、自动重连开关)驱动 UI 表现
- 自动重连循环
- 在后台/前台切换、连接断开或需要配对时,按指数退避策略重试
- 支持抖动、最大延迟与中止信号,避免风暴式重试
- 会话与能力注册
- 连接配置变更后,重新应用节点能力、命令与权限,确保即时生效
配对代码生成与验证
- 生成与去重
- 生成唯一的人类友好配对码,保证不与现有集合冲突
- 请求生命周期
- 维护请求列表,按最后可见时间裁剪过期项,限制最大挂起数量
- 验证与批准
- 通过通道批准配对码,返回对应条目;支持账户维度匹配
连接控制器工作原理
- 关键职责
- 解析服务端点、构建 URL、组装连接选项(角色、作用域、能力、命令、权限、客户端标识)
- 管理信任提示、接受/拒绝、持久化指纹与自动连接
- 场景适配
- 发现连接:解析 SRV/A/AAAA,必要时探测指纹
- 手动连接:根据主机/端口/TLS 决策,必要时探测指纹
- 最近连接:优先从持久化恢复
网关信任提示与安全认证
- 信任提示
- 首次 TLS 连接时,展示远端证书 SHA-256 指纹,要求用户核验
- 认证源
- 支持设备令牌、共享令牌、密码等多种认证方式
- 平台实现
- iOS 与 Android 均实现指纹比对与 TOFU 逻辑,确保一致的安全边界
连接失败处理与重连机制
- 失败分类
- 未配对:暂停重连,等待用户批准;UI 保持稳定,避免闪烁
- 凭据缺失/错误:暂停自动重连,直到用户提供正确凭据
- 重连策略
- 指数退避 + 抖动,支持最大延迟与中止信号
- 在前台活跃、非后台静默期间维持更积极的重连节奏
连接配置存储
- Keychain 存储
- 实例 ID、令牌、密码、最近一次连接信息、客户端 ID 覆盖与代理选择
- UserDefaults 协同
- 用于开关、默认值与迁移;Keychain 作为最终可信存储
- 迁移与清理
- 首次访问时将旧的 UserDefaults 数据迁移到 Keychain,并清理冗余键
依赖关系分析
- 组件耦合
- OnboardingWizardView 依赖 GatewayConnectionController 与 GatewaySettingsStore
- GatewayConnectionController 依赖 GatewayDiscoveryModel、GatewaySettingsStore、GatewayTLSPinning 与 NodeAppModel
- 外部依赖
- Bonjour/NWBrowser 用于服务发现
- CoreImage 用于图片导入的 QR 提取
- Keychain 用于凭据与连接记录的持久化
性能考虑
- 发现与解析
- 限制解析超时,避免阻塞主线程;在主线程运行底层网络回调以确保及时响应
- 重连退避
- 合理设置初始延迟与最大延迟,结合抖动降低同步风暴风险
- UI 刷新
- 使用观察者模式与最小化状态变更,减少不必要的视图重建
系统集成功能
iOS 应用位于 apps/ios/Sources 下,采用按功能域分层的组织方式:
- 设备与系统状态:Device 子目录
- 系统服务访问:Calendar、Contacts、Reminders、EventKit
- 网关连接与安全:Gateway 子目录
- 应用模型与业务逻辑:Model 子目录
- 通知与系统事件:Services 子目录
- 应用入口与生命周期:OpenClawApp.swift
应用入口与生命周期"] end subgraph "设备与系统状态" B["DeviceInfoHelper.swift
设备与平台信息"] C["DeviceStatusService.swift
设备状态聚合"] D["NetworkStatusService.swift
网络状态检测"] end subgraph "系统服务" E["CalendarService.swift
日历事件"] F["ContactsService.swift
联系人"] G["RemindersService.swift
提醒事项"] H["EventKitAuthorization.swift
权限判定"] end subgraph "网关与连接" I["GatewayConnectionController.swift
发现/连接/信任验证"] J["NodeAppModel.swift
应用模型与会话管理"] end subgraph "通知与事件" K["NotificationService.swift
通知中心封装"] L["OpenClawApp.swift
推送/后台任务/场景事件"] end A --> J J --> I J --> C J --> D J --> E J --> F J --> G E --> H F --> H G --> H A --> K A --> L
核心组件
- 应用入口与生命周期:负责应用启动、场景状态变更、远程推送注册与处理、后台刷新任务调度。
- 设备信息与状态:提供平台版本、设备型号、应用版本、电池/热/存储/网络状态等聚合数据。
- 系统服务访问:通过 EventKit/Contacts 访问日历、联系人、提醒事项;统一权限判定与错误处理。
- 网关连接控制器:负责网关发现、服务解析、TLS 指纹校验、自动重连与能力/命令/权限上报。
- 应用模型:统一管理节点与操作者会话、语音唤醒、Talk 模式、深链处理、通知桥接与后台恢复策略。
- 通知中心:封装 UNUserNotificationCenter 的授权状态查询、授权请求与通知添加。
架构总览
系统以 NodeAppModel 为核心协调器,向上承载 UI 与用户交互,向下对接设备状态、系统服务、网关连接与通知系统。应用入口 OpenClawAppDelegate 负责系统级事件(推送、后台任务、场景切换)与应用模型的绑定。
详细组件分析
设备信息与状态监控
- 设备信息:平台字符串、设备家族、机器标识、应用版本/构建号等,用于网关节点负载与设置展示。
- 设备状态:电池电量/状态、低电量模式、热状态、存储总量/可用/已用、网络状态、系统运行时长。
- 网络状态:基于 Network.framework 的 NWPathMonitor,支持超时回退与接口类型识别。
系统服务访问与数据同步(日历、联系人、提醒事项)
- 权限控制:统一通过 EventKitAuthorization 判定读/写权限,避免在调用链中弹窗导致阻塞。
- 日历:支持事件范围查询与新增,限制返回条数,支持默认日历解析与错误语义化。
- 联系人:支持名称搜索与全量枚举,去重与匹配策略,支持电话/邮箱归一化与存在性检查。
- 提醒事项:支持列表筛选(全部/完成/未完成)与新增,支持截止时间解析与默认列表解析。
网关连接与系统事件处理
- 发现与连接:基于 Bonjour/MDNS 解析服务端点,支持手动与自动连接,TLS 指纹校验与信任提示。
- 自动重连:根据场景状态(前台/后台)与健康监测策略进行重连与抑制。
- 通知桥接:将 Apple Watch 快速回复映射为应用内事件,支持静默推送唤醒与后台刷新任务。
- 会话与能力:动态生成能力、命令与权限集合,确保与系统授权状态一致。
通知与系统事件处理
- 授权与分类:动态请求通知授权,按动作数量与样式创建分类,支持被动/限时/活动打断级别。
- 推送处理:远程推送到达时唤醒应用模型,执行静默唤醒逻辑并安排后台刷新任务。
- 场景事件:场景进入后台时安排后台刷新任务,前台恢复时清理抑制与恢复会话。
依赖关系分析
- 组件耦合:NodeAppModel 作为协调器,依赖设备状态、网络状态、系统服务与网关控制器;应用入口仅负责生命周期与系统事件。
- 外部依赖:UIKit/Foundation/Network/EventKit/Contacts/Photos/ReplayKit 等系统框架;OpenClawKit 协议与数据模型。
- 可能的循环依赖:当前结构以 NodeAppModel 为中心向外依赖,未见明显循环;注意服务层不应反向依赖应用模型。
性能考虑
- 异步与超时:网络状态检测使用带超时的 Continuation,避免长时间阻塞;推送与后台任务使用任务调度与过期处理。
- 资源释放:后台任务到期时取消挂起任务,结束后台任务句柄;断开网关连接前清理状态。
- 权限与UI:避免在 invoke 流程中触发系统授权弹窗,降低超时风险;对联系人/日历等批量操作设置上限。
- 缓存策略:应用模型维护会话键与品牌配置,减少重复请求;后台恢复时优先健康检查再断线重连,避免"假连接"。
语音触发与唤醒
语音相关能力在多端并行实现,并通过网关统一管理全局触发词列表:
- iOS:VoiceWakeManager 负责唤醒检测;TalkModeManager 支持连续对话与 TTS
- macOS:VoiceWakeRuntime/VoiceWakeTester/VoiceWakeSettings 提供运行时、测试与设置界面
- Android:VoiceWakeMode 控制唤醒模式;SecurePrefs 存储本地偏好;GatewayEventHandler 同步网关触发词
- 网关:voicewake.ts 提供方法与事件广播;infra 层持久化触发词
- 通用 TTS:支持多种提供商与输出格式转换(含电话场景)
核心组件
- 语音唤醒管理器(iOS)
- 负责麦克风与语音识别权限申请、音频引擎配置、识别任务启动与结果回调、暂停/恢复外部音频捕获、触发词匹配与命令分发
- 语音唤醒运行时(macOS)
- 基于 AVAudioEngine 与 SFSpeechRecognizer 的识别管线;静音窗口、触发后等待、冷却与去抖;触发音效播放
- 触发词同步与存储(网关)
- 提供获取/设置接口与事件广播;本地 JSON 文件持久化;默认触发词与清理规则
- 语音反馈与通话模式(多端)
- Talk 模式:静音窗口发送、打断策略、ElevenLabs 流式播放;电话场景:TTS 输出格式转换(PCM→μ-law 8kHz)
- 偏好设置与测试(macOS)
- 触发词编辑、附加语言选择、麦克风选择、电平表与测试状态展示
架构总览
语音唤醒与通话的整体流程如下:
详细组件分析
iOS 语音唤醒管理器(VoiceWakeManager)
职责与流程要点:
- 权限管理:麦克风与语音识别授权,超时处理与错误提示
- 音频引擎:测量模式、混音与其他应用、蓝牙支持;输入节点 tap 缓冲队列
- 识别管线:识别请求、部分结果、回调中提取命令、去重与重启
- 外部音频捕获:暂停/恢复,避免冲突
- 触发词:从 UserDefaults 加载、变更监听、清洗与限制长度/数量
macOS 语音唤醒运行时与测试
- 运行时(VoiceWakeRuntime)
- 静音窗口、触发后等待、强制停止、去抖、RMS 能量估计、触发音效
- 文本回退静音检测、冷却与防重复触发
- 测试器(VoiceWakeTester)
- 识别请求/任务生命周期、输入节点 tap、缓冲追加、状态机更新
- 触发词归一化、片段令牌化、静音等待与最终化
通道数/采样率有效"] CheckFmt --> InstallTap["安装输入节点 Tap
缓冲队列"] InstallTap --> EngineStart["启动 AVAudioEngine"] EngineStart --> Listen["监听识别回调"] Listen --> Match{"匹配触发词?"} Match --> |是| BeginCap["开始捕获
播放触发音效"] Match --> |否| Listen BeginCap --> SilenceWin["静音窗口计时"] SilenceWin --> Timeout{"超时/静音?"} Timeout --> |是| Finalize["提交并重置"] Timeout --> |否| Continue["继续收集"] Continue --> Listen
触发词同步与偏好设置
- 网关侧
- 方法:voicewake.get / voicewake.set;事件:voicewake.changed
- 存储:~/.openclaw/settings/voicewake.json;默认触发词;清理与上限
- iOS 侧
- UserDefaults 读写触发词;变更监听;清洗与长度/数量限制
- macOS 侧
- 设置界面:触发词表格、附加语言、麦克风选择、电平表、测试状态
- Android 侧
- VoiceWakeMode:off/foreground/always;SecurePrefs 存储触发词与模式;GatewayEventHandler 从网关拉取并应用
语音通话模式与语音 Orb 覆盖层
- 行为概览:持续语音对话,静音窗口发送,打断策略,回复写入 WebChat,支持语音指令前缀控制 TTS
- macOS UI:菜单栏切换、Overlay 显示阶段状态(听/想/说)、点击交互
- iOS:动态噪声阈值估计、阈值上下限、音频活动记录
- Android:输出格式校验(pcm_16/22/24/44kHz、mp3_),延迟层级校验
语音反馈与电话场景 TTS
- 通用 TTS:按优先级尝试提供商,ElevenLabs 输出格式适配,返回采样率与格式
- 电话 TTS:将 PCM 转换为 μ-law 8kHz(Twilio/Media Streams)
- Discord 语音消息:生成波形占位图,失败时回退
依赖关系分析
- 平台耦合度
- iOS 与 macOS 分别封装独立的识别与 UI;Android 以模式开关与本地偏好为主
- 网关集中化
- 触发词由网关统一管理并通过事件广播,确保跨端一致性
- 音频栈
- AVAudioEngine + SFSpeechRecognizer(iOS/macOS);Tap 队列与异步 drain 保证实时性
- TTS 与输出
- 通用 TTS 解析配置与提供商;电话场景专用格式转换
性能考量
- 音频缓冲与实时性
- iOS/macOS 使用输入节点 tap 将缓冲复制到队列,drain 任务周期性追加至识别请求,降低主线程压力
- 静音检测与阈值
- iOS 动态噪声阈值估计,基于样本排序取均值;macOS RMS 能量估计与噪声地板融合,提升端点检测鲁棒性
- 输出格式与延迟
- Android 支持多种 PCM 采样率;macOS/iOS 默认低延迟 PCM;可选 mp3 以换取更低带宽
- 识别与播放
- 识别部分结果与去重,避免重复触发;触发音效与播放打断策略减少误操作
Canvas 表面渲染
Canvas 在多端通过 WebView/原生窗口承载 A2UI 内容,并由统一的消息协议驱动渲染与交互。
导航/快照/调试状态"] IOS_ScreenWebView["ScreenWebView
WKWebView 协作器"] IOS_ScreenRecordService["ScreenRecordService
ReplayKit 录制"] end subgraph "Android" AND_CanvasScreen["CanvasScreen
Compose 承载 WebView"] AND_CanvasController["CanvasController
导航/快照/调试状态"] end subgraph "macOS" MAC_CanvasManager["CanvasManager
面板展示/自动导航"] end subgraph "共享层" Shared_A2UICommands["CanvasA2UICommands
推送/重置命令"] Shared_A2UIAction["CanvasA2UIAction
动作上下文/格式化"] Shared_BootstrapJS["bootstrap.js
A2UI Host/Lit 组件"] Shared_ScreenCommands["ScreenCommands
屏幕命令定义"] end IOS_ScreenController --> IOS_ScreenWebView IOS_ScreenController --> IOS_ScreenRecordService AND_CanvasScreen --> AND_CanvasController MAC_CanvasManager --> Shared_BootstrapJS Shared_A2UICommands --> Shared_BootstrapJS Shared_A2UIAction --> Shared_BootstrapJS Shared_ScreenCommands --> IOS_ScreenRecordService Shared_ScreenCommands --> AND_CanvasController
核心组件
- iOS ScreenController:负责 WebView 导航、快照生成、调试状态注入与 A2UI 就绪检测
- Android CanvasController:负责 WebView 导航、快照生成、调试状态注入与参数解析
- macOS CanvasManager:负责 Canvas 面板展示、自动导航到网关推送的 A2UI 地址、调试状态刷新
- 共享 A2UI 命令与动作:定义 canvas.a2ui.push/reset 与动作上下文格式化
- bootstrap.js:A2UI Host 组件,处理消息、派发用户动作、桥接到原生
- 屏幕录制服务:iOS 使用 ReplayKit,Android/macOS 通过网关或应用内流程实现
架构总览
Canvas 渲染采用"消息驱动 + 平台桥接"的模式:
- 应用侧通过命令推送 A2UI 消息,A2UI Host 解析并渲染为可交互界面
- 用户在 Canvas 中触发的动作被序列化并通过原生桥发送至应用层
- 应用层根据动作类型执行业务逻辑(如屏幕录制、导航等),并将结果回推渲染
bootstrap.js" participant Native as "原生桥接" participant GW as "网关/远程通道" App->>Host : "canvas.a2ui.push" 推送消息数组 Host->>Host : "processMessages()" 渲染表面 Host-->>App : "getSurfaces()" 返回表面列表 App->>Native : "注册消息处理器/JS 接口" GW-->>App : "推送 A2UI 地址/快照" App->>Host : "自动导航到 A2UI 地址" App->>Native : "屏幕录制/快照请求" Native-->>App : "返回录制数据/快照"
详细组件分析
iOS 屏幕控制器与 WebView 集成
- 导航与安全:支持本地文件/HTTP/HTTPS;对本地回环地址进行隔离,避免从远程网关加载
- 调试状态:通过注入脚本显示网关状态标题/副标题
- 快照:基于 WKWebView 截图,支持 PNG/JPEG 及质量控制
- A2UI 就绪检测:轮询判断 openclawA2UI 是否可用
- 屏幕录制:通过 ScreenRecordService 使用 ReplayKit 写入 MP4,支持音频与帧率限制
Android WebView 集成与 Canvas 控制器
- Compose 承载 WebView 生命周期管理,销毁时移除 JS 接口与停止加载
- CanvasController 提供导航、调试状态注入、JS 评估与快照(PNG/JPEG)
- 参数解析:支持 url、maxWidth、format、quality 等
注册 JS 接口"] Attach --> Navigate["CanvasController.navigate(url)"] Navigate --> Eval["CanvasController.eval(js)"] Navigate --> Snapshot["CanvasController.snapshotBase64/format/quality/maxWidth"] Snapshot --> Dispose["页面销毁/退出时清理"] Dispose --> End(["结束"])
macOS Canvas 面板与自动导航
- CanvasManager 管理面板生命周期与锚点定位,支持默认锚点或鼠标位置
- 自动导航:监听网关推送,解析 A2UI 地址并在面板可见时跳转
- 调试状态:根据连接模式显示标题/副标题
A2UI 消息处理与动作桥接
- 命令:canvas.a2ui.push/reset,支持 JSONL 别名
- 动作上下文:包含会话、表面、组件与上下文 JSON,支持标签值清洗与紧凑 JSON
- Host:接收消息数组,渲染为多个表面;捕获用户动作并桥接原生
- 平台差异:iOS 使用 WKScriptMessageHandler,Android 使用 WebView.addJavascriptInterface
屏幕录制与远程控制
- iOS:使用 ReplayKit 捕获视频/AAC,AVAssetWriter 写入 MP4;支持帧率限制与音频开关
- Android/macOS:通过网关推送或应用内命令触发录制/快照,返回二进制或 Base64 数据
- 远程控制:macOS CanvasManager 监听网关状态,自动导航到 A2UI 地址,实现跨设备同步
screenIndex/duration/fps/audio"] Parse --> Clamp["速率限制/参数校验"] Clamp --> Start["启动捕获/写入器"] Start --> Loop["采样循环
视频/音频"] Loop --> Stop["停止捕获/完成写入"] Stop --> Done["返回输出路径/数据"]
依赖关系分析
- 平台耦合度:iOS/macOS 主要依赖 WKWebView/原生窗口;Android 依赖 WebView
- 共享协议:A2UI 命令与动作在多端一致,确保跨平台一致性
- 外部依赖:ReplayKit(iOS)、AVFoundation(iOS)、Lit/A2UI 组件(Web)
A2UI 命令/动作/Host"] --> iOS["iOS
ScreenController/WKWebView"] Shared --> Android["Android
CanvasController/WebView"] Shared --> macOS["macOS
CanvasManager/窗口"] iOS --> ReplayKit["ReplayKit/AVFoundation"] Android --> WebView["Android WebView"] macOS --> Window["原生窗口/面板"]
性能考量
- 快照压缩与尺寸裁剪:iOS/macOS 优先使用原生编码;Android 通过 Bitmap 缩放与压缩
- 帧率与时长限制:iOS 录制对 FPS 与时长进行钳制,避免资源浪费
- UI 更新节流:A2UI Host 在收到消息后批量渲染并请求更新,减少重绘次数
- 调试状态开销:仅在启用调试时注入状态脚本,避免影响生产性能
iOS 节点
iOS 节点应用位于 apps/ios,采用 Swift 6、Xcode 16+、基于 XcodeGen 的工程配置。核心模块包括:
- 应用入口与场景生命周期:OpenClawApp、NodeAppModel
- Canvas 渲染与交互:ScreenController、WebView 集成
- 语音触发:VoiceWakeManager(基于 SFSpeech)
- 实时活动:LiveActivityManager、OpenClawActivityAttributes、ActivityWidget
- 网关连接与能力路由:GatewayConnectionController、NodeAppModel 中的能力分发
- 权限与后台任务:Info.plist 声明、BGTaskScheduler、后台保活与重连策略
应用入口"] --> B["NodeAppModel
状态/会话/能力路由"] B --> C["ScreenController
Canvas 渲染/快照/脚本执行"] B --> D["VoiceWakeManager
语音触发"] B --> E["LiveActivityManager
实时活动"] B --> F["GatewayConnectionController
网关连接/权限上报"] E --> G["OpenClawActivityAttributes
活动属性/状态"] E --> H["OpenClawLiveActivity
小组件视图"]
核心组件
- 应用模型与会话管理:NodeAppModel 统一维护"节点"和"操作者"两类网关会话,负责健康监测、后台保活、权限协调、命令路由与错误处理。
- Canvas 渲染与交互:ScreenController 提供导航、默认画布加载、调试状态注入、快照与 JS 评估,支持本地资源与远程 URL。
- 语音触发:VoiceWakeManager 管理麦克风与语音识别授权、音频引擎、识别回调与命令提取,支持与其他音频子系统(如 Talk)互斥。
- 实时活动:LiveActivityManager 管理 Activity 启动、状态更新与重复实例清理;小组件通过 OpenClawLiveActivity 展示。
- 网关连接与权限:GatewayConnectionController 汇总当前权限状态并上报;OpenClawApp 注册后台刷新任务以唤醒恢复。
架构总览
下图展示从用户交互到网关调用、再到 Canvas 与实时活动的端到端流程。
组件详解
设备配对流程
- 客户端侧:NodeAppModel 在启动或场景激活时,通过网关会话查询待审批请求列表,暂停自动重连以稳定 UI,等待人工批准后恢复。
- 网关侧:设备配对插件周期轮询待审批请求,向客户端推送提示;批准/拒绝通过网关 RPC 完成。
- 流程要点:前台优先、避免后台重连风暴、明确的人机审批窗口。
Canvas 表面渲染与交互
- 导航与默认画布:支持本地 scaffold HTML 与远程 URL;对 loopback 场景进行安全过滤,避免误加载远程内容。
- JS 评估与快照:提供 PNG/JPEG 快照与 JS 字符串评估接口,用于 A2UI 动作反馈与调试。
- A2UI 动作:解析用户在 Canvas 内部点击的动作参数,构造消息上下文并通过网关下发。
语音触发(Voice Wake)
- 权限与授权:分别请求麦克风与语音识别授权;在模拟器上禁用以规避音频栈问题。
- 音频管线:安装输入 tap 将 PCM 缓冲复制到队列,异步注入识别请求;识别回调在主线程处理。
- 互斥与暂停:当 Talk 模式启用或外部音频捕获需求出现时,暂停语音触发并释放音频会话,结束后恢复。
- 命令提取:基于唤醒词门控规则匹配识别片段,去抖后派发命令。
LiveActivity 实时活动
- 生命周期:启动时校验权限,若已存在则复用并清理重复实例;根据连接状态更新内容(连接中/空闲/断开)。
- 内容状态:包含状态文本、是否空闲/断开/连接中及起始时间。
- 小组件:使用 ActivityConfiguration 动态岛适配不同区域布局。
推送通知与后台执行
- 注册与令牌:应用启动即注册远程通知;APNs 令牌在网关连接建立后上报。
- 后台刷新:注册 BGAppRefreshTask,按需调度唤醒刷新,记录日志便于排障。
- 保活与重连:场景切换、后台保活租约、健康监测与断线重连策略协同工作,避免后台死连接。
权限模型与后台限制
- 权限声明:Info.plist 明确相机、麦克风、定位、运动、照片库、Bonjour、本地网络等用途说明。
- 运行时授权:VoiceWakeManager 与 GatewayConnectionController 分别检查并请求授权。
- 后台限制:后台执行受限,Canvas、Camera、Screen、Talk 等命令在后台被限制;后台定位需"始终"授权。
离线与设备间同步
- 离线策略:NodeAppModel 在后台场景采用"保活租约 + 健康监测 + 主动断开/重连"的组合,避免后台死连接。
- 同步与会话:主会话键与选中代理 ID 保存在网关设置存储中,跨会话保持一致。
- 会话键:主会话键与代理选择影响分享网关中会话上下文。
数据加密与隐私
- 加密开关:Info.plist 中关闭"使用非豁免加密",表明应用不涉及加密出口控制。
- 隐私声明:各权限用途在 Info.plist 中明确说明,符合 App Store 审核要求。
- 建议:生产环境建议启用传输层加密(TLS)与最小化数据采集,遵循平台隐私指引。
依赖关系分析
- 模块耦合:NodeAppModel 作为中枢,依赖 ScreenController、VoiceWakeManager、LiveActivityManager 与 GatewayConnectionController。
- 外部框架:ActivityKit、WidgetKit、Speech、AVFoundation、WebKit、UserNotifications 等。
- 目标产物:主应用、分享扩展、活动小组件、WatchApp 扩展等。
性能与后台行为
- 后台限制:Canvas、Camera、Screen、Talk 等命令在后台受限;后台定位需"始终"授权。
- 保活策略:场景切换、保活租约、健康监测与主动断开/重连协同,减少后台死连接。
- 资源影响:测试路径强调避免持续高热与过度耗电,建议在后台触发场景中谨慎使用高负载能力。
LiveActivity 实时活动
LiveActivity 相关代码主要位于 iOS 应用与 Activity 小组件扩展中,核心由以下模块组成:
- 活动属性与状态模型:定义活动的属性键与内容状态,确保应用与小组件共享一致的数据契约。
- 活动管理器:负责启动、更新与清理活动,维护当前活动实例与去重逻辑。
- 小组件视图:在锁屏与动态岛屿展示活动状态,提供简洁的状态指示与时间显示。
- 应用层编排:在连接状态变化时驱动活动状态更新,保证 UI 与活动状态一致。
- 配置清单:声明支持 LiveActivities 能力与后台模式,为活动运行提供系统级许可。
连接状态编排"] LAM["LiveActivityManager
活动生命周期管理"] end subgraph "LiveActivity 扩展" ATTR["OpenClawActivityAttributes
属性+状态模型"] WIDGET["OpenClawLiveActivity
锁屏/动态岛屿视图"] WBUNDLE["OpenClawActivityWidgetBundle
小组件入口"] end subgraph "系统与配置" SYS["iOS 系统 ActivityKit"] INFO_APP["Info.plist(iOS 应用)
NSSupportsLiveActivities"] INFO_WDG["Info.plist(Activity 小组件)
NSSupportsLiveActivities"] end NAM --> LAM LAM --> ATTR WIDGET --> ATTR WBUNDLE --> WIDGET LAM --> SYS WIDGET --> SYS INFO_APP --> SYS INFO_WDG --> SYS
核心组件
- 活动属性与状态模型
- 定义活动属性键(如代理名、会话键),以及内容状态字段(文本、是否空闲/断开/连接中、开始时间等),用于在应用与小组件之间传递一致的状态。
- 活动管理器
- 单例管理当前活动实例,负责启动、更新与清理;具备去重能力,保留最"新"的活动并结束其他重复实例;提供连接中/空闲/断开三种状态的快速切换。
- 小组件视图
- 锁屏视图与动态岛屿视图根据状态渲染不同元素(指示圆点颜色、文本、尾部图标/计时器),并在断开/连接中/空闲状态下采用不同的视觉反馈。
- 应用层编排
- 在连接建立、断开、重连等关键节点调用活动管理器更新状态,确保活动与应用实际状态保持一致。
- 配置与权限
- 应用与小组件 Info.plist 均声明支持 LiveActivities;应用 Info.plist 还声明了后台模式,为活动在后台维持与更新提供基础条件。
架构总览
下图展示了从应用层到系统与小组件的整体流程:应用在连接状态变化时通过活动管理器更新活动内容;小组件基于同一属性模型渲染锁屏与动态岛屿界面;系统负责活动的生命周期与显示。
组件详解
活动属性与状态模型
- 属性键
- 包含代理名与会话键,用于区分不同会话与代理实例,便于在多实例场景下进行状态隔离与追踪。
- 内容状态
- 提供状态文本、空闲/断开/连接中的布尔标记与开始时间,小组件据此决定渲染样式与尾部信息(如计时器或断网图标)。
- 预览与调试
- 提供预览状态常量,便于在开发阶段快速验证小组件外观与交互。
活动管理器
- 单例与日志
- 使用单例模式集中管理活动生命周期,内部记录关键事件日志,便于问题定位。
- 启动与去重
- 启动时检查系统授权与现有活动集合,保留最新的活动实例并结束其他重复实例,避免 UI 与系统状态不一致。
- 状态更新
- 提供连接中、空闲、断开三态的快速构建与更新方法,统一由活动实例异步更新。
- 活动可用性检测
- 通过活动状态判断当前活动是否仍处于活跃状态,若非活跃则清空缓存实例,保证后续操作正确性。
小组件视图
- 锁屏视图
- 左侧状态圆点按三态着色,中间为品牌与状态文本,右侧根据状态显示进度、断网图标或计时器。
- 动态岛屿
- 在展开区域以"左侧指示点 + 中间文本 + 右侧图标/计时"的布局呈现;紧凑与最小化视图分别适配不同空间。
- 状态映射
- 断开/连接中/空闲/其他四种状态对应不同颜色与尾部元素,确保用户在锁屏即可直观感知连接健康度。
应用层编排与状态同步
- 连接建立/重连
- 在连接成功后将活动切换为空闲态,向用户传达"可工作"的状态。
- 断开/离线
- 在断开或离线时将活动切换为断开态,提示用户网络异常。
- 连接中
- 在尝试连接或重连过程中将活动切换为连接中态,避免用户误以为已连接。
- 启动时机
- 若当前无活动,则启动新活动;否则仅更新状态,减少不必要的创建与销毁。
macOS 工作活动存储(对比参考)
- 作用
- 记录作业与工具类活动的会话键、角色、类型、标签与时间戳,推导当前工作状态与图标状态,辅助 macOS 上的活动可视化与状态管理。
- 关键点
- 通过会话键聚合作业与工具活动,延迟移除工具结果以避免闪烁;根据主会话优先级与最近更新时间选择当前活动。
- 与 LiveActivity 的关系
- 虽为 macOS 实现,但其"会话键/角色/类型/时间戳"的建模思路可借鉴至 iOS 活动状态设计,提升跨端一致性。
依赖关系分析
- 组件耦合
- NodeAppModel 与 LiveActivityManager 强耦合:前者在连接状态变化时直接驱动后者更新活动状态,确保 UI 与活动状态一致。
- LiveActivityManager 与 ActivityKit 弱耦合:通过系统 API 创建与更新活动,内部封装错误日志与去重逻辑,降低上层复杂度。
- 小组件与属性模型弱耦合:小组件仅依赖属性模型提供的状态字段进行渲染,不直接参与业务逻辑。
- 外部依赖
- iOS 系统 ActivityKit 提供活动生命周期与显示能力;Info.plist 声明支持 LiveActivities 与后台模式,为活动在后台维持与更新提供系统级许可。
性能与后台策略
- 后台更新策略
- 应用 Info.plist 声明了后台模式,结合 LiveActivities 能力,可在后台维持活动显示与状态更新。建议在后台场景下避免频繁刷新,采用节流或按需更新策略。
- 去重与清理
- 活动管理器在启动时对重复活动进行清理,保留最新实例,减少系统资源占用与渲染冲突。
- 渲染优化
- 小组件视图根据状态选择轻量渲染(如断开态仅显示图标),降低锁屏与动态岛屿的绘制开销。
- 状态切换成本
- 连接中/空闲/断开三态切换通过统一工厂方法生成状态对象,避免重复计算与内存抖动。