Matter tv-app Android Common-API 模块详解:内容应用与 Matter Agent 服务的 AIDL 跨进程通信机制
【免费下载链接】connectedhomeipMatter (formerly Project CHIP) creates more connections between more objects, simplifying development for manufacturers and increasing compatibility for consumers, guided by the Connectivity Standards Alliance.项目地址: https://gitcode.com/GitHub_Trending/co/connectedhomeip
本文围绕 Matter 项目(connectedhomeip)中 tv-app 的 Androidcommon-api模块展开,系统讲解内容应用(Content App)如何借助 AIDL 接口、集群/属性 ID 常量与 Intent 常量与 Matter Agent 服务进行跨进程交互。读完本文,你将理解setSupportedClusters与reportAttributeChange两个核心接口的语义边界、权限绑定校验机制,并能结合仓库内的示例客户端与服务端实现走通完整的动态端点注册与属性变更上报流程。
一、模块定位:common-api 是什么
Matter 的 tv-app 示例采用"平台应用(platform-app)+ 内容应用(content-app)"的双进程架构:Matter 协议栈运行在平台应用中,而具体的流媒体内容由第三方内容应用承载。common-api模块就是为二者定义契约的公共接口层。根据 模块说明文档:
The tv-app common-api module defines the interface to interact with the Matter agent service for the content apps. This module defines the AIDL interfaces, clusters and command abstractions accessible. It also defines various constants and intent field definitions that would be used by the content app while interacting with the Matter SDK.
概括来说,common-api提供四类内容:
- AIDL 接口:跨进程调用 Matter Agent 服务的抽象接口;
- 集群与命令抽象:媒体投屏相关的 Cluster/Command/Attribute ID 常量;
- Intent 常量:命令下发、属性读取等交互所需的 Action 与 extra 字段名;
- 动态端点能力:帮助内容应用动态注册端点(集群)并向 SDK 上报属性变更。
模块目录结构如下(examples/tv-app/android/App/common-api/):
common-api/ ├── README.md └── src/main/ ├── aidl/com/matter/tv/app/api/ │ ├── IMatterAppAgent.aidl # 核心 AIDL 接口 │ ├── SetSupportedClustersRequest.aidl │ └── SupportedCluster.aidl └── java/com/matter/tv/app/api/ ├── Clusters.java # Cluster/Command/Attribute ID 常量 └── MatterIntentConstants.java # Action / 权限 / extra 字段常量二、权限要求:绑定 Matter Agent 服务的前置条件
按照 README 的 "Permissions needed" 一节,内容应用要使用 Matter Agent 接口必须同时满足三个条件:
- 内容应用需查询并绑定(query and bind)一个处理
com.matter.tv.app.api.action.MatterAppAgentAction 的服务; - 宿主进程(即平台应用)必须持有
com.matter.tv.app.api.permission.SEND_DATA权限; - 内容应用(客户端)自身必须持有
com.matter.tv.app.api.permission.BIND_SERVICE_PERMISSION权限才能完成绑定。
这三个字符串并非文档中的"口头约定",它们由 MatterIntentConstants.java 精确定义:
public static final String ACTION_MATTER_COMMAND = "com.matter.tv.app.api.action.MATTER_COMMAND"; public static final String ACTION_MATTER_AGENT = "com.matter.tv.app.api.action.MatterAppAgent"; public static final String PERMISSION_MATTER_AGENT_BIND = "com.matter.tv.app.api.permission.BIND_SERVICE_PERMISSION"; public static final String PERMISSION_MATTER_AGENT = "com.matter.tv.app.api.permission.SEND_DATA";权限校验在 AIDL 文件头部注释中同样有说明(见 IMatterAppAgent.aidl):
/* * To use this interface, partners should query for and bind to a service that handles the "com.matter.tv.app.api.action.MatterAppAgent" Action. * They should verify the host process holds the "com.matter.tv.app.api.permission.SEND_DATA" permission * To bind to this service the client app itself must hold "com.matter.tv.app.api.permission.BIND_SERVICE_PERMISSION". */ interface IMatterAppAgent { ... }三、Matter App Agent 核心 AIDL 接口
Matter Agent 服务对外暴露的接口是IMatterAppAgent,包含两个方法。完整定义见 IMatterAppAgent.aidl:
interface IMatterAppAgent { boolean setSupportedClusters(in SetSupportedClustersRequest request); boolean reportAttributeChange(in int clusterId, in int attributeId); }3.1 setSupportedClusters:动态集群上报
该 API 允许合作方(内容应用)向 Matter Agent动态上报其支持的集群集合。README 与 AIDL 注释中对其语义有三点重要约束,值得逐条展开:
- 非增量式(not incremental):每次调用都必须上报应用支持的全量集群列表,而不是只上报新增部分;
- 缺省即删除:上一次调用中上报、但本次调用中遗漏的集群会被移除;
- 不影响静态集群:在应用资源中静态声明的集群不受此机制影响、不会被移除;但动态集群可以基于集群名(cluster name)覆盖并隐藏(override and hide)同名静态集群。
这一语义在服务端实现 ContentAppAgentService.java 中得到印证:服务通过Binder.getCallingUid()反查调用方包名,再从已发现的内容应用集合中定位ContentApp对象并整体替换其集群列表:
final int callingUID = Binder.getCallingUid(); final String pkg = getApplicationContext().getPackageManager().getNameForUid(callingUID); ContentApp contentApp = ContentAppDiscoveryService.getReceiverInstance().getDiscoveredContentApp(pkg); if (contentApp != null) { contentApp.setSupportedClusters(request.supportedClusters); return true; }注意服务端还有一层身份核验:它不信任调用方自报的包名,而是以 UID 为据反查,确保动态集群只能归属到真实发起调用的内容应用。
3.2 reportAttributeChange:属性变更上报
该 API 让内容应用在自身属性值发生变化后通知 SDK,参数为集群 ID 与属性 ID:
public boolean reportAttributeChange(int clusterId, int attributeId) { ... }从服务端实现可以看到其处理链路:reportAttributeChange找到调用方对应的ContentApp端点后,通过线程池异步转发给AppPlatformService.reportAttributeChange(endpointId, clusterId, attributeId)——注释中说明这样做的目的是避免内容应用在命令处理过程中同步调用时阻塞 CHIP 协议栈锁:
// Make this call async so that even if the content apps make this call during command // processing and synchronously, the command processing thread will not block for the // chip stack lock. executorService.execute(() -> { AppPlatformService.get() .reportAttributeChange(contentApp.getEndpointId(), clusterId, attributeId); });即:reportAttributeChange只是告诉平台应用"这个端点上的这个属性变了",最终由平台应用侧的 CHIP 栈完成向控制端的属性通知(Notify)分发。此外,若该内容应用尚无有效端点(endpointId == INVALID_ENDPOINTID),调用会直接失败并返回 false。
3.3 请求数据结构:SupportedCluster 与 SetSupportedClustersRequest
集群上报通过两个 parcelable 数据结构承载。SupportedCluster.aidl 定义了单个集群的完整描述:
parcelable SupportedCluster { int clusterIdentifier; // 集群 ID int features; // 功能位图 int[] optionalCommandIdentifiers; // 可选命令 ID 列表 int[] optionalAttributesIdentifiers; // 可选属性 ID 列表 }SetSupportedClustersRequest.aidl 则是一次请求的载体:
parcelable SetSupportedClustersRequest { List<SupportedCluster> supportedClusters; }从字段设计可以推断,该结构能够表达"支持哪些集群、启用哪些 feature、支持哪些可选命令与可选属性",这与 Matter 数据模型中集群的 feature/optional command/optional attribute 概念一一对应。
四、Clusters 常量类:集群、命令与属性 ID 速查
Clusters.java 以嵌套静态类的方式组织常用集群 ID 及其对应的命令、属性、类型常量,为媒体投屏场景下各端与 Matter 规范中定义的相关集群之间提供"免查表"的引用方式。文件头注释注明其定位是"media related clusters",并留有"通过 ZAP 工具生成"的 TODO。当前已覆盖的集群及其 ID 如下:
| 集群常量类 | 集群 ID | 典型命令(命令 ID) | 典型属性(属性 ID) |
|---|---|---|---|
Clusters.AccountLogin | 0x050E | GetSetupPIN(0x00)、Login(0x02)、Logout(0x03) | — |
Clusters.MediaPlayback | 0x0506 | Play(0x00)、Pause(0x01)、Seek(0x0B) 等 | CurrentState(0x00)、SampledPosition(0x03)、PlaybackSpeed(0x04) |
Clusters.ContentLauncher | 0x050A | LaunchContent(0x00)、LaunchURL(0x01) | AcceptHeader(0x00)、SupportedStreamingProtocols(0x01) |
Clusters.TargetNavigator | 0x0505 | NavigateTarget(0x00) | TargetList(0x00)、CurrentTarget(0x01) |
几个有代表性的常量定义示例:
// MediaPlayback 集群:播放状态枚举与状态码 public static class Types { public static class PlaybackStateEnum { public static final int Playing = 0x00; public static final int Paused = 0x01; public static final int NotPlaying = 0x02; public static final int Buffering = 0x03; } public static class StatusEnum { public static final int Success = 0x00; public static final int InvalidStateForCommand = 0x01; public static final int NotAllowed = 0x02; ... } } // ContentLauncher 集群:搜索参数类型枚举(Actor/Channel/Genre/Provider...) public static class ParameterEnum { public static final int Actor = 0x00; public static final int Channel = 0x01; ... public static final int Video = 0x0D; }配合reportAttributeChange使用时,内容应用可直接引用这些常量避免裸写魔法数字,例如:
// 上报 MediaPlayback 集群的 CurrentState 属性变更 client.reportAttributeChange(Clusters.MediaPlayback.Id, Clusters.MediaPlayback.Attributes.CurrentState);五、Intent 常量:命令下发与属性读取的交互契约
MatterIntentConstants.java 除了前文介绍的 Action 与权限字符串外,还定义了命令/属性交互所需的全部 extra 字段名:
| 常量 | 值 | 用途 |
|---|---|---|
ACTION_MATTER_COMMAND | com.matter.tv.app.api.action.MATTER_COMMAND | 平台应用向内容应用下发 Matter 命令 |
ACTION_MATTER_AGENT | com.matter.tv.app.api.action.MatterAppAgent | 绑定 Matter Agent 服务的 Action |
PERMISSION_MATTER_AGENT_BIND | ...permission.BIND_SERVICE_PERMISSION | 客户端绑定所需权限 |
PERMISSION_MATTER_AGENT | ...permission.SEND_DATA | 宿主进程需持有的权限 |
EXTRA_COMMAND_PAYLOAD | EXTRA_COMMAND_PAYLOAD | 命令参数负载(字节数组) |
EXTRA_RESPONSE_PAYLOAD | EXTRA_RESPONSE_PAYLOAD | 响应负载 |
EXTRA_ATTRIBUTE_ACTION | EXTRA_ATTRIBUTE_ACTION | 属性操作类型 |
ATTRIBUTE_ACTION_READ | ATTRIBUTE_ACTION_READ | 属性读取操作标记 |
EXTRA_DIRECTIVE_RESPONSE_PENDING_INTENT | — | 携带用于回复的 PendingIntent |
EXTRA_COMMAND_ID/EXTRA_CLUSTER_ID/EXTRA_ATTRIBUTE_ID | — | 命令/集群/属性 ID |
在命令下发方向,服务端 ContentAppAgentService.java 的sendCommand静态方法演示了这些常量的实际拼装方式:以ACTION_MATTER_COMMAND构建 Intent,写入EXTRA_COMMAND_PAYLOAD、EXTRA_COMMAND_ID、EXTRA_CLUSTER_ID,并指定目标包名后投递给内容应用;同时定义了ACTION_MATTER_RESPONSE(com.matter.tv.app.api.action.MATTER_COMMAND_RESPONSE)用于内容应用回传结果,以及FAILED_UNSUPPORTED_CLUSTER(0xc3)、FAILED_UNSUPPORTED_COMMAND(0x81)、FAILED_UNSUPPORTED_ATTRIBUTE(0x86)、FAILED_TIMEOUT(0x94)等错误状态码常量,供命令链路两端统一错误语义。
六、实战:内容应用侧的完整调用链
仓库自带的内容应用示例 MatterAgentClient.java 展示了第三方内容应用接入common-api的标准姿势,可作为集成参考实现。其流程为:
- 初始化:在 Activity 或 BroadcastReceiver 中调用
MatterAgentClient.initialize(context)缓存 Context(用于连接丢失后重连); - 解析与绑定:以
new Intent(MatterIntentConstants.ACTION_MATTER_AGENT)查询可绑定的服务,并通过resolveBindIntent校验客户端自身持有PERMISSION_MATTER_AGENT_BIND、宿主持有PERMISSION_MATTER_AGENT(SEND_DATA),再执行bindService; - 同步屏障:首次远端调用前用
CountDownLatch等待服务连接建立(超时 8 秒),避免在 binder 尚未就绪时调用; - 业务调用:两个对外方法与 AIDL 接口一一对应,并处理
RemoteException:
public boolean reportClusters(SetSupportedClustersRequest request) { IMatterAppAgent matterAgent = getOrReinitializeMatterAgent(); if (matterAgent == null) return false; try { return matterAgent.setSupportedClusters(request); } catch (RemoteException e) { Log.e(TAG, "Error invoking remote method to set supported clusters to Matter agent"); } return false; } public boolean reportAttributeChange(int clusterId, int attributeId) { IMatterAppAgent matterAgent = getOrReinitializeMatterAgent(); if (matterAgent == null) return false; try { return matterAgent.reportAttributeChange(clusterId, attributeId); } catch (RemoteException e) { Log.e(TAG, "Error invoking remote method to report attribute change to Matter agent"); } return false; }值得注意的健壮性设计:getOrReinitializeMatterAgent()在发现 binder 为空时会触发一次重连重试,保证内容应用进程重启、服务重启等场景下调用依然可用。
七、关键文件索引
| 内容 | 路径 |
|---|---|
| 模块说明文档 | examples/tv-app/android/App/common-api/README.md |
| 核心 AIDL 接口 | IMatterAppAgent.aidl |
| 请求数据结构 | SupportedCluster.aidl、SetSupportedClustersRequest.aidl |
| 集群/属性 ID 常量 | Clusters.java |
| Intent/权限常量 | MatterIntentConstants.java |
| 内容应用参考客户端 | MatterAgentClient.java |
| 平台应用 Agent 服务实现 | ContentAppAgentService.java |
八、小结
common-api是 Matter tv-app 双进程架构中的"契约层":它以 AIDL 定义了setSupportedClusters(全量覆盖式动态集群注册)与reportAttributeChange(属性变更通知)两条上行通道,以 Intent 常量定义了下行命令与属性读取通道,并以Clusters常量类消除了内容应用与 Matter 集群 ID 之间的认知成本。对合作方而言,集成路径清晰——依赖该模块、声明相应权限、参考MatterAgentClient完成服务绑定,即可获得动态端点注册与属性上报能力;对平台侧而言,服务端以 UID 反查包名的做法在开放 API 的同时保证了端点归属的安全性。
【免费下载链接】connectedhomeipMatter (formerly Project CHIP) creates more connections between more objects, simplifying development for manufacturers and increasing compatibility for consumers, guided by the Connectivity Standards Alliance.项目地址: https://gitcode.com/GitHub_Trending/co/connectedhomeip
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考