Android 应用层的关键文件组织如下:
- 应用入口与运行时:NodeApp、NodeRuntime
- 前台服务:NodeForegroundService
- 网关会话:GatewaySession(WebSocket 客户端)
- 平台服务:DeviceNotificationListenerService(通知监听)
- 设备信息与健康:DeviceHandler(电池、内存等)
- 安全与偏好:SecurePrefs
- UI 绑定:MainViewModel(对 NodeRuntime 的调用封装)
应用入口"] B["NodeRuntime
节点运行时"] C["NodeForegroundService
前台服务"] D["DeviceNotificationListenerService
通知监听服务"] end subgraph "网关层" E["GatewaySession
WebSocket 会话"] end subgraph "设备与系统" F["DeviceHandler
设备信息/健康"] G["SecurePrefs
加密偏好"] end A --> B B --> C B --> E B --> F B --> G D --> B
核心组件
- NodeApp:应用生命周期持有者,延迟初始化 NodeRuntime
- NodeRuntime:核心运行时,负责:
- 网关连接(操作员会话与节点会话)
- 自动重连与断线恢复
- 状态流(连接状态、服务器名、麦克风状态等)
- 事件分发与命令派发(InvokeDispatcher)
- 通知监听事件转发到节点会话
- NodeForegroundService:前台服务,负责:
- 创建并更新通知
- 订阅 NodeRuntime 状态流,动态刷新通知内容
- 提供"断开"动作以触发 NodeRuntime 断开连接
- GatewaySession:WebSocket 客户端,封装连接、请求、事件与自动重连循环
- DeviceNotificationListenerService:系统通知监听服务,将通知事件转发给 NodeRuntime
- DeviceHandler:设备健康信息采集(电池、内存、温度等)
- SecurePrefs:加密存储(SharedPreferences 加密包装),用于实例 ID、显示名、网关凭据等
架构总览
下图展示从前台服务到运行时、再到网关会话的整体交互流程。
详细组件分析
NodeForegroundService 前台服务
- 职责
- 在应用启动后立即以前台服务方式运行,避免被系统回收
- 订阅 NodeRuntime 的多源状态流,动态更新通知标题与文本
- 提供"断开"动作,通过广播触发 NodeRuntime 断开连接并停止自身
- 关键点
- 使用通知通道(IMPORTANCE_LOW)与 FOREGROUND_SERVICE_TYPE_DATA_SYNC
- 通知设置为 ongoing 且仅提示一次,确保用户可随时查看
- 首次启动时调用 startForegroundWithTypes,后续使用 notify 更新
- 生命周期
- onCreate:创建通知通道、初始通知、订阅状态流
- onStartCommand:处理 ACTION_STOP 动作,触发断开并 stopSelf
- onDestroy:取消协程作业,释放资源
NodeRuntime 节点运行时与生命周期控制
- 职责
- 管理两个 GatewaySession:操作员会话与节点会话
- 维护连接状态、服务器名、远端地址、主会话键等状态流
- 自动发现与自动连接(受信任网关 TLS 指纹约束)
- 将通知监听事件转发至节点会话
- 管理麦克风、TTS、Canvas 等子系统
- 连接与重连
- runLoop 循环尝试连接,失败指数退避,保持持续重连
- onConnected/onDisconnected 回调驱动状态流更新
- 状态流
- isConnected、statusText、serverName、remoteAddress、micEnabled、micIsListening 等
- 通过 combine 组合多个流,驱动前台通知实时更新
- 生命周期控制
- setForeground:切换前后台,必要时停止语音会话
- disconnect:清空目标端点并断开会话
网关会话(GatewaySession)与自动重连
- 运行循环
- runLoop:根据 desired 目标连接;失败则延迟重试,指数退避上限 8 秒
- connectOnce:建立单次连接,等待关闭或异常
- 请求与事件
- request:发送 RPC 请求并等待响应,超时抛出异常
- handleEvent:处理事件帧,识别 node.invoke.request 并派发到 onInvoke
- TLS 与指纹
- 支持 TLS 参数与指纹回调,首次连接时探测并保存指纹,后续自动信任
通知监听服务与状态同步
- DeviceNotificationListenerService
- 实现 onNotificationPosted,解析通知为内部条目并写入存储
- 过滤自身包名的通知,向 NodeEventSink 发送变更事件
- 事件同步
- NodeRuntime 初始化时设置事件 Sink,将事件转发到节点会话(node.event)
- 前台服务订阅 NodeRuntime 状态流,实现 UI/通知与运行时状态的双向同步
设备健康与内存管理
- DeviceHandler
- 读取电池快照(状态、充电、电量分数、温度)
- 读取内存快照(总内存、可用内存、是否低内存、压力等级)
- 输出 JSON 负载供上层使用
- 内存与电池指标可用于:
- UI 健康指示
- 自适应降级(如降低麦克风采样率、暂停非关键任务)
安全与配置
- 权限与前台服务类型
- AndroidManifest 声明 FOREGROUND_SERVICE、FOREGROUND_SERVICE_DATA_SYNC、INTERNET 等
- NodeForegroundService 使用 FOREGROUND_SERVICE_TYPE_DATA_SYNC
- 加密存储
- SecurePrefs 使用 EncryptedSharedPreferences 存储网关令牌、密码、TLS 指纹等
- 实例 ID 与显示名自动生成/迁移,保证唯一性与隐私
- TLS 指纹校验
- 首次连接探测指纹,用户确认后持久化,后续自动信任
依赖关系分析
- 组件耦合
- NodeForegroundService 强依赖 NodeRuntime 的状态流,弱依赖 NodeApp(获取 runtime)
- NodeRuntime 依赖 GatewaySession(两个会话)、DeviceNotificationListenerService(事件源)、DeviceHandler(健康数据)、SecurePrefs(配置与凭据)
- GatewaySession 依赖 OkHttp WebSocket、DeviceIdentityStore、DeviceAuthStore
- 外部依赖
- Android 系统服务:通知、前台服务、通知监听服务
- OkHttp:WebSocket 客户端
- 可能的循环依赖
- 当前结构无直接循环依赖;事件通过回调与状态流解耦
性能与电池优化
- 通知即时行为
- 使用 FOREGROUND_SERVICE_IMMEDIATE,确保通知立即可见,减少用户感知延迟
- 协程与背压
- 使用 SupervisorJob + IO 主线程,避免主线程阻塞
- 状态流合并使用 distinctUntilChanged,减少无效 UI 刷新
- 自动重连与退避
- 指数退避上限 8 秒,降低网络波动对系统的影响
- 电池与内存
- DeviceHandler 提供电池/内存快照,便于在低电量/低内存时降级处理
- 建议
- 在低电量/低内存场景下暂停非关键任务(如 Canvas 重载)
- 控制通知频率,避免频繁更新导致 CPU/电量消耗
- 对长耗时任务拆分为小步,配合 WorkManager 或前台服务
界面组件
Android UI 采用 Jetpack Compose 架构,以"主题层 → 布局层 → 屏幕层 → 功能组件层"的分层组织方式:
- 主题层:统一颜色、字体与动态色方案
- 布局层:顶部状态栏、底部导航、Scaffold 容器
- 屏幕层:引导流程、标签页容器、各功能页(聊天、语音、屏幕、设置)
- 功能组件层:聊天输入区、消息气泡、工具调用提示、错误提示等
设置主题与根内容"] --> B["OpenClawTheme
Material3 动态色"] B --> C["RootScreen
引导/标签页入口"] C --> D["PostOnboardingTabs
Scaffold + TabBar"] D --> E["ConnectTabScreen"] D --> F["ChatSheetContent
消息列表 + 输入区"] D --> G["VoiceTabScreen"] D --> H["ScreenTabScreen"] D --> I["SettingsSheet
权限与设备配置"]
核心组件
- 主题系统:基于 Material3 动态色,提供覆盖容器色与图标色的辅助函数,确保深浅模式一致体验
- 移动 UI 令牌:集中定义颜色、字体族与排版样式,统一视觉语言
- 根屏幕:根据引导完成状态切换到标签页容器
- 标签页容器:顶部状态栏 + 底部导航 + 内容区域,支持 IME 折叠与 Tab 切换
- 设置页面:集中管理权限、位置模式、睡眠策略、通知与数据访问等
- 聊天界面:会话选择、消息列表、输入区(文本+图片附件)、工具调用提示、实时流式助手
架构总览
Compose 驱动的单 Activity 多屏幕架构,通过 ViewModel 暴露状态流,屏幕层订阅并渲染 UI;功能组件通过参数回调与 ViewModel 交互,形成清晰的数据流向。
详细组件分析
主题系统与颜色/字体令牌
- 动态色方案:根据系统深浅模式选择动态亮/暗配色,作为 MaterialTheme 的 colorScheme
- 覆盖容器色与图标色:提供 overlayContainerColor 与 overlayIconColor,用于浮层与覆盖 UI 的一致性
- 颜色令牌:集中定义背景、表面、边框、文本、强调色、成功/警告/危险等语义色
- 字体令牌:定义字体族与多级排版样式(标题、正文、说明、脚注等),统一在组件中引用
标签页容器与状态指示器
- 顶部状态栏:根据连接状态映射为不同视觉状态(已连接、连接中、警告、错误、离线),并以色块与点标示
- 底部导航:Tab 切换时高亮当前项,结合 IME 显示隐藏规则优化输入体验
- 内容区域:按需渲染各功能页,如屏幕页支持"恢复仪表盘"提示
绿色系"] B --> |连接中/重连| D["Connecting
蓝色系"] B --> |配对/授权| E["Warning
橙色系"] B --> |错误/失败| F["Error
红色系"] B --> |默认| G["Offline
灰阶"]
聊天界面
- 会话选择:横向滚动展示历史会话,当前会话高亮
- 错误提示:错误文本出现时以警示色块显示
- 消息列表:支持文本、Markdown、图片、工具调用、流式助手等多类型渲染
- 输入区:支持文本输入、图片附件、思考级别选择、刷新/中止、发送按钮与禁用态
设置页面
- 设备信息:实例 ID、设备型号、版本号
- 权限管理:麦克风、相机、短信、通知、照片、联系人、日历、运动、位置(含精确位置)
- 位置模式:Off/WhileUsing/Precise Location 三态控制
- 屏幕常亮:防止休眠开关
- 交互流程:权限请求通过 ActivityResultLauncher 触发,状态变更通过 ViewModel 同步
数据流与状态管理
- ViewModel 暴露 StateFlow,屏幕与组件通过 collectAsState 订阅
- 聊天:消息列表、错误、健康状态、思考级别、流式助手文本、待执行工具调用、会话列表
- 连接:网关发现状态、连接状态、远端地址、服务器名
- 语音/屏幕/节点:相机、Canvas 控制、前台服务等
依赖关系分析
- 组件耦合:屏幕层仅依赖 ViewModel 的 StateFlow,功能组件通过回调与 ViewModel 解耦
- 主题与样式:所有 UI 组件统一引用 MobileUiTokens 中的颜色与排版,避免硬编码
- 权限与系统服务:设置页通过 ActivityResultLauncher 与系统权限对话交互,避免直接持有上下文引用
性能考量
- 布局折叠:底部导航在 IME 弹出时自动隐藏,减少重绘范围
- 滚动优化:消息列表与会话选择使用水平滚动与懒加载容器,限制一次性渲染量
- 图片处理:图片附件在 IO 线程解码与 Base64 编码,完成后在主线程更新 UI
- 状态订阅:仅在必要作用域内订阅 StateFlow,避免过度重组
- 主题与样式:集中定义样式令牌,减少重复计算与资源分配
权限与安全
Android 应用位于 apps/android/app,核心安全与权限相关文件分布如下:
- 权限请求:PermissionRequester.kt
- 安全偏好:SecurePrefs.kt
- 清单与服务:AndroidManifest.xml
- 构建与依赖:build.gradle.kts
- 网络与备份配置:network_security_config.xml、backup_rules.xml
- TLS 固定:GatewayTls.kt
- 设备权限状态上报:DeviceHandler.kt
- 设置界面权限状态展示:SettingsSheet.kt
- 安全偏好测试:SecurePrefsTest.kt
权限请求器"] B["SecurePrefs
安全偏好设置"] C["AndroidManifest
权限与服务声明"] D["build.gradle.kts
构建与依赖"] E["network_security_config.xml
网络安全策略"] F["backup_rules.xml
备份规则"] G["GatewayTls
TLS 固定"] H["DeviceHandler
设备权限状态上报"] I["SettingsSheet
设置界面权限状态"] J["SecurePrefsTest
安全偏好测试"] end A --> C B --> C G --> C H --> C I --> C D --> C E --> C F --> C J --> B
核心组件
- 权限请求器(PermissionRequester):封装多权限一次性请求、理由说明对话框、超时控制与设置引导。
- 安全偏好(SecurePrefs):基于 EncryptedSharedPreferences 的敏感数据加密存储,同时维护明文偏好用于非敏感配置;支持实例 ID、网关凭据、唤醒词等。
- 清单与服务(AndroidManifest):声明网络、定位、相机、麦克风、通知、短信、媒体访问、日历联系人等权限;注册前台服务、通知监听服务、FileProvider。
- 网络安全策略(network_security_config.xml):允许本地与特定域名的清晰文本流量,适配受信尾随网络场景。
- 备份规则(backup_rules.xml):启用全量文件备份。
- TLS 固定(GatewayTls):自定义 TrustManager 实现证书指纹校验,支持首次信任(TOFU)与持久化存储。
- 设备权限状态上报(DeviceHandler):汇总设备权限状态并以 JSON 形式上报。
- 设置界面权限状态展示(SettingsSheet):在设置页动态检查并显示各类权限状态。
架构总览
下图展示了从"权限请求"到"安全存储"再到"网络通信"的整体链路,以及与清单和服务的关系。
详细组件分析
权限请求器(PermissionRequester)
- 功能要点
- 批量检测缺失权限,必要时弹出理由对话框,尊重用户选择。
- 使用 ActivityResultLauncher 触发系统授权对话框,支持超时控制。
- 合并当前授权状态与回调结果,对被拒绝且无理由的权限引导至系统设置。
- 关键行为
- 检测逻辑:仅对未授予的权限发起请求。
- 超时与并发:通过互斥锁与超时机制避免阻塞与竞态。
- 结果合并:若某权限在回调前已被授予,视为已授权。
- 用户体验
- 对相机、麦克风、短信等权限提供明确标签提示。
- 对不可恢复权限(无理由)引导至应用详情页设置。
安全偏好设置(SecurePrefs)
- 数据隔离
- 明文偏好:存放非敏感配置(如实例 ID、显示名、位置模式、唤醒词等)。
- 加密偏好:存放敏感信息(如网关令牌、密码),使用 EncryptedSharedPreferences 与 AES256-GCM。
- 主密钥与加密方案
- 使用 MasterKey.AES256_GCM 作为主密钥,Key/Value 分别采用 AES256_SIV 与 AES256_GCM。
- 生命周期与状态流
- 大部分字段以 StateFlow 暴露,便于 UI 订阅。
- 迁移与兼容
- 对历史键值进行迁移(例如位置模式的"always"迁移到新枚举值)。
- 测试覆盖
- 单元测试验证迁移逻辑正确性。
Android 清单与权限配置(AndroidManifest)
- 权限声明
- 网络与前台服务:INTERNET、ACCESS_NETWORK_STATE、FOREGROUND_SERVICE、FOREGROUND_SERVICE_DATA_SYNC。
- 通知与 Wi‑Fi:POST_NOTIFICATIONS、NEARBLY_WIFI_DEVICES(含 flags)、ACCESS_FINE_LOCATION、ACCESS_COARSE_LOCATION。
- 媒体与存储:CAMERA、RECORD_AUDIO、SEND_SMS、READ_MEDIA_IMAGES、READ_MEDIA_VISUAL_USER_SELECTED、READ_EXTERNAL_STORAGE(maxSdk=32)。
- 联系人与日历:READ_CONTACTS、WRITE_CONTACTS、READ_CALENDAR、WRITE_CALENDAR。
- 行为识别:ACTIVITY_RECOGNITION。
- 特性声明
- 摄像头与电话硬件为可选(required=false)。
- 服务与 Provider
- 前台服务、通知监听服务、FileProvider(用于分享文件)。
网络安全策略与备份规则
- 网络安全配置
- 允许基础清晰文本流量,针对 openclaw.local 与 ts.net 子域开放 HTTP。
- 适用于受信尾随网络环境,降低本地开发与内网场景的 TLS 成本。
- 备份规则
- 启用全量文件备份,注意敏感数据应仅存于加密偏好中。
TLS 固定与安全通信
- 目标
- 在客户端侧对远端网关证书进行指纹校验,防止中间人攻击。
- 实现
- 自定义 TrustManager:当提供期望指纹时严格比对;否则回退到默认信任策略。
- 支持首次信任(TOFU):若允许且首次成功握手,则将指纹持久化。
- 提供 HostnameVerifier 与 SSLSocketFactory,便于 OkHttp 或原生 HTTPS 使用。
- 使用建议
- 对外网与非受信网络强制启用 TLS 与指纹校验;对本地环回地址可放宽策略。
设备权限状态上报与设置页展示
- 设备权限状态上报
- DeviceHandler 汇总相机、麦克风、位置、照片、联系人、日历、运动、通知等权限状态,生成 JSON。
- 设置页展示
- SettingsSheet 在 onResume 时检查各权限状态,更新 UI 展示。
依赖关系分析
- 构建与依赖
- Compose、OkHttp、安全加密库(androidx.security:security-crypto)、相机库等。
- 测试框架(Robolectric、Kotest、MockWebServer)。
- 运行时依赖
- 权限请求依赖 ActivityResultContracts 与 ActivityCompat。
- 安全存储依赖 EncryptedSharedPreferences 与 MasterKey。
- TLS 固定依赖 javax.net.ssl 与系统信任管理器。
性能考量
- 权限请求
- 使用互斥锁避免并发重复请求;超时控制防止 UI 卡死。
- 合并当前状态与回调结果,减少后续判断成本。
- 安全存储
- EncryptedSharedPreferences 会带来一定开销,建议批量写入与合理缓存。
- 对频繁读取的键值可引入内存缓存(当前实现以 StateFlow 为主)。
- TLS 固定
- 证书指纹计算与持久化需在后台线程执行,避免阻塞主线程。
- 首次握手失败重试与超时控制,提升稳定性。
设备控制
Android 节点应用位于 apps/android/app,核心控制逻辑集中在 node 包中;网关侧策略与测试位于 src/gateway。
命令分发器"] DH["DeviceHandler
设备信息/权限/健康"] CH["CameraHandler
相机列表/拍照/录像"] CNH["ContactsHandler
联系人搜索/新增"] NH["NotificationsHandler
通知快照/动作"] SH["SmsHandler
短信发送"] LH["LocationHandler
位置获取"] AH["A2UIHandler
A2UI 就绪/消息应用"] end subgraph "网关" POL["node-command-policy.ts
命令白名单/平台默认值"] TEST["android-node.capabilities.live.test.ts
能力验证测试"] DISC["GatewayDiscovery.kt
mDNS/NSD 发现"] PAIR["message-handler.ts
配对握手/拒绝"] PNOTI["device-pair/notify.ts
配对提醒轮询"] end ID --> DH ID --> CH ID --> CNH ID --> NH ID --> SH ID --> LH ID --> AH POL --> ID TEST --> ID DISC --> ID PAIR --> ID PNOTI --> ID
核心组件
- 命令分发器(InvokeDispatcher)
- 统一入口,根据命令名路由到对应处理器,并进行前台限制、能力可用性检查与错误包装
- 支持 Canvas/A2UI、相机、位置、设备、通知、系统、相册、联系人、日历、运动、短信等命令族
- 各子处理器
- DeviceHandler:设备状态、信息、权限、健康度
- CameraHandler:相机设备枚举、拍照、录视频(含大小限制与错误处理)
- ContactsHandler:联系人搜索、新增(含权限校验与批量插入)
- NotificationsHandler:通知快照、动作执行(打开/忽略/回复)
- SmsHandler:短信发送(错误码映射)
- LocationHandler:位置获取(权限、精度、超时)
- A2UIHandler:A2UI 主机解析、就绪检测、消息应用
- 网关侧策略
- node-command-policy.ts:按平台定义默认命令集、危险命令白名单、节点声明校验
- 文档与测试
- 平台文档:Android 节点连接、命令面与注意事项
- 能力测试:覆盖 Canvas、相机、位置、设备、通知、调试等命令
架构总览
Android 节点通过 mDNS/NSD 发现网关,建立 WebSocket 连接并完成配对。命令由网关侧策略决定是否允许,节点侧通过 InvokeDispatcher 分发至各 Handler 执行,结果回传给网关。
mDNS/NSD" participant GW as "Gateway" participant Policy as "node-command-policy.ts" participant Disp as "InvokeDispatcher" participant H as "各 Handler" Node->>Disc : 发现 _openclaw-gw._tcp Disc-->>Node : 返回网关地址/端口 Node->>GW : 建立 WebSocket 连接 GW-->>Node : 握手/配对请求 alt 未配对且非静默 GW-->>Node : 拒绝连接并提示配对 else 已配对或静默 GW-->>Node : 允许连接 end Node->>GW : node.invoke(command, params) GW->>Policy : 校验命令是否允许 Policy-->>GW : 允许/拒绝 GW->>Disp : 调用分发器 Disp->>H : 路由到具体处理器 H-->>Disp : 执行结果/错误 Disp-->>GW : 包装响应 GW-->>Node : 返回 payload 或错误
详细组件分析
命令分发与注册机制(NodeHandler 系统)
- 命令注册
- 通过 InvokeCommandRegistry 查找命令规格,包含命令名、是否要求前台、可用性条件
- 可用性条件支持:Always、CameraEnabled、LocationEnabled、SmsAvailable、MotionActivityAvailable、MotionPedometerAvailable、DebugBuild
- 分发流程
- 若命令未知,返回 INVALID_REQUEST
- 若命令要求前台但节点不在前台,返回 NODE_BACKGROUND_UNAVAILABLE
- 根据可用性条件检查(如相机/位置/短信/运动),不满足则返回相应错误码
- 路由到具体处理器执行,A2UI 需要先确保 Canvas 可用与 A2UI 主机就绪
- 错误处理
- 统一包装为 GatewaySession.InvokeResult,包含 ok、payload 或 error(code/message)
设备权限与状态(DeviceHandler)
- 设备状态:电池、热状态、存储、网络连通性、耗电模式、uptime
- 设备信息:设备名、型号标识、系统版本、应用版本/构建号、语言区域
- 权限状态:相机、麦克风、位置、短信、通知监听、通知、相册、通讯录、日历、运动
- 健康度:内存压力、电池状态/充电类型、温度、电流、Doze/LowPower 模式、安全补丁级别
相机控制(CameraHandler)
- 列表:列举可用相机设备
- 拍照:触发闪光、HUD 提示、捕获并返回 base64 图像
- 录像:可选包含外部音频,限制最大负载,过大则删除临时文件并返回 PAYLOAD_TOO_LARGE
- 错误处理:将异常映射为错误码与用户可读消息
联系人管理(ContactsHandler)
- 搜索:支持查询关键字与数量上限,返回联系人列表
- 新增:支持姓名、组织、电话、邮箱,通过批量插入写入系统通讯录
- 权限:读取需 READ_CONTACTS,写入需 WRITE_CONTACTS;无权限直接返回 CONTACTS_PERMISSION_REQUIRED
通知处理(NotificationsHandler)
- 快照:读取通知快照(若启用监听但未连接,尝试重新绑定)
- 动作:支持 open、dismiss、reply,reply 需要 replyText
- 失败:返回 UNAVAILABLE 或具体错误码
短信发送(SmsHandler)
- 参数解析后委托底层 SmsManager 执行
- 错误映射:将内部错误字符串按冒号前缀提取为错误码,返回 SMS_SEND_FAILED 默认码
位置服务(LocationHandler)
- 权限:需要粗/精定位之一;前台运行时才允许
- 精度:precise/coarse/balanced,受精确权限与系统设置影响
- 超时:默认 10 秒,范围 1--60 秒
- 异常:超时返回 LOCATION_TIMEOUT,其他异常返回 LOCATION_UNAVAILABLE
A2UI 交互(A2UIHandler)
- 主机解析:优先节点 Canvas 主机,否则回退到运营者主机
- 就绪检测:导航到 A2UI 页面并轮询检查 ready 标志
- 消息应用:支持 messages 数组或 jsonl 字段,严格校验 v0.8 消息格式
网关命令策略与平台默认
- 平台默认命令集:Android 默认开放 Canvas、Camera、Location、通知、系统通知、设备信息/状态/权限/健康、联系人、日历、提醒、相册、运动等命令
- 危险命令:相机拍照/录像、屏幕录制、联系人新增、日历新增、提醒新增、短信发送等需显式允许
- 声明校验:命令必须同时在节点声明的 commands 列表中
设备发现、蓝牙配对与网络通信
- 设备发现:Android 使用 mDNS/NSD 发现 _openclaw-gw._tcp,支持本地与单播 DNS-SD(跨网络场景)
- 蓝牙配对:通过网关握手阶段触发配对请求,未配对且非静默时拒绝连接并提示
- 网络通信:WebSocket 连接,支持 Token/密码认证与 TLS
依赖关系分析
- 节点侧
- InvokeDispatcher 依赖各 Handler 与 A2UIHandler,受 InvokeCommandRegistry 的规格约束
- 各 Handler 依赖系统服务(相机、联系人、通知、位置、短信等)
- 网关侧
- node-command-policy.ts 决定命令允许与否,结合节点声明与配置
- 测试用例覆盖 Android 节点能力矩阵,验证命令返回结构与错误码
性能考量
- 相机录视频负载限制:超过阈值会删除临时文件并返回错误,避免大包导致传输失败
- 前台限制:Canvas/A2UI/相机/录屏等命令仅在前台可用,减少后台资源占用
- 通知监听:若监听未连接,自动尝试重绑,降低用户干预成本
- 网络发现:本地与单播 DNS-SD 双通道,提升跨网络发现成功率
网关通信
Android 网关通信相关代码主要位于 Android 应用模块中,核心类为 GatewaySession 与 DeviceAuthPayload;同时在后端/通用层提供协议定义与校验工具。
会话管理/连接/消息处理"] DAP["DeviceAuthPayload.kt
认证载荷构建/归一化"] end subgraph "通用协议" PI["protocol/index.ts
帧校验/常量导出"] SCHEMA["protocol/schema.ts
协议模式入口"] end GS --> DAP GS --> PI PI --> SCHEMA
核心组件
- GatewaySession(Android)
- 负责 WebSocket 连接、消息收发、RPC 请求/响应、事件分发、节点调用请求处理、TLS 配置与指纹回调、Canvas URL 规范化等
- 内部 Connection 类封装单次连接细节,包括握手、认证、心跳与断线处理
- DeviceAuthPayload(Android)
- 构建 v3 设备认证载荷字符串,统一元数据字段大小写规则,便于跨运行时一致性校验
- 协议与校验(通用)
- 提供帧类型、参数 Schema、AJV 校验器、协议版本常量与错误码导出
架构总览
Android 端通过 OkHttp WebSocket 客户端发起连接,按"握手挑战---连接参数---认证---会话建立"的顺序完成握手;随后进入消息循环,区分"响应帧"和"事件帧",并支持节点侧向客户端发起的"invoke 请求"。
组件详解
GatewaySession 会话管理
- 连接与生命周期
- 支持 connect/disconnect/reconnect,内部使用协程与互斥锁保证并发安全
- 运行循环按指数回退策略重连,最大延迟上限控制
- 消息处理
- onMessage 解析 JSON 帧,区分 "res"(响应)与 "event"(事件)
- pending 映射用于匹配请求 ID 与响应
- 认证与握手
- 等待 connect.challenge 事件提取 nonce,随后发送 connect RPC
- 可选携带 token/password 与设备签名(公钥、签名、时间戳、nonce)
- 节点调用
- 接收 "node.invoke.request" 事件,调用应用提供的处理器,再以 "node.invoke.result" 回_ack_
- Canvas URL 规范化
- 根据连接是否 TLS 以及返回的 canvasHostUrl,修正 scheme/port/path/query/fragment
DeviceAuthPayload 认证机制
- 载荷格式
- v3 版本字符串由固定字段拼接,字段包括版本、设备 ID、客户端 ID、模式、角色、作用域列表、签名时间、令牌、nonce、平台、设备系列
- 平台与设备系列字段进行大小写归一化(仅 ASCII A-Z 转小写),确保跨运行时一致性
- 使用场景
- 在 connect 参数中生成设备签名,随同公钥一起提交给网关,由网关验证签名与时间戳
协议常量与校验(通用)
- 协议版本
- 通过 schema 导入统一导出协议版本常量,客户端在 connect 参数中声明 min/maxProtocol
- 帧与参数校验
- 使用 AJV 编译各 Schema,提供 validateXxx 函数用于请求/响应/事件/参数的运行时校验
- formatValidationErrors 将校验错误格式化为可读字符串
- 错误码与帧类型
- 导出 ErrorCodes、GatewayFrame、RequestFrame、ResponseFrame、EventFrame 等类型与校验器
心跳检测与保活
- 服务端心跳
- 网关定期下发 "tick" 事件,客户端收到后更新最近心跳时间
- 客户端保活
- OkHttp 客户端设置 pingInterval,默认 30 秒;若长时间无消息,可结合业务层 watchdog 强制重连(参考其他平台实现)
消息序列化与反序列化
- 发送
- request 方法构造 "req" 帧,序列化为 JSON 后通过 WebSocket 发送
- 接收
- onMessage 解析文本为 JSON 对象,根据 "type" 分派到 handleResponse 或 handleEvent
- 响应帧通过 pending 映射匹配请求 ID,超时抛出异常
- 节点调用结果
- invoke 结果以 "node.invoke.result" RPC 返回,支持 payload 与 error 字段
连接配置与 TLS
- TLS 配置
- 可选 SSL Socket Factory 与 Hostname Verifier,支持自定义证书指纹校验回调
- Ping 与超时
- 写超时 60 秒,读超时无限制,ping 间隔 30 秒
- Canvas URL 规范化
- 根据连接是否 TLS 修正 https/scheme 与端口,保留路径/查询/片段
节点调用(GatewaySessionInvoke)
- 事件到 RPC 的桥接
- 当收到 "node.invoke.request" 事件时,解析参数(id/nodeId/command/paramsJSON/timeoutMs),调用应用提供的处理器
- 处理完成后以 "node.invoke.result" 发送结果,超时范围在 15--120 秒之间
- 测试场景
- 单测覆盖握手、事件分发、结果回传与关闭流程
依赖关系分析
- Android 端
- GatewaySession 依赖 OkHttp WebSocket、Kotlinx Serialization、协程与互斥锁
- DeviceAuthPayload 作为纯函数对象,被 Connection 构造 connect 参数时调用
- 通用层
- protocol/index.ts 导出 AJV 校验器与协议常量,供服务端/客户端共享
- schema.ts 汇总各类 Schema,形成强类型协议模型
性能与可靠性
- 指数回退重连
- 连接失败时按 1.7 指数增长延迟,上限 8 秒,避免风暴式重试
- 超时与确认
- connect RPC 超时 12 秒,invoke 结果确认超时在 15--120 秒区间
- 心跳与保活
- OkHttp ping 间隔 30 秒;业务层可结合 watchdog 在长时间无消息时触发重连
- TLS 与指纹
- 可配置自定义证书链与主机名校验,并在连接建立时回调指纹,便于运维审计
相机与屏幕录制
Android 相关实现集中在 apps/android/app 模块,核心文件包括:
- 节点命令处理器:CameraHandler、PhotosHandler
- 相机采集与录制:CameraCaptureManager
- 状态与权限:CameraHudState、PermissionRequester
- 辅助工具:JpegSizeLimiter
- 文档参考:docs/platforms/android.md
节点命令入口"] CCM["CameraCaptureManager
相机采集/录制"] PH["PhotosHandler
相册读取"] PR["PermissionRequester
权限申请"] HLS["CameraHudState
HUD 状态模型"] JSL["JpegSizeLimiter
JPEG 尺寸限制器"] end CH --> CCM CH --> HLS CH --> PR PH --> PR CCM --> PR CCM --> JSL
核心组件
- CameraHudState:定义相机 HUD 的状态类型(拍照、录制、成功、错误),用于 UI 提示
- CameraHandler:节点命令入口,负责 camera.list、camera.snap、camera.clip 的调用与结果封装
- CameraCaptureManager:实际执行相机操作,含权限检查、设备选择、拍照与录制、文件输出与事件监听
- PhotosHandler:读取系统相册最新图片,按预算压缩为 JPEG 并返回 base64
- PermissionRequester:统一的权限申请与引导,支持理由说明与设置页跳转
- JpegSizeLimiter:在尺寸与质量之间迭代压缩,确保输出不超过上限
架构总览
下图展示了从节点命令到相机采集与录制的整体流程,以及与权限系统的交互。
组件详解
相机状态管理:CameraHudState
- 定义状态类型:Photo(拍照)、Recording(录制中)、Success(成功)、Error(错误)
- 数据结构包含 token、kind、message,用于 UI 层渲染与自动隐藏
相机处理器:CameraHandler
职责与流程:
- 设备列表:调用 CameraCaptureManager.listDevices,返回设备数组
- 拍照:显示 HUD、触发闪光、调用 snap,返回 base64 JPEG;失败时显示错误 HUD
- 录制:显示 HUD、可选启用外部音频、调用 clip,将 mp4 文件读入内存并 base64 编码返回;超过阈值则删除临时文件并报错
关键行为:
- 参数解析:includeAudio、durationMs、deviceId、facing、quality、maxWidth
- 负载限制:对 clip 的二进制大小进行上限检查,避免 WebSocket 超限
- HUD 生命周期:成功/错误提示与自动隐藏时间
相机采集与录制:CameraCaptureManager
职责与流程:
- 设备枚举:通过 ProcessCameraProvider 获取可用摄像头并映射为设备信息
- 权限保障:ensureCameraPermission / ensureMicPermission
- 拍照:
- 解析参数:facing、quality、maxWidth、deviceId
- 绑定生命周期并拍摄 JPEG,读取 EXIF 方向并旋转,按 maxWidth 缩放
- 使用 JpegSizeLimiter 控制 JPEG 大小,返回 JSON 字符串
- 录制:
- 解析参数:facing、durationMs、includeAudio、deviceId
- 设置最低质量以减小文件体积
- 绑定 Preview 与 VideoCapture,预热后开始录制,延时后停止
- 监听 Finalize 事件,超时或失败时清理临时文件并抛出异常
- 返回 File 与元数据(时长、是否含音频)
辅助工具:
- takeJpegWithExif:异步拍摄 JPEG 并返回字节与 EXIF 方向
- cameraDeviceInfoOrNull / cameraIdOrNull:从 CameraInfo 解析设备信息
- JpegSizeLimiter:在尺寸与质量间迭代压缩,确保不超过上限
相册处理:PhotosHandler
职责与流程:
- 权限检查:根据系统版本选择 READ_MEDIA_IMAGES 或 READ_EXTERNAL_STORAGE
- 查询策略:按拍摄时间与添加时间降序查询最近图片
- 解码与缩放:按最大宽度计算 inSampleSize 并解码,必要时缩放
- 压缩与预算:使用预算约束编码 JPEG,逐张评估 base64 长度,累计不超过总预算
- 返回结构:每张图片包含 format、base64、width、height、createdAt
权限管理:PermissionRequester
- 支持多权限一次性申请
- 对需要理由的权限弹窗说明用途
- 对被拒绝且不再提示的权限,引导用户前往应用设置开启
- 内部互斥与超时控制,保证并发安全
JPEG 尺寸限制器:JpegSizeLimiter
- 输入:初始宽高、起始质量、最大字节数
- 策略:优先降低质量,再逐步缩小尺寸,直到满足上限
- 输出:最终字节、宽高、质量
屏幕录制(跨平台对比)
- iOS 实现要点:
- 计算配置:时长、帧率、音频开关、输出路径
- 启动/停止/写入:分阶段回调,准备写入器、处理视频样本、完成写入
- FPS 采样间隔:按目标帧率去抖,避免过量写入
- macOS 实现要点:
- 可选音频输入配置(AAC)
- 流停止错误记录与日志
依赖关系分析
- CameraHandler 依赖 CameraCaptureManager、PermissionRequester、CameraHudState
- CameraCaptureManager 依赖 CameraX(ProcessCameraProvider、ImageCapture、VideoCapture)、ExifInterface、JpegSizeLimiter
- PhotosHandler 依赖系统媒体存储 ContentResolver、Bitmap 解码与压缩
- PermissionRequester 依赖 Android ActivityResultLauncher 与系统设置页面
性能考量
- 拍照路径
- 预热与方向:先旋转再缩放,减少重复变换开销
- 压缩预算:JPEG 压缩与尺寸缩放双管齐下,确保 payload 不超限
- 录制路径
- 最低质量:优先降低质量而非分辨率,兼顾体积与清晰度
- 预览绑定:强制绑定 Preview 以激活编码管线,避免无有效数据
- 超时与清理:录制完成后等待 Finalize,超时或失败及时删除临时文件
- 相册读取
- inSampleSize 估算与按预算编码,避免一次性加载过大位图
- 累计预算控制,保证多图返回不越界
应用架构
Android 应用位于 apps/android/app 模块,采用 Kotlin + Jetpack Compose UI + OkHttp WebSocket 通信的现代 Android 技术栈。核心目录与职责概览:
- app/src/main/java/ai/openclaw/app
- 入口与生命周期:NodeApp、MainActivity、NodeForegroundService
- 状态与视图模型:MainViewModel
- 核心运行时:NodeRuntime 及其子系统(网关会话、Canvas、相机、语音等)
- UI 层:RootScreen 与各功能页(Compose)
- 配置与清单:AndroidManifest.xml、build.gradle.kts
- app/src/main/res:资源与主题
- 测试:app/src/test/java 下按功能域分层测试
Application"] MA["MainActivity
ComponentActivity"] VM["MainViewModel
AndroidViewModel"] NS["NodeForegroundService
Service"] end subgraph "运行时核心" NR["NodeRuntime
核心编排"] GS["GatewaySession
操作端/节点端"] CC["CanvasController
WebView 承载"] CM["CameraCaptureManager
CameraX 封装"] end NA --> NR MA --> VM VM --> NR MA --> NS NR --> GS NR --> CC NR --> CM
核心组件
- NodeApp:应用入口,负责严格模式调试与延迟初始化 NodeRuntime 单例
- MainActivity:承载 Compose UI,绑定 MainViewModel,处理权限与系统窗口标志,延后启动前台服务
- MainViewModel:将 NodeRuntime 的大量 StateFlow 暴露给 UI,并转发用户设置与连接控制命令
- NodeRuntime:应用核心运行时,聚合网关会话、Canvas、相机、位置、短信、语音等子系统,统一状态与事件分发
- NodeForegroundService:前台服务,根据 NodeRuntime 状态流动态更新通知
- GatewaySession:封装 WebSocket 连接、鉴权、RPC 请求/响应、事件分发与自动重连
- CanvasController:WebView 容器与调试状态注入,提供快照与脚本执行能力
- CameraCaptureManager:基于 CameraX 的拍照/视频录制封装,含权限与 EXIF 方向处理
架构总览
应用采用"单例运行时 + 响应式状态流"的架构模式:
- NodeApp.lazy 持有 NodeRuntime,避免冷启动路径冗余
- NodeRuntime 使用协程与 StateFlow 组织多源状态(连接、Canvas、相机、语音、聊天等),并通过 GatewaySession 与远端网关保持双向通信
- MainActivity 仅承担 UI 与生命周期职责,通过 MainViewModel 访问运行时能力
- NodeForegroundService 以前台服务形式常驻,实时反映连接状态与麦克风监听状态
详细组件分析
启动流程与生命周期
- 应用启动
- AndroidManifest 指定 Application 为 NodeApp,Activity 为 MainActivity
- NodeApp 在 onCreate 中启用严格模式(调试构建)并延迟初始化 NodeRuntime
- MainActivity 生命周期
- onCreate:禁用系统窗口装饰适配、初始化 PermissionRequester、绑定相机/短信权限请求器、设置 UI 主题与根屏幕、在首帧后延时启动 NodeForegroundService
- onStart/onStop:切换前台状态,影响 NodeRuntime 内部的语音会话与外部音频捕获标记
- 前台服务
- NodeForegroundService 在 onCreate 中创建通知通道并首次显示"正在启动"通知
- 通过组合 NodeRuntime 的多个状态流,动态更新标题与内容;支持从通知触发断开连接
启用严格模式"] AppInit --> RuntimeInit["NodeApp.runtime.lazy 初始化"] RuntimeInit --> ActivityInit["MainActivity.onCreate()"] ActivityInit --> BindPerms["绑定权限请求器"] ActivityInit --> SetUI["设置主题与根屏幕"] ActivityInit --> DelayStart["首帧后延时启动前台服务"] ActivityInit --> Foreground["NodeForegroundService.start()"] Foreground --> CombineState["合并状态流
连接/服务器/麦克风"] CombineState --> UpdateNotify["更新通知"] ActivityInit --> OnStart["onStart() 设置前台=true"] ActivityInit --> OnStop["onStop() 设置前台=false"]
状态管理与 MainViewModel
- MainViewModel 作为 AndroidViewModel,持有 NodeApp.runtime 并直接暴露 NodeRuntime 的大量 StateFlow(连接状态、Canvas 状态、相机/位置/麦克风/扬声器、聊天状态等)
- 提供 setter 方法将用户设置与连接控制命令转发至 NodeRuntime,实现 UI 对运行时的可控访问
- 通过 viewModels() 在 MainActivity 中获取实例,确保进程内共享与生命周期感知
NodeRuntime:运行时核心
- 协程与作用域:使用 SupervisorJob + Dispatchers.IO 管理子系统任务,保证异常不扩散
- 子系统聚合:Canvas、相机、位置、短信、通知、系统、照片、联系人、日历、运动、A2UI、Invoke 分发器等
- 网关会话:维护两个 GatewaySession(操作端与节点端),分别处理连接、断开、事件与 RPC 请求
- 状态流:对外暴露大量只读 StateFlow,内部通过 MutableStateFlow 维护可变状态并进行去抖与合并
- 自动连接:依据偏好设置与发现列表,自动连接可信网关(基于存储的 TLS 指纹)
- Canvas A2UI:支持从 WebView 触发 agent.request 并回传状态反馈
UI 与导航
- RootScreen:根据 onboardingCompleted 决定展示引导流程或主标签页
- MainActivity:设置系统窗口装饰、权限请求器、绑定相机/短信权限、根据生命周期控制"防休眠"标志位、渲染主题与根屏幕
- NodeForegroundService:根据运行时状态流动态更新通知,支持从通知断开连接
网络与安全
- GatewaySession 使用 OkHttp WebSocket 实现连接、鉴权、RPC 请求与事件分发
- 支持 TLS 参数解析与指纹校验,首次连接时捕获指纹并提示用户验证,随后持久化到偏好中
- 自动重连策略随失败次数指数退避,上限保护
签名/公钥/角色/权限"] Auth --> Send["发送 connect 请求"] Send --> Resp{"响应 ok?"} Resp -- 是 --> Ready["建立会话
保存 canvasHostUrl/mainSessionKey"] Resp -- 否 --> Fail["抛出错误并断开"] Ready --> Event["事件/请求分发"] Event --> Retry["异常/断开后按指数退避重连"]
设备能力与媒体
- CanvasController:WebView 容器,支持导航、调试状态注入、JS 评估与图片快照(PNG/JPEG)
- CameraCaptureManager:基于 CameraX 的拍照与视频录制,含 EXIF 方向旋转、质量压缩、最大尺寸限制与权限检查
依赖关系分析
- 模块耦合
- NodeApp 与 NodeRuntime:单例持有,低耦合高内聚
- MainActivity 与 MainViewModel:通过 ViewModelProvider 解耦,UI 不直接依赖运行时
- NodeRuntime 与子系统:通过组合模式聚合,职责清晰
- 外部依赖
- Jetpack Compose、Material3、Navigation
- OkHttp WebSocket、CameraX、Kotlinx Serialization、Kotlinx Coroutines
- BouncyCastle、CommonMark 等
性能考量
- 启动路径优化:MainActivity 在首帧后才启动前台服务,减少冷启动阻塞
- 状态流去抖与合并:NodeRuntime 使用 combine + distinctUntilChanged 控制 UI 更新频率
- 图片快照与压缩:CanvasController 与 CameraCaptureManager 对图片进行缩放与质量压缩,避免超大负载
- 协程调度:IO 调度器用于网络与磁盘 IO,Main 调度器用于 UI 相关操作
- 通知更新:NodeForegroundService 仅在状态变化时更新通知,降低系统开销
语音功能
Android 语音相关代码主要位于应用模块的 voice 包与根级配置类中,配合 UI 层的引导与权限请求,形成完整的语音工作流。
唤醒模式枚举"] B["VoiceWakeManager
唤醒监听器"] C["WakeWords
唤醒词工具"] D["SecurePrefs
安全偏好存储"] E["VoiceWakeCommandExtractor
命令提取器"] F["TalkDirectiveParser
指令解析器"] G["TalkModeManager
Talk 模式管理器"] H["ElevenLabsStreamingTts
流式 TTS"] I["OnboardingFlow
引导与权限"] J["VoiceTabScreen
语音标签页 UI"] end A --> D C --> D D --> B D --> G B --> E G --> F G --> H I --> J J --> G
核心组件
- 语音唤醒模式:VoiceWakeMode 定义 off/foreground/always 三种模式,并提供从原始字符串解析的方法。
- 唤醒词管理:WakeWords 提供解析、变更检测与清洗逻辑;SecurePrefs 负责持久化存储与默认值。
- 唤醒监听:VoiceWakeManager 使用 Android SpeechRecognizer 进行持续监听,结合 VoiceWakeCommandExtractor 提取触发后的命令。
- Talk 指令解析:TalkDirectiveParser 解析首行 JSON 指令,支持多键名别名映射,剥离后返回纯文本与未知键列表。
- Talk 模式:TalkModeManager 实现录音、转写、对话、TTS 播放与中断控制,支持 ElevenLabs 流式 TTS 与系统 TTS 双通道。
- 流式 TTS:ElevenLabsStreamingTts 通过 WebSocket 接收实时音频,AudioTrack/PCM 或 MediaPlayer 播放。
架构总览
Android 语音功能采用"手动触发 + 云端转写 + 本地/云端 TTS"的混合架构。用户在语音标签页点击开始录音,TalkModeManager 启动 SpeechRecognizer,转写为文本后发送到网关,等待最终回复并进行 TTS 播放。若具备 ElevenLabs 凭证则优先使用其流式 TTS,否则回退系统 TTS。
详细组件分析
语音唤醒模式与唤醒词管理
- VoiceWakeMode:定义三种模式,提供字符串到枚举的解析,默认值为前台模式。
- WakeWords:限制最大数量与长度,支持逗号分隔解析、变更检测与清洗。
- SecurePrefs:持久化存储唤醒词列表与模式,提供默认唤醒词集合;加载时进行 JSON 解码与清洗。
语音唤醒监听与命令提取
- VoiceWakeManager:封装 SpeechRecognizer 生命周期,处理错误与重启;监听部分/最终结果,调用 VoiceWakeCommandExtractor 提取命令。
- VoiceWakeCommandExtractor:基于触发词正则匹配,提取触发词之后的自然语言命令,过滤空值与标点。
Talk 指令解析机制
- TalkDirectiveParser:解析首行 JSON 指令,支持多键名别名(如 voice_id、speakerBoost 等),记录未知键;剥离指令后返回纯文本与未知键列表。
Talk 模式:录音、转写、TTS 播放
- 录音与转写:TalkModeManager 使用 SpeechRecognizer,启用云转写与静默窗口策略,避免过早结束。
- 对话与订阅:支持 chat.subscribe 订阅事件流,缓存最终文本,减少轮询。
- TTS 播放:优先 ElevenLabs 流式 TTS(WebSocket),失败或不支持时回退到文件下载播放或系统 TTS;支持音频焦点与中断控制。
依赖关系分析
- 组件耦合
- VoiceWakeManager 依赖 WakeWords 与 VoiceWakeCommandExtractor,受 SecurePrefs 中的模式与触发词影响。
- TalkModeManager 依赖 GatewaySession、ElevenLabsStreamingTts 与系统 TTS,内部维护状态流与播放令牌。
- 外部依赖
- Android SpeechRecognizer(云转写)
- ElevenLabs API(流式 TTS)
- Android AudioManager/AudioTrack/MediaPlayer(音频播放)
性能考虑
- 转写与静默策略
- 使用云转写模型,配合静默窗口参数,提升自然停顿后的识别稳定性。
- 在播放期间可选择"说话时打断"以避免录音拾取播放音频导致的设备特定问题。
- TTS 播放路径
- 优先 WebSocket 流式 PCM,降低延迟;失败时降级为 MP3 文件下载播放,提高兼容性。
- AudioTrack 缓冲区大小与最小缓冲区计算,避免 OEM 设备(如 OxygenOS/OnePlus)的 AudioTrack 写入问题。
- 状态与资源管理
- 使用播放令牌与生成器确保并发播放不会互相干扰;及时释放 MediaPlayer/AudioTrack 与 SpeechRecognizer。
- 缓存最终聊天文本,减少历史查询开销。
Android 应用
Android 应用位于 apps/android 目录,采用 Gradle 多模块结构,核心模块为 app;测试与基准位于 benchmark、test 等目录。应用通过 Jetpack Compose 构建 UI,使用 OkHttp WebSocket 连接网关,结合 CameraX 实现相机与视频录制能力,并通过自研 NodeRuntime 统一调度各类节点命令与状态。
生命周期与权限绑定"] VM["MainViewModel
状态与行为入口"] RT["NodeRuntime
运行时与会话调度"] end subgraph "节点处理层" CAM["CameraCaptureManager
拍照/录屏"] DEVH["DeviceHandler
设备信息/健康/权限"] INV["InvokeDispatcher
命令分发"] A2UI["A2UIHandler
Canvas/A2UI"] end subgraph "网关通信层" GS["GatewaySession
WebSocket/RPC"] DISC["GatewayDiscovery
发现/信任提示"] end subgraph "系统与权限" PERM["PermissionRequester
动态权限请求"] SVC["NodeForegroundService
前台服务"] NLS["DeviceNotificationListenerService
通知监听"] end MA --> VM VM --> RT RT --> CAM RT --> DEVH RT --> INV RT --> A2UI RT --> GS RT --> DISC MA --> PERM MA --> SVC MA --> NLS
核心组件
- MainActivity:负责应用启动、权限请求器绑定、前台服务启动时机、保持屏幕常亮等。
- MainViewModel:桥接 UI 与 NodeRuntime,暴露状态流与操作方法。
- NodeRuntime:统一运行时,管理网关会话、节点命令分发、Canvas/A2UI、语音、聊天、设备能力等。
- PermissionRequester:封装动态权限请求流程,支持理由对话与设置页引导。
- CameraCaptureManager:基于 CameraX 的拍照与录屏能力,支持参数化控制与权限校验。
- DeviceHandler:提供设备状态、信息、权限与健康度查询。
- GatewaySession:基于 OkHttp 的 WebSocket 会话,负责连接、RPC 请求、事件分发与重连。
架构总览
应用采用"运行时统一调度 + 节点处理器 + 网关会话"的分层架构。NodeRuntime 将 UI 层与底层系统能力解耦,通过 InvokeDispatcher 将命令路由到具体处理器(如 CameraHandler、LocationHandler、DeviceHandler 等),并通过 GatewaySession 与网关建立长连接,实现命令调用、事件推送与 Canvas/A2UI 交互。
详细组件分析
设备控制与命令执行
- 运行时调度:NodeRuntime 初始化多个处理器(相机、位置、设备、通知、系统、照片、联系人、日历、运动、短信、A2UI、调试等),并通过 InvokeDispatcher 将命令路由至对应处理器。
- 命令分发:InvokeDispatcher 在处理前检查前置条件(如前台状态、相机启用、位置模式、短信可用性、录音权限等),并根据处理器返回结果构造响应。
- 网关会话:GatewaySession 负责 WebSocket 连接、RPC 请求、事件分发与重连策略,支持 TLS 参数与指纹校验,确保首次连接的信任提示与后续自动连接的安全性。
相机与屏幕录制
- 权限与参数:支持 CAMERA、RECORD_AUDIO(可选)权限;参数包括 facing、quality、maxWidth、durationMs、deviceId、includeAudio 等。
- 拍照:使用 CameraX ImageCapture,读取 EXIF 方向并旋转/缩放,压缩至 5MB 以内,返回 base64 JPEG。
- 录屏:使用 VideoCapture + Recorder,最低质量以减小文件体积;需预热摄像头并提供空预览以激活编码器;支持带/不带音频录制。
- 错误处理:超时与失败场景删除临时文件、释放资源并抛出明确异常。
设备命令与权限管理
- 设备状态/信息/健康:DeviceHandler 提供电池、存储、网络、内存、温度、压力等级等信息;权限状态汇总(相机、麦克风、位置、短信、通知监听、通知、相册、联系人、日历、运动等)。
- 权限请求:PermissionRequester 支持多权限批量请求、理由对话、被拒后引导至系统设置页。
- 后台限制:应用通过前台服务维持关键能力(数据同步、通知等),并在生命周期变化时调整行为(如前台切换时停止语音会话)。
网络通信与设备发现
- WebSocket 会话:GatewaySession 建立 wss/ws 连接,发送 connect 挑战、签名设备信息、接收 canvasHostUrl 与会话默认值;支持 TLS 参数与指纹校验。
- 自动连接与信任:NodeRuntime 在发现可信网关后自动连接,首次连接要求用户确认 TLS 指纹并持久化;手动连接模式要求已保存指纹。
- 事件与 RPC:支持 node.event 推送与 node.invoke.request 调用,InvokeDispatcher 将请求路由到对应处理器并回传结果。
Android 权限体系与后台服务限制
- 权限清单:应用声明 INTERNET、NETWORK_STATE、FOREGROUND_SERVICE、POST_NOTIFICATIONS、NEARBY_WIFI_DEVICES、LOCATION、CAMERA、RECORD_AUDIO、SMS、READ_MEDIA_*、READ_CONTACTS、READ_CALENDAR、ACTIVITY_RECOGNITION 等。
- 动态权限:相机、录音、短信、通知监听等在运行时请求;PermissionRequester 提供理由对话与设置页引导。
- 后台限制:应用通过前台服务维持数据同步;前台切换时停止语音会话以节省资源;最小化对电池与性能的影响。
依赖关系分析
- 构建与工具链:根级 build.gradle.kts 引入 Android 插件、ktlint、Compose、Serialization 插件;app/build.gradle.kts 配置编译目标、签名、依赖与打包规则。
- 第三方库:OkHttp、Material3、CameraX、dnsjava、BCProv、Commonmark、Kotlinx Serialization、Kotlinx Coroutines、Kotest/Robolectric 测试框架等。
- 资源与图标:mipmap、values、xml 等资源目录用于主题、备份/数据提取规则、网络配置与文件提供者。
性能考虑
- 启动路径精简:MainActivity 在首帧后才启动前台服务,降低冷启动时间。
- 拍摄与录制优化:拍照前旋转/缩放在主线程完成但耗时短;录屏使用最低质量与空预览激活编码器,缩短初始化时间。
- 资源压缩:JPEG 压缩限制在 5MB 内,避免传输与解析开销过大。
- 任务调度:使用 SupervisorJob 与 IO 线程池隔离网络与 I/O;状态流组合与去重减少 UI 重组。
- 打包与混淆:开启资源压缩与 ProGuard 规则,排除无关文件与元数据。