Jami 20260403.0 API 使用详解

面向要在服务器上程序化操控 Jami 的开发者,讲清 Jami 20260403.0 的进程 API 全貌:D-Bus 总线端点(cx.ring.Ring)与四个对象、199 个方法 + 78 个信号的接口清单、busctl 调用约定(签名规则、错误模型、异步事件模式),并给出账户、配置编解码、设备信任、呼叫、信号订阅等各域的可运行实测调用与响应回显。

JamiJami即时通讯VoIPP2P视频会议2026/10/26

Jami 20260403.0 API 使用详解

目标:面向要在服务器上程序化操控 Jami 的开发者,讲清 Jami 20260403.0 的进程 API 全貌:D-Bus 总线端点(cx.ring.Ring)与四个对象、199 个方法 + 78 个信号的接口清单、busctl 调用约定(签名规则、错误模型、异步事件模式),并给出账户、配置编解码、设备信任、呼叫、信号订阅等各域的可运行实测调用与响应回显。全部调用在 Ubuntu 22.04 服务器(jamisrv1,账户 ff5bdfae563e6707)实测执行。

一、环境信息

项值来源
实验系统Ubuntu 22.04 服务器(主机名 jamisrv1,无图形界面)实测
软件版本jami-daemon 20260403.0~dfsg1-1(Jami Core 16.0.0)jamid --version 实测
API 形态D-Bus 会话总线(bus name cx.ring.Ring,由 jamid 提供);底层为 libjami C++ API 的 DBus 绑定实测
实验账户accountID ff5bdfae563e6707,RingID 813cbf71078e4d16b0462ce1c694c54bc2f52979实测
调用工具busctl(systemd 249 自带);图形化调试可用官方文档推荐的 d-feet实测/官方文档
实验日期2026-10-26实测
关联文章部署见《Ubuntu 22.04 安装 Jami 20260403.0 指南》;参数语义见《Jami 20260403.0 配置参数详解》本专栏

二、前置条件(端点与访问控制)

  1. jami-daemon 运行中(安装篇的 jamiserver.service),bus name 已注册:
