1)文章由移远通信技术股份有限公司提供
2)以下内容包含了个人理解,仅供参考,如有不合理处,请联系笔者修改/删除
文章目录
- 一、背景与目标
-
- [1.1 环境信息](#1.1 环境信息)
- [1.2 前置知识回顾](#1.2 前置知识回顾)
- [1.3 本文追踪目标](#1.3 本文追踪目标)
- 二、调试环境准备
-
- [2.1 调试配置](#2.1 调试配置)
- [2.2 关键断点设置](#2.2 关键断点设置)
- 三、audio_server接收COMMAND_START
-
- [3.1 断点命中确认](#3.1 断点命中确认)
- [3.2 验证code值](#3.2 验证code值)
- 四、RendererInServer到HpaeRendererStreamImpl
-
- [4.1 调用链传递](#4.1 调用链传递)
- [4.2 业务链路梳理](#4.2 业务链路梳理)
- [4.3 关键代码分析](#4.3 关键代码分析)
- 五、HPAE线程切换机制
-
- [5.1 多线程处理现象](#5.1 多线程处理现象)
- [5.2 标准库包装帧](#5.2 标准库包装帧)
- [5.3 AudioRenderSink入口](#5.3 AudioRenderSink入口)
- [六、AudioRenderSink调用HDI Proxy](#六、AudioRenderSink调用HDI Proxy)
-
- [6.1 关键代码分析](#6.1 关键代码分析)
- [6.2 函数指针验证](#6.2 函数指针验证)
- [6.3 调用链确认](#6.3 调用链确认)
- 七、AudioRenderProxyStart发送CMD_AUDIO_RENDER_START
-
- [7.1 生成代码分析](#7.1 生成代码分析)
- [7.2 验证命令值](#7.2 验证命令值)
- [7.3 GDB调试技巧](#7.3 GDB调试技巧)
- [八、AudioRenderProxyCall到HDF Remote Dispatch](#八、AudioRenderProxyCall到HDF Remote Dispatch)
-
- [8.1 条件断点命中](#8.1 条件断点命中)
- [8.2 代理调用实现](#8.2 代理调用实现)
- [九、HdfRemoteAdapterOptionalDispatch解析Binder Remote](#九、HdfRemoteAdapterOptionalDispatch解析Binder Remote)
-
- [9.1 断点命中](#9.1 断点命中)
- [9.2 关键源码分析](#9.2 关键源码分析)
- [9.3 GDB验证Binder对象](#9.3 GDB验证Binder对象)
- [9.4 获取Binder Handle](#9.4 获取Binder Handle)
- 十、证明Binder对端是audio_host
-
- [10.1 查看audio_server的Binder描述符](#10.1 查看audio_server的Binder描述符)
- [10.2 查找对应的Binder节点](#10.2 查找对应的Binder节点)
- [10.3 验证进程ID](#10.3 验证进程ID)
- [10.4 链路确认](#10.4 链路确认)
- 十一、结论
-
- [11.1 完整调用链路](#11.1 完整调用链路)
- [11.2 核心证明点](#11.2 核心证明点)
- [11.3 最终结论](#11.3 最终结论)
一、背景与目标
1.1 环境信息
- 硬件平台: RK3576
- 内核版本: Linux 6.6
- OpenHarmony版本: 6.1.0.31 (API 23)
c
// build/version.gni 关键配置
declare_args() {
sdk_version = "6.1.0.31"
api_version = "23"
release_type = "Release"
meta_version = "3.0.0"
platform_version = "4.0.0"
}
1.2 前置知识回顾
上一篇已证明的链路:
c
media_service
-> IpcStreamProxy::Start
-> IIpcStreamIpcCode::COMMAND_START
-> audio_server::IpcStreamInServer::Start
1.3 本文追踪目标
完整调用链路如下所示:
#mermaid-svg-5Po8REMt5i52Q46A{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-5Po8REMt5i52Q46A .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-5Po8REMt5i52Q46A .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-5Po8REMt5i52Q46A .error-icon{fill:#552222;}#mermaid-svg-5Po8REMt5i52Q46A .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-5Po8REMt5i52Q46A .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-5Po8REMt5i52Q46A .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-5Po8REMt5i52Q46A .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-5Po8REMt5i52Q46A .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-5Po8REMt5i52Q46A .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-5Po8REMt5i52Q46A .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-5Po8REMt5i52Q46A .marker{fill:#333333;stroke:#333333;}#mermaid-svg-5Po8REMt5i52Q46A .marker.cross{stroke:#333333;}#mermaid-svg-5Po8REMt5i52Q46A svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-5Po8REMt5i52Q46A p{margin:0;}#mermaid-svg-5Po8REMt5i52Q46A .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-5Po8REMt5i52Q46A .cluster-label text{fill:#333;}#mermaid-svg-5Po8REMt5i52Q46A .cluster-label span{color:#333;}#mermaid-svg-5Po8REMt5i52Q46A .cluster-label span p{background-color:transparent;}#mermaid-svg-5Po8REMt5i52Q46A .label text,#mermaid-svg-5Po8REMt5i52Q46A span{fill:#333;color:#333;}#mermaid-svg-5Po8REMt5i52Q46A .node rect,#mermaid-svg-5Po8REMt5i52Q46A .node circle,#mermaid-svg-5Po8REMt5i52Q46A .node ellipse,#mermaid-svg-5Po8REMt5i52Q46A .node polygon,#mermaid-svg-5Po8REMt5i52Q46A .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-5Po8REMt5i52Q46A .rough-node .label text,#mermaid-svg-5Po8REMt5i52Q46A .node .label text,#mermaid-svg-5Po8REMt5i52Q46A .image-shape .label,#mermaid-svg-5Po8REMt5i52Q46A .icon-shape .label{text-anchor:middle;}#mermaid-svg-5Po8REMt5i52Q46A .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-5Po8REMt5i52Q46A .rough-node .label,#mermaid-svg-5Po8REMt5i52Q46A .node .label,#mermaid-svg-5Po8REMt5i52Q46A .image-shape .label,#mermaid-svg-5Po8REMt5i52Q46A .icon-shape .label{text-align:center;}#mermaid-svg-5Po8REMt5i52Q46A .node.clickable{cursor:pointer;}#mermaid-svg-5Po8REMt5i52Q46A .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-5Po8REMt5i52Q46A .arrowheadPath{fill:#333333;}#mermaid-svg-5Po8REMt5i52Q46A .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-5Po8REMt5i52Q46A .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-5Po8REMt5i52Q46A .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5Po8REMt5i52Q46A .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-5Po8REMt5i52Q46A .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5Po8REMt5i52Q46A .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-5Po8REMt5i52Q46A .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-5Po8REMt5i52Q46A .cluster text{fill:#333;}#mermaid-svg-5Po8REMt5i52Q46A .cluster span{color:#333;}#mermaid-svg-5Po8REMt5i52Q46A div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-5Po8REMt5i52Q46A .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-5Po8REMt5i52Q46A rect.text{fill:none;stroke-width:0;}#mermaid-svg-5Po8REMt5i52Q46A .icon-shape,#mermaid-svg-5Po8REMt5i52Q46A .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5Po8REMt5i52Q46A .icon-shape p,#mermaid-svg-5Po8REMt5i52Q46A .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-5Po8REMt5i52Q46A .icon-shape .label rect,#mermaid-svg-5Po8REMt5i52Q46A .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5Po8REMt5i52Q46A .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-5Po8REMt5i52Q46A .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-5Po8REMt5i52Q46A :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} media_service
发送COMMAND_START
audio_server
IpcStreamInServer::Start
RendererInServer::Start
HpaeRendererStreamImpl::Start
HPAE线程切换
AudioRenderSink::Start
AudioRenderProxyStart
AudioRenderProxyCall
id=33
HdfRemoteAdapterOptionalDispatch
code=33
Binder IPC
handle=15
audio_host
HdfRemoteServiceStub::OnRemoteRequest
本文需要证明的三件事:
audio_server确实接收到了来自media_service的COMMAND_STARTaudio_server内部最终调用了HDI proxy的AudioRenderProxyStart- HDI proxy的Binder对端确实是
audio_host
二、调试环境准备
2.1 调试配置
attach到audio_server进程后,首先配置GDB信号处理:
gdb
# 忽略SIG38信号(OpenHarmony内部信号)
handle SIG38 nostop noprint pass
2.2 关键断点设置
gdb
# 1. audio_server入口断点
b OHOS::AudioStandard::IpcStreamInServer::Start
b OHOS::AudioStandard::RendererInServer::Start
b OHOS::AudioStandard::HpaeRendererStreamImpl::Start
b OHOS::AudioStandard::AudioRenderSink::Start
# 2. HDI proxy层断点
b AudioRenderProxyStart
b AudioRenderProxyCall if id == 33 # 条件断点,只捕获START命令
b HdfRemoteAdapterOptionalDispatch
condition <断点号> code == 33 # 条件断点,只捕获code=33
调试技巧:
AudioRenderProxyCall是通用helper函数,会被多个IAudioRender方法调用,必须加条件id == 33HdfRemoteAdapterOptionalDispatch也会被多个HDF请求复用,同样需要条件断点
三、audio_server接收COMMAND_START
3.1 断点命中确认
当IpcStreamInServer::Start被命中时,GDB堆栈如下:
c
#0 OHOS::AudioStandard::IpcStreamInServer::Start
at ipc_stream_in_server.cpp:195
#1 OHOS::AudioStandard::IpcStreamStub::OnRemoteRequest
(this=<optimized out>, code=4, data=..., reply=..., option=...)
at gen/foundation/multimedia/audio_framework/services/audio_service/idl/ipc_stream_stub.cpp:591
#2 OHOS::IPCObjectStub::SendRequestInner
(this=0x7fa0cb25f0, code=4, data=..., reply=..., option=...)
at ipc_object_stub.cpp:409

3.2 验证code值
在GDB中反推code=4对应的枚举值:
gdb
frame 1
p (OHOS::AudioStandard::IIpcStreamIpcCode)code
输出结果:
text
$1 = OHOS::AudioStandard::IIpcStreamIpcCode::COMMAND_START
结论 :当前audio_server接收到的正是来自media_service的COMMAND_START请求。
四、RendererInServer到HpaeRendererStreamImpl
4.1 调用链传递
继续执行,命中HpaeRendererStreamImpl::Start:

c
#0 OHOS::AudioStandard::HpaeRendererStreamImpl::Start
at hpae_renderer_stream_impl.cpp:158
#1 OHOS::AudioStandard::RendererInServer::StartInner
at renderer_in_server.cpp:1205
#2 OHOS::AudioStandard::RendererInServer::Start
at renderer_in_server.cpp:1129
#3 OHOS::AudioStandard::IpcStreamStub::OnRemoteRequest(code=4)
at ipc_stream_stub.cpp:591
#4 OHOS::IPCObjectStub::SendRequestInner(code=4)
4.2 业务链路梳理
c
IpcStreamStub::OnRemoteRequest(COMMAND_START)
-> IpcStreamInServer::Start
-> RendererInServer::Start
-> RendererInServer::StartInner
-> HpaeRendererStreamImpl::Start
4.3 关键代码分析
HpaeRendererStreamImpl::Start()的核心逻辑:
cpp
int32_t HpaeRendererStreamImpl::Start()
{
preBufDone_.store(false);
ClockTime::GetAllTimeStamp(timestamp_);
// 将播放流Start请求交给HPAE manager处理
int32_t ret = IHpaeManager::GetHpaeManager().Start(
HPAE_STREAM_CLASS_TYPE_PLAY, processConfig_.originalSessionId);
if (ret != SUCCESS) {
AUDIO_ERR_LOG("HpaeRendererStreamImpl::Start failed, ret:%{public}d", ret);
}
return ret;
}
关键点:这里只是将请求转发给HPAE(High Performance Audio Engine)管理器,真正的硬件操作在后面。
五、HPAE线程切换机制
5.1 多线程处理现象
继续执行会发现HpaeManager::Start和HpaeRendererManager::Start在不同线程中被命中:
c
Thread "OS_IPC_*" hit HpaeManager::Start
Thread "HpaeManager" hit HpaeManager::Start::$_30::operator()
Thread "Speaker" hit HpaeRendererManager::Start::$_7::operator()
5.2 标准库包装帧
GDB堆栈中会出现大量标准库帧,这是HPAE内部任务调度的包装:
c
std::__h::__invoke
std::__h::function<void ()>::operator()
HpaeNoLockQueue::ProcessRequests
HpaeSignalProcessThread::Run
这些帧属于任务调度机制,不是业务逻辑核心。过滤后得到核心链路:
c
HpaeRendererStreamImpl::Start
-> HPAE::HpaeManagerImpl::Start
-> HPAE::HpaeManager::Start
-> HPAE::HpaeRendererManager::Start
-> HPAE::HpaeRendererManager::ConnectInputSession
-> HPAE::HpaeSinkOutputNode::RenderSinkStart
-> AudioRenderSink::Start
5.3 AudioRenderSink入口
命中AudioRenderSink::Start时的GDB堆栈:
c
#0 OHOS::AudioStandard::AudioRenderSink::Start
at audio_render_sink.cpp:112
#1 OHOS::AudioStandard::HPAE::HpaeSinkOutputNode::RenderSinkStart
at hpae_sink_output_node.cpp:381
#2 OHOS::AudioStandard::HPAE::HpaeRendererManager::ConnectInputSession
at hpae_renderer_manager.cpp:472
#3 OHOS::AudioStandard::HPAE::HpaeRendererManager::Start(unsigned int)::$_7::operator()()
at hpae_renderer_manager.cpp:675
#10 OHOS::AudioStandard::HPAE::HpaeNoLockQueue::ProcessRequests
#11 OHOS::AudioStandard::HPAE::HpaeSignalProcessThread::Run
六、AudioRenderSink调用HDI Proxy
6.1 关键代码分析
AudioRenderSink::Start()的实现:
cpp
int32_t AudioRenderSink::Start(void)
{
std::lock_guard<std::mutex> lock(sinkMutex_);
// 检查是否已启动
if (started_) {
return SUCCESS;
}
// 验证audioRender_指针
CHECK_AND_RETURN_RET_LOG(audioRender_ != nullptr,
ERR_INVALID_HANDLE, "render is nullptr");
// 调用HDI proxy的Start方法
int32_t ret = audioRender_->Start(audioRender_);
if (ret == SUCCESS) {
started_ = true;
AUDIO_INFO_LOG("AudioRenderSink::Start success");
} else {
AUDIO_ERR_LOG("AudioRenderSink::Start failed, ret:%{public}d", ret);
}
return ret;
}
6.2 函数指针验证
在GDB中验证audioRender_->Start的实际函数:
gdb
# 打印audioRender_指针
p audioRender_
# 打印Start函数指针
p audioRender_->Start
# 查看函数符号信息
info symbol audioRender_->Start
实测输出:
text
p audioRender_
$5 = (IAudioRender *) 0x7fbe862800
p audioRender_->Start
$6 = (int32_t (*)(IAudioRender *)) 0x7f3bbd6e70 <AudioRenderProxyStart>
info symbol audioRender_->Start
AudioRenderProxyStart in section .text of .../libaudio_proxy_6.0.z.so
6.3 调用链确认
c
AudioRenderSink::Start
-> audioRender_->Start(audioRender_) // 函数指针调用
-> AudioRenderProxyStart // 实际调用的HDI proxy函数
重要发现 :audio_server并没有直接调用audio_host中的service实现,而是调用了libaudio_proxy_6.0.z.so中的HDI proxy函数。
七、AudioRenderProxyStart发送CMD_AUDIO_RENDER_START
7.1 生成代码分析
AudioRenderProxyStart()是HDF工具自动生成的代理代码:
c
static int32_t AudioRenderProxyStart(struct IAudioRender *self)
{
struct HdfSBuf *audioRenderData = HdfSbufTypedObtain(SBUF_IPC);
struct HdfSBuf *audioRenderReply = HdfSbufTypedObtain(SBUF_IPC);
// 写入接口token
if (!HdfRemoteServiceWriteInterfaceToken(self->AsObject(self), audioRenderData)) {
HDF_LOGE("%{public}s: write interface token failed", __func__);
HdfSbufRecycle(audioRenderData);
HdfSbufRecycle(audioRenderReply);
return HDF_ERR_INVALID_PARAM;
}
// 调用代理函数,传递CMD_AUDIO_RENDER_START
int32_t audioRenderRet = AudioRenderProxyCall(
self, CMD_AUDIO_RENDER_START, audioRenderData, audioRenderReply, false);
HdfSbufRecycle(audioRenderData);
HdfSbufRecycle(audioRenderReply);
return audioRenderRet;
}

7.2 验证命令值
在GDB中验证CMD_AUDIO_RENDER_START的值:

gdb
(gdb) frame 1
#1 0x0000007f983d4cc0 in HdfRemoteServiceStub::OnRemoteRequest (this=0x7f989d67c0, code=33, data=..., reply=..., option=...)
at ../../drivers/hdf_core/adapter/uhdf2/ipc/src/hdf_remote_adapter.cpp:60
60 ret = dispatcher->Dispatch(reinterpret_cast<HdfRemoteService *>(service_->target), code, dataSbuf, replySbuf);
(gdb) p this
$5 = (HdfRemoteServiceStub *) 0x7f989d67c0
(gdb) p this->service_
$6 = (HdfRemoteService *) 0x7f989dde50
(gdb) p this->service_->dispatcher
$7 = (HdfRemoteDispatcher *) 0x7f98a169e0
(gdb) p this->service_->dispatcher->Dispatch
$8 = (int (*)(HdfRemoteService *, int, HdfSBuf *, HdfSBuf *)) 0x7f17291270 <AudioRenderOnRemoteRequest>
(gdb) list *this->service_->dispatcher->Dispatch
0x7f17291270 is in AudioRenderOnRemoteRequest (gen/drivers/interface/audio/v6_0/audio_render_stub.c:1620).
1615 struct AudioRenderStub *stub = CONTAINER_OF(self, struct AudioRenderStub, interface);
1616 return stub->remote;
1617 }
1618
1619 static int32_t AudioRenderOnRemoteRequest(struct HdfRemoteService *remote, int code, struct HdfSBuf *data, struct HdfSBuf *reply)
1620 {
1621 struct AudioRenderStub *stub = (struct AudioRenderStub*)remote;
1622 if (stub == NULL || stub->remote == NULL || stub->interface == NULL) {
1623 HDF_LOGE("%{public}s: invalid stub object", __func__);
1624 return HDF_ERR_INVALID_OBJECT;
1625 }
1626 if (!HdfRemoteServiceCheckInterfaceToken(stub->remote, data)) {
1627 HDF_LOGE("%{public}s: interface token check failed", __func__);
1628 return HDF_ERR_INVALID_PARAM;
1629 }
1630
1631 switch (code) {
1632 case CMD_AUDIO_RENDER_GET_LATENCY:
1633 return SerStubGetLatency(stub->interface, data, reply);
1634 case CMD_AUDIO_RENDER_RENDER_FRAME:
gdb
# 直接打印值
p/d CMD_AUDIO_RENDER_START
# 使用typeof技巧查看枚举名称
p (typeof(CMD_AUDIO_RENDER_GET_LATENCY))CMD_AUDIO_RENDER_START
# 验证33对应的枚举
p (typeof(CMD_AUDIO_RENDER_GET_LATENCY))33
实测输出:
text
$2 = 33
$3 = CMD_AUDIO_RENDER_START
7.3 GDB调试技巧
CMD_AUDIO_RENDER_*是匿名枚举,没有直接的枚举类型名。GDB不能直接使用:
gdb
# 错误:类型名不存在
p (AudioRenderInterfaceCode)33
正确做法是借用同一枚举组中的任意常量类型:
gdb
# 正确:使用typeof获取枚举类型
p (typeof(CMD_AUDIO_RENDER_GET_LATENCY))33
这样就可以从整数值33反推出对应的枚举常量CMD_AUDIO_RENDER_START。
八、AudioRenderProxyCall到HDF Remote Dispatch
8.1 条件断点命中
使用条件断点捕获AudioRenderProxyCall:
gdb
b AudioRenderProxyCall if id == 33
命中时的GDB堆栈:

c
#0 AudioRenderProxyCall
(self=0x7fbc899b20, id=33, data=0x7f3b0aaac0,
reply=0x7f3b0aaae0, isOneWay=false)
at gen/drivers/interface/audio/v6_0/audio_render_proxy.c:80
#1 AudioRenderProxyStart
at gen/drivers/interface/audio/v6_0/audio_render_proxy.c:1723
#2 OHOS::AudioStandard::AudioRenderSink::Start
at audio_render_sink.cpp:144
#3 OHOS::AudioStandard::HPAE::HpaeSinkOutputNode::RenderSinkStart
at hpae_sink_output_node.cpp:381
#4 OHOS::AudioStandard::HPAE::HpaeRendererManager::ConnectInputSession
at hpae_renderer_manager.cpp:472
#5 OHOS::AudioStandard::HPAE::HpaeRendererManager::Start(unsigned int)::$_7::operator()()
at hpae_renderer_manager.cpp:675
8.2 代理调用实现
AudioRenderProxyCall()的实现:
c
static int32_t AudioRenderProxyCall(struct IAudioRender *self, int32_t id,
struct HdfSBuf *data, struct HdfSBuf *reply, bool isOneWay)
{
struct HdfRemoteService *remote = self->AsObject(self);
// 参数检查
if (remote == NULL ||
remote->dispatcher == NULL ||
remote->dispatcher->Dispatch == NULL ||
remote->dispatcher->DispatchAsync == NULL) {
return HDF_ERR_INVALID_OBJECT;
}
// 根据isOneWay选择同步或异步调用
if (isOneWay) {
return remote->dispatcher->DispatchAsync(remote, id, data, reply);
} else {
return remote->dispatcher->Dispatch(remote, id, data, reply);
}
}
由于isOneWay=false,实际走的是同步调用路径:
text
remote->dispatcher->Dispatch(remote, id, data, reply)
这里的id=33就是CMD_AUDIO_RENDER_START。
九、HdfRemoteAdapterOptionalDispatch解析Binder Remote
9.1 断点命中
继续命中HdfRemoteAdapterOptionalDispatch:

c
#0 HdfRemoteAdapterOptionalDispatch
(service=0x7fbbef4bf0, code=33, data=0x7f3b0aaac0, reply=0x7f3b0aaae0, sync=true)
at hdf_remote_adapter.cpp:111
#1 AudioRenderProxyCall
(self=0x7fbc899b20, id=33, ...)
at audio_render_proxy.c:91
#2 AudioRenderProxyStart
at audio_render_proxy.c:1723
#3 AudioRenderSink::Start
at audio_render_sink.cpp:144
9.2 关键源码分析
HdfRemoteAdapterOptionalDispatch的关键源码:
cpp
static int HdfRemoteAdapterOptionalDispatch(struct HdfRemoteService *service, int code,
HdfSBuf *data, HdfSBuf *reply, bool sync)
{
...
int flag = sync ? OHOS::MessageOption::TF_SYNC : OHOS::MessageOption::TF_ASYNC;
OHOS::MessageOption option(flag);
struct HdfRemoteServiceHolder *holder = reinterpret_cast<struct HdfRemoteServiceHolder *>(service);
if (dataParcel != nullptr) {
OHOS::sptr<OHOS::IRemoteObject> remote = holder->remote_;
if (remote != nullptr) {
return remote->SendRequest(code, *dataParcel, *replyParcel, option);
}
}
return HDF_FAILURE;
}
关键点:
HdfRemoteService *service实际上是HdfRemoteServiceHolder的起始地址holder->remote_才是真正的 BinderIRemoteObjectproxy- 不要看
service->target,client proxy 侧的service->target可能是 0
9.3 GDB验证Binder对象
在GDB中验证Binder对象:
gdb
# 查看HdfRemoteServiceHolder结构
ptype HdfRemoteServiceHolder
# 转换指针类型
p (struct HdfRemoteServiceHolder*)service
# 获取remote_成员
p ((struct HdfRemoteServiceHolder*)service)->remote_
# 查看引用计数
p ((struct HdfRemoteServiceHolder*)service)->remote_.refs_
实测输出:
text
type = struct HdfRemoteServiceHolder {
HdfRemoteService service_;
OHOS::sptr<OHOS::IRemoteObject> remote_;
OHOS::sptr<OHOS::IRemoteObject::DeathRecipient> deathRecipient_;
std::__h::u16string descriptor_;
}
$10 = (HdfRemoteServiceHolder *) 0x7fbbef4bf0
$11 = {refs_ = 0x7fbc899970}
$12 = (OHOS::IRemoteObject *) 0x7fbc899970
9.4 获取Binder Handle
继续把 remote_.refs_ 转成 IPCObjectProxy,取 binder handle:
gdb
p/d ((OHOS::IPCObjectProxy*)0x7fbc899970)->handle_
实测输出:
text
$17 = 15
结论 :audio_server 中 HDF AudioRender proxy 的 Binder handle 是 15。
十、证明Binder对端是audio_host
10.1 查看audio_server的Binder描述符
在板端查看 audio_server 的 binder proc:
sh
cat /sys/kernel/debug/binder/proc/$(pidof audio_server) | grep "desc 15"
实测输出:
text
ref 75509: desc 15 node 75508 s 1 w 0 d 0000000000000000
这表示:audio_server 的 binder desc 15 指向 node 75508。
10.2 查找对应的Binder节点
再去 audio_host 的 binder proc 查同一个 node:
sh
cat /sys/kernel/debug/binder/proc/$(pidof audio_host) | grep "node 75508"
实测输出:
text
node 75508: u0000007fa96a04f0 c0000007fa96607c0 hs 1 hw 1 ls 0 lw 0 is 1 iw 1 tr 1 proc 4995
10.3 验证进程ID
确认 proc 4995 对应的进程:
sh
ps -ef | grep audio_server
实测输出:
text
audio 4995 1 0 16:16:49 ? 00:00:02 audio_server
10.4 链路确认
这说明:
text
audio_server 中的 Binder desc 15
-> node 75508
-> node 75508 位于 audio_host
-> 当前引用进程 proc 4995 是 audio_server
因此,audio_server 中 AudioRenderProxyCall(id=33) 最终发送到了 audio_host。
十一、结论
11.1 完整调用链路
整个调用链路可以整理为:
c
audio_server:
IpcStreamStub::OnRemoteRequest(IIpcStreamIpcCode::COMMAND_START)
-> IpcStreamInServer::Start
-> RendererInServer::Start
-> RendererInServer::StartInner
-> HpaeRendererStreamImpl::Start
-> IHpaeManager::GetHpaeManager().Start(...)
-> HPAE::HpaeManagerImpl::Start
-> HPAE::HpaeManager::Start
-> HPAE::HpaeRendererManager::Start
-> HPAE::HpaeRendererManager::ConnectInputSession
-> HPAE::HpaeSinkOutputNode::RenderSinkStart
-> AudioRenderSink::Start
-> audioRender_->Start(audioRender_)
-> AudioRenderProxyStart
-> AudioRenderProxyCall(id=33)
-> HdfRemoteAdapterOptionalDispatch(code=33)
-> remote->SendRequest(33, ...)
audio_host:
HdfRemoteServiceStub::OnRemoteRequest(code=33)
11.2 核心证明点
通过GDB调试和代码分析,我们证明了以下关键点:
text
1. audio_server 入口 code=4,可反推为 IIpcStreamIpcCode::COMMAND_START
2. HpaeRendererStreamImpl::Start 进入 HPAE
3. HPAE 在线程切换后进入 AudioRenderSink::Start
4. AudioRenderSink::Start 调用 audioRender_->Start
5. audioRender_->Start 的实际函数指针是 AudioRenderProxyStart
6. AudioRenderProxyStart 调用 AudioRenderProxyCall(..., CMD_AUDIO_RENDER_START, ...)
7. CMD_AUDIO_RENDER_START 的整数值是 33
8. HdfRemoteAdapterOptionalDispatch(code=33) 中的 holder->remote_ 是 IPCObjectProxy
9. 该 IPCObjectProxy 的 handle 是 15
10. binder debugfs 证明 audio_server 的 desc 15 指向 audio_host 中的 node
11.3 最终结论
text
audio_server 收到 media_service 的流 Start 后,
经过 RendererInServer 和 HPAE 启动本地 render sink,
最终通过 HDF AudioRender proxy 向 audio_host 发送 CMD_AUDIO_RENDER_START。