AudioFlinger 如何通过 hwBinder 调用 Audio HAL
AudioFlinger 如何通过 hwBinder 调用 Audio HAL
注意:本文基于 Android 8.1 进行分析
Qidi 2020.07.03 (Markdown & Haroopad)
我们已经知道,在AudioPolicyManager
构造时,会解析配置文件audio_policy.conf
或audio_policy_configuration.xml
。之后,它会获取到AudioFlinger
的实例,并根据解析结果,调用loadHwModule()
依次加载各个 audio HAL。
因此,AudioFlinger
与 Audio HAL 建立联系的过程一定能在它内部的代码中找到。让我们直接从AudioFlinger
的代码开始分析:
AudioFlinger::AudioFlinger() : BnAudioFlinger(), mMediaLogNotifier(new AudioFlinger::MediaLogNotifier()), mPrimaryHardwareDev(NULL), mAudioHwDevs(NULL), //...{ //... // 初始化 mDevicesFactoryHal mDevicesFactoryHal = DevicesFactoryHalInterface::create(); //...}// loadHwModule_l() must be called with AudioFlinger::mLock heldaudio_module_handle_t AudioFlinger::loadHwModule_l(const char *name){ for (size_t i = 0; i < mAudioHwDevs.size(); i++) { if (strncmp(mAudioHwDevs.valueAt(i)->moduleName(), name, strlen(name)) == 0) { ALOGW("loadHwModule() module %s already loaded", name); return mAudioHwDevs.keyAt(i); } } sp<DeviceHalInterface> dev; // 以 Legacy 方式或 Binder 方式,真正加载 Audio HAL 的入口 int rc = mDevicesFactoryHal->openDevice(name, &dev); if (rc) { ALOGE("loadHwModule() error %d loading module %s", rc, name); return AUDIO_MODULE_HANDLE_NONE; } //... mAudioHwDevs.add(handle, new AudioHwDevice(handle, name, dev, flags)); ALOGI("loadHwModule() Loaded %s audio interface, handle %d", name, handle); return handle;}
我们看到 AudioFlinger::loadHwModule_l()
调用了mDevicesFactoryHal->openDevice()
,而实际上 mDeviceFactoryHal
是类DevicesFactoryHalHybrid
的一个实例。
在源文件 /frameworks/av/media/libaudiohal/DevicesFactoryHalHybrid.cpp
里,我们可以看到类的实现:
sp<DevicesFactoryHalInterface> DevicesFactoryHalInterface::create() { return new DevicesFactoryHalHybrid();}// DevicesFatoryHalHybrid 类的作用是对 mLocalFactory 和 mHidlFactory 进行包裹,// 而这两个成员实际上都是 sp 类型 DevicesFactoryHalHybrid::DevicesFactoryHalHybrid() : mLocalFactory(new DevicesFactoryHalLocal()), mHidlFactory(#ifdef USE_LEGACY_LOCAL_AUDIO_HAL nullptr#else new DevicesFactoryHalHidl()#endif ) {}DevicesFactoryHalHybrid::~DevicesFactoryHalHybrid() {}status_t DevicesFactoryHalHybrid::openDevice(const char *name, sp<DeviceHalInterface> *device) { // 当目标不是 A2DP HAL, 且宏 USE_LEGACY_LOCAL_AUDIO_HAL 未定义时, // 始终使用 hwbinder 方式获取 Audio HAL // // 以我所开发的设备为例, 宏 USE_LEGACY_LOCAL_AUDIO_HAL 在 BoardCongif_common.mk 中 // 被定义为 false if (mHidlFactory != 0 && strcmp(AUDIO_HARDWARE_MODULE_ID_A2DP, name) != 0) { return mHidlFactory->openDevice(name, device); } return mLocalFactory->openDevice(name, device);}
顺带一提,这里可以看到通过定义宏USE_LEGACY_LOCAL_AUDIO_HAL
,我们可以强制AudioFlinger
不使用 hwBinder 访问 Audio HAL,而是采用老方法加载*.so
库文件。这个宏一般定义在BoardConfig.mk
文件中。但需要注意,即便使用加载库的方式也稍稍有了变化:以前 AudioFlinger 通过 dlopen()
直接加载,现在变成了调用 vndk 接口 android_load_sphal_library()
来实现。
闲话休题。到了这里实际依然还没抵达 hwBinder 的入口,而是继续调用了 DevicesFactoryHalHybrid
的成员接口 openDevice()
。层层深入,再来看看 DevicesFactoryHalHidl
类中的实现:
status_t DevicesFactoryHalHidl::openDevice(const char *name, sp<DeviceHalInterface> *device) { if (mDevicesFactory == 0) return NO_INIT; IDevicesFactory::Device hidlDevice; status_t status = nameFromHal(name, &hidlDevice); if (status != OK) return status; Result retval = Result::NOT_INITIALIZED; // 此处的 openDevice() 会调用到 BpHwDevicesFactory::openDevice() 中, // BpHwDevicesFactory 的实现代码是在编译时由 soong 自动生成的 Return<void> ret = mDevicesFactory->openDevice( hidlDevice, // 该 lamda 函数作为回调函数传入 Bp 端, // 当 Binder 调用完成时,该 lamda 函数会被调用 [&](Result r, const sp<IDevice>& result) { retval = r; if (retval == Result::OK) { *device = new DeviceHalHidl(result); // result 是从 Binder 调用完成后返回的 IDevice 实例 } }); if (ret.isOk()) { if (retval == Result::OK) return OK; else if (retval == Result::INVALID_ARGUMENTS) return BAD_VALUE; else return NO_INIT; } return FAILED_TRANSACTION;}
终于,在这里面我们看到了另一个 openDevice()
,它属于 IDevicesFactory
,这就是和 audio HAL 相关的具体的 hwBinder 接口类了。这个类相应的代码实际上是在编译时自动生成的,源文件(或者说接口定义文件,HIDL 本来也是这个含义)中只有对接口的定义,位于 /hardware/interfaces/audio/2.0/IDevicesFactory.hal
中:
package android.hardware.audio@2.0;import android.hardware.audio.common@2.0;import IDevice;interface IDevicesFactory { typedef android.hardware.audio@2.0::Result Result; enum Device : int32_t { PRIMARY, A2DP, USB, R_SUBMIX, STUB }; /** * Opens an audio device. To close the device, it is necessary to release * references to the returned device object. * * @param device device type. * @return retval operation completion status. Returns INVALID_ARGUMENTS * if there is no corresponding hardware module found, * NOT_INITIALIZED if an error occured while opening the hardware * module. * @return result the interface for the created device. */ openDevice(Device device) generates (Result retval, IDevice result);};
编译时,上方的 .hal
文件会转换成相应的 .cpp
和 .h
文件。这些文件均可以对应在 /out/soong/.intermediates/hardware/interfaces/audio/2.0/
路径下找到。这里我们简要查看 openDevice()
接口相关的生成代码,看看都做了些什么:
// Methods from IDevicesFactory follow.::android::hardware::Return<void> BpHwDevicesFactory::_hidl_openDevice(::android::hardware::IInterface *_hidl_this, ::android::hardware::details::HidlInstrumentor *_hidl_this_instrumentor, IDevicesFactory::Device device, openDevice_cb _hidl_cb) { #ifdef __ANDROID_DEBUGGABLE__ //... #else (void) _hidl_this_instrumentor; #endif // __ANDROID_DEBUGGABLE__ //... #endif // __ANDROID_DEBUGGABLE__ ::android::hardware::Parcel _hidl_data; ::android::hardware::Parcel _hidl_reply; ::android::status_t _hidl_err; ::android::hardware::Status _hidl_status; Result _hidl_out_retval; ::android::sp<IDevice> _hidl_out_result; _hidl_err = _hidl_data.writeInterfaceToken(BpHwDevicesFactory::descriptor); if (_hidl_err != ::android::OK) { goto _hidl_error; } _hidl_err = _hidl_data.writeInt32((int32_t)device); if (_hidl_err != ::android::OK) { goto _hidl_error; } // (生成代码)执行 transact 调用, 传入 command code 1 (openDevice) _hidl_err = ::android::hardware::IInterface::asBinder(_hidl_this)->transact(1 /* openDevice */, _hidl_data, &_hidl_reply); if (_hidl_err != ::android::OK) { goto _hidl_error; } _hidl_err = ::android::hardware::readFromParcel(&_hidl_status, _hidl_reply); if (_hidl_err != ::android::OK) { goto _hidl_error; } if (!_hidl_status.isOk()) { return _hidl_status; } _hidl_err = _hidl_reply.readInt32((int32_t *)&_hidl_out_retval); if (_hidl_err != ::android::OK) { goto _hidl_error; } { ::android::sp<::android::hardware::IBinder> _hidl__hidl_out_result_binder; // (生成代码)获得 audio HAL binder 实例 _hidl_err = _hidl_reply.readNullableStrongBinder(&_hidl__hidl_out_result_binder); if (_hidl_err != ::android::OK) { goto _hidl_error; } // 获得 audio device 实例 _hidl_out_result = ::android::hardware::fromBinder<IDevice,BpHwDevice,BnHwDevice>(_hidl__hidl_out_result_binder); } // 调用回调函数(也就是前面的 lamda 函数)将 audio device 实例传回上层 _hidl_cb(_hidl_out_retval, _hidl_out_result); atrace_end(ATRACE_TAG_HAL); #ifdef __ANDROID_DEBUGGABLE__ //... #endif // __ANDROID_DEBUGGABLE__ _hidl_status.setFromStatusT(_hidl_err); return ::android::hardware::Return<void>();_hidl_error: _hidl_status.setFromStatusT(_hidl_err); return ::android::hardware::Return<void>(_hidl_status);}
能看出来,这些自动生成的代码仍然是使用的 Parcel.writeXXX --> asBinder()/remote() --> transact() --> BinderDriver --> onTransact() --> Parcel.readXXX
那一套。(实际上,binder, hwbinder 和 vndbinder 共用一套 Binder 类关系和代码逻辑)
至此,AudioFlinger 便获得了 audio HAL 的 Bp 端实例,通过它即可自由调用 audio HAL 任意接口。以 setMasterVolume()
为例:
status_t AudioFlinger::setMasterVolume(float value){ //... // Set master volume in the HALs which support it. for (size_t i = 0; i < mAudioHwDevs.size(); i++) { AutoMutex lock(mHardwareLock); AudioHwDevice *dev = mAudioHwDevs.valueAt(i); mHardwareStatus = AUDIO_HW_SET_MASTER_VOLUME; if (dev->canSetMasterVolume()) { // 通过 audio device 实例调用 Audio HAL 中实现的 setMasterVolume() 接口 // 实际执行时依然以 HIDL 方式经过 hwBinder 完成 dev->hwDevice()->setMasterVolume(value); } mHardwareStatus = AUDIO_HW_IDLE; } //... return NO_ERROR;}
最后,我们再一起通过下方的类图,回顾和理清刚刚提到的几个类之间的关系吧:
更多相关文章
- MTK Android(安卓)Audio 序
- Android(安卓)使用SWIG生成Jni代码
- Android下调试.so库类的bug
- Android下载的视频,在相册中显示
- Android(安卓)集成百度地图实现设备定位
- Android(安卓)NDK学习(7)NDK测试时遇到的问题:C与C++互相调用
- Android回调的使用方式
- Android应用开发提高系列(4)
- android studio 自用快捷键方案