sudo -iu jamiserver bash -c 'source /tmp/env-jamiserver; busctl --user list | grep cx.ring'
cx.ring.Ring             4386 jamid   jamiserver :1.0          jamiserver.service -       -
  1. 访问控制 = 会话总线 UID 归属:Jami 的 D-Bus API 没有 token/密码/TLS,安全边界是「这条私有会话总线属于谁」。总线由 jamiserver 用户拉起,root 直连会报 Transport endpoint is not connected(安装篇 FAQ#2 实测),因此所有调用都必须以运行 daemon 的用户身份执行:sudo -iu jamiserver + source /tmp/env-jamiserver。外部程序(Python dbus/gRPC-DBus 桥、systemd 服务)同理——用专用账户运行 daemon,再以同一账户运行调用方。
  2. 对象树与总线地址获取:
busctl --user tree cx.ring.Ring
└─/cx
  └─/cx/ring
    └─/cx/ring/Ring
      ├─/cx/ring/Ring/CallManager
      ├─/cx/ring/Ring/ConfigurationManager
      ├─/cx/ring/Ring/Instance
      └─/cx/ring/Ring/PresenceManager

三、API 总览

官方文档《APIs of Jami》口径:daemon API 由 5 个 Manager + 1 个 Instance 组成(CallManager / ConfigurationManager / PresenceManager / VideoManager / PluginManager 等管理器 + Instance 注册计数);其中 DBus 绑定在 jamid 20260403.0 上实际暴露 4 个对象(VideoManager 未单独出现,视频设备管理能力并入配置与媒体参数路径)——以下全部以实测树为准。

busctl --user introspect 全量方法表统计(实测):

对象(接口)方法数信号数职责
ConfigurationManager14251账户/配置/联系人/会话(swarm)/证书/数据传输/名字服务
CallManager5021呼叫与会议生命周期、媒体操作、通话内消息
PresenceManager55联系人在线状态订阅与发布
Instance21客户端注册计数(Register/Unregister/started)
合计19978

风格约定(全部接口一致):账户用 accountID(16 位十六进制串)定位、呼叫用 callID、会话用 conversationID;批量数据一律 a{ss}(字符串键值映射)或 as(字符串数组);写操作多返回 bool,读操作返回映射/数组。

四、通用调用约定(busctl 首呼与签名规则)

4.1 首次调用

sudo -iu jamiserver bash -c 'source /tmp/env-jamiserver; busctl --user call cx.ring.Ring /cx/ring/Ring/ConfigurationManager cx.ring.Ring.ConfigurationManager getAccountList ""'
as 1 "ff5bdfae563e6707"

busctl 四要素:call 服务 对象 接口 方法,方法名在服务内唯一时接口段可省写为 cx.ring.Ring.ConfigurationManager 全称(本篇全用全称,避免歧义)。

4.2 签名规则:多参数签名必须整体传

busctl 的参数模型是「一个完整签名串 + 扁平实参」。两个实测坑:

# setAccountDetails 真实签名 sa{ss}(账户ID + 键值映射)
setAccountDetails "sa{ss}" ff5bdfae563e6707 1 Account.autoAnswer true   # ✅ 成功

# 拆成 s 与 a{ss} 两段传 → 实测报错
"Too many parameters for signature"     # 签名只写了 s,多出的键值对无处安放
"Too few parameters for signature"      # 反向:签名给了,实参不足

同一规则适用于一切多参数方法,例如 exportToFile(签名 ssss)、placeCall(签名 ss)。先 busctl --user introspect 查签名,再整体传入是唯一可靠路径。

4.3 错误模型:空结果优先于异常

实测三类错误返回路径:

场景实测返回程序化处理
accountID 不存在getAccountDetails s 0000000000000000 → a{ss} 0(空映射,不抛 D-Bus 错误)判空而不是捕获异常
方法名不存在Call failed: Unknown method exportOnRing or interface …开发期 introspect 校对(exportOnRing 是旧 Ring 时代方法名,现名 exportToFile)
签名不匹配Too many/too few parameters for signaturebusctl 阶段即失败,不会触及 daemon

五、各 API 域详解

5.1 账户域(ConfigurationManager)

addAccount —— 创建账户(安装篇实测,键值映射入参,返回 accountID):

busctl --user call cx.ring.Ring /cx/ring/Ring/ConfigurationManager \
  cx.ring.Ring.ConfigurationManager addAccount "a{ss}" 4 \
  "Account.type" "RING" \
  "Account.alias" "headless-srv1" \
  "Account.displayName" "headless-srv1" \
  "Account.archivePath" ""
s "ff5bdfae563e6707"

getAccountTemplate —— 创建前的参数模板(实测返回 59 键,别名/设备名为空、Account.archiveHasPassword 模板默认 true):

... getAccountTemplate s RING
a{ss} 59 "Account.accountDiscovery" "false" ... "Account.alias" "" ... "Account.archiveHasPassword" "true" ... "Account.deviceName" "" ...

标准用法:先取模板 → 改若干键 → 传入 addAccount。其余账户域高频方法(签名实测见 introspect):

方法签名说明
getAccountDetailss → a{ss}59 键全量参数(配置篇详解)
getVolatileAccountDetailss → a{ss}9 键运行态(注册状态/宣告/DHT 端口)
setAccountDetailssa{ss} → -写参数,触发账户重载与重注册(配置篇 3.3 闭环实测)
sendRegistersb → -手动注册/注销(false 即离线,不删数据)
setAccountActivesb → -启停账户但不删除
removeAccounts → -删除账户(本地密钥数据一并清除,不可逆)

5.2 配置与编解码域

getCodecList / getCodecDetails(编解码器以 daemon 内部 ID 定位):

... getCodecList
au 12 27 139 12 4 86076 69660 69643 536956963 268521507 86051 65543 65542
... getCodecDetails su ff5bdfae563e6707 27
a{ss} 6 "CodecInfo.bitrate" "800" "CodecInfo.frameRate" "0" "CodecInfo.max_bitrate" "6000" "CodecInfo.min_bitrate" "200" "CodecInfo.name" "H.264/AVC" "CodecInfo.type" "VIDEO"
... getCodecDetails su ff5bdfae563e6707 139   # → "CodecInfo.name" "VP8",VIDEO

getSupportedTlsMethod(TLS 传输方法白名单):

as 4 "Default" "TLSv1.2" "TLSv1.1" "TLSv1"

音频设备/录音/历史等全局项也在此域(getAudioOutputDeviceList、getRecordPath、setHistoryLimit 等,共 142 个方法,全表可用 introspect 获取)。

5.3 设备、信任与备份域

getKnownRingDevices(本账户已链接设备):

... getKnownRingDevices s ff5bdfae563e6707
a{ss} 1 "d49eef0fb5e2a129fc0564c11c5c39bdb13675fe9f0e88c1553abfb4e3a1ab43" "jamisrv1"

exportToFile —— 导出账户备份(签名 4 个字符串:账户 ID、目标路径 + 两个可选参数实测传空串即成功;返回 bool):

... exportToFile ssss ff5bdfae563e6707 /tmp/srv1-archive "" ""
b true
$ ls -la /tmp/srv1-archive
-rw-r--r-- 1 jamiserver jamiserver 15114 Oct 26 06:25 /tmp/srv1-archive
$ head -c 8 /tmp/srv1-archive | od -A x -t x1z
000000 1f 8b 08 00 00 00 00 00   ← gzip 流(安装篇 6.6 节 archive.gz 的同形态封装)

该文件即账户的完整身份备份(密钥 + 配置),扩展名由调用方决定(桌面客户端导出为 .jac)。备份文件等同账户本体,落盘即应纳入加密保管。 信任与联系人读接口(新账户均为空集,实测回显):

getContacts      → aa{ss} 0
getTrustRequests → aa{ss} 0
getConversations → as 0

5.4 呼叫域(CallManager)

placeCall —— 发起呼叫(对不可达对端实测:callID 立即分配,呼叫自行失败后从列表消失,进程不报错):

busctl --user call cx.ring.Ring /cx/ring/Ring/CallManager cx.ring.Ring.CallManager \
  placeCall ss ff5bdfae563e6707 3d1112ab2bb089370c0744a44bbbb0786418d40b
s "4964305948076509"          ← daemon 生成的 callID
$ sleep 6 后查询
... getCallList s ff5bdfae563e6707
as 0                            ← 呼叫已终止并出清

呼叫生命周期方法全表(签名实测):accept/refuse/hangUp/hold/resume(ss → b)、placeCallWithMedia(ssaa{ss} → s,带自定义媒体参数)、transfer/attendedTransfer(sss → b)、toggleRecording(ss → b)、playDTMF(s → -)、会议族 addParticipant/joinParticipant/joinConference/createConfFromParticipantList 等。入呼是异步的:必须监听 incomingCall 信号拿到 callID 后再 accept/refuse(见 5.6)。

5.5 Presence 与 Instance

PresenceManager(5 方法):subscribeBuddy(ssb → -,订阅对端在线状态)、publish(sbs → -,发布本端状态)、getSubscriptions/setSubscriptions/answerServerRequest;通知走 newBuddyNotification(ssis)信号。Instance 仅 Register(is → -)/Unregister(i → -)与 started 信号,用于客户端向 daemon 声明存活计数,脚本化场景可忽略。

5.6 信号订阅:异步事件的唯一入口

写操作的结果与所有异步事件(入呼、来消息、注册状态变化)都通过 D-Bus 信号推送。busctl 实测监听(订阅 ConfigurationManager 全部信号,触发一次 setAccountDetails 后捕获):

busctl --user monitor --match "type=signal,interface=cx.ring.Ring.ConfigurationManager"
‣ Type=signal  Sender=:1.0  Path=/cx/ring/Ring/ConfigurationManager
  Interface=cx.ring.Ring.ConfigurationManager  Member=registrationStateChanged
  MESSAGE "ssis" {
          STRING "ff5bdfae563e6707";
          STRING "UNREGISTERED";      ← 账户重载瞬间先注销
          INT32 0;
          STRING "";
  };
‣ Member=volatileAccountDetailsChanged
  MESSAGE "sa{ss}" {
          STRING "ff5bdfae563e6707";
          ARRAY "{ss}" { STRING "Account.active"; STRING "true"; ...
                         STRING "Account.deviceAnnounced"; STRING "false"; ...

业务集成最常监听的信号(签名实测):

信号接口签名语义
registrationStateChangedConfigurationManagerssis账户注册状态机变化(TRYING/REGISTERED/UNREGISTERED…)
incomingCallCallManagersssaa{ss}入呼(携带 accountID/callID/对端 RingID)
incomingAccountMessageConfigurationManagerssssa{ss}收到点对点账户消息
swarmMessageReceivedConfigurationManagerss(…)收到 swarm 会话消息
callStateChangedCallManagersssi呼叫状态变化
dataTransferEventConfigurationManagerssssi文件传输进度/结果

注意:安装篇互通路径里提到的「监听 messagesReceived」,在 20260403.0 的实际信号名是 incomingAccountMessage(点对点)与 swarmMessageReceived(会话),老教程命名已被取代。生产集成用 sd-bus/GIO/dbus-python 订阅同名信号即可,语义与 busctl monitor 完全一致。

六、异步事件与轮询约定

本地 IPC 无分页/限流概念,通行的取数约定是两条(实测结论):

  1. 状态类数据轮询读:getVolatileAccountDetails 为权威快照,配合 3~5 秒间隔轮询足够;注册收敛实测约 25 秒内完成(TRYING → REGISTERED)。
  2. 事件类数据订阅信号:消息/呼叫/传输必须走信号(5.6),轮询方法(getLastMessages、getCallList)只适合补漏与对账。monitor() 方法(b → -)可让 daemon 输出调试流,排障期临时打开。

七、常见问题(实测命中)

  1. Too many/too few parameters for signature:多参数签名必须整体传(sa{ss}/ssss),见 4.2;这是 busctl 调 Jami 的第一大坑。
  2. 传了不存在的 accountID 不报错:返回空映射 a{ss} 0(4.3 实测)。程序必须判空,否则静默失效。
  3. 方法名对不上:老资料里的 exportOnRing 已更名 exportToFile;messagesReceived 信号已拆为 incomingAccountMessage/swarmMessageReceived。以 introspect 实时输出为准,不要照抄历史文档。
  4. root 跑集成脚本连不上:会话总线归属 jamiserver(2 节实测)。systemd 集成服务加 User=jamiserver 并 EnvironmentFile=/tmp/env-jamiserver 即可。
  5. placeCall 返回了 callID 但通话没建立:callID 分配 ≠ 呼叫成功;对端不可达时呼叫自行终止(5.4 实测 6 秒后列表为空)。成功与否以 callStateChanged 信号为准。
  6. 导出备份成功但文件没有 .jac 后缀:exportToFile 只负责内容(gzip 形态),扩展名由调用方指定;后缀不影响再次导入(addAccount 时指定 archivePath)。

八、参考链接