OpenHarmony播放音乐start请求从audio_server到audio_host的调用流程03

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

本文需要证明的三件事

  1. audio_server确实接收到了来自media_serviceCOMMAND_START
  2. audio_server内部最终调用了HDI proxy的AudioRenderProxyStart
  3. 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 == 33
  • HdfRemoteAdapterOptionalDispatch也会被多个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_serviceCOMMAND_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::StartHpaeRendererManager::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_ 才是真正的 Binder IRemoteObject proxy
  • 不要看 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_serverAudioRenderProxyCall(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。
相关推荐
moyh-blog4 小时前
OpenHarmony播放音乐start请求从media_service到audio_server的调用流程02
openharmony·audio
moyh-blog8 小时前
OpenHarmony播放音乐start请求从audio_host到ALSA库的调用流程04
audio·openahrmony
一颗小行星!7 天前
在线音频分轨网站推荐:5 款人声、伴奏与乐器分离工具对比
audio
山顶夕景1 个月前
【Audio】Audio encoder相关Benchmark
大模型·音视频·多模态·audio·检索·全模态
CheungChunChiu3 个月前
Linux 音频子系统完整梳理:ALSA、ASoC、DAPM、Codec、Machine、es8389 与 rk‑multicodecs 全解析
linux·运维·音视频·codec·audio·asla·dapm
summerkissyou19874 个月前
Android-Audio-根据音频焦点控制播放
android·audio
ameyume4 个月前
基于原生Android 16设置音量调用流程
android·audio
千里马学框架5 个月前
干货分享:车载音频audio调试开发之dumpsys CarAudioService剖析
android·音视频·面试题·audio·系统开发·车载audio·framework工程师
summerkissyou19875 个月前
Android-Audio-编码和解码
android·audio