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 held
audio_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<DevicesFactoryHalInterface> 類型
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 [email protected];
import [email protected];
import IDevice;
interface IDevicesFactory {
typedef [email protected]::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;
}
最後,我們再一起通過下方的類圖,回顧和理清剛剛提到的幾個類之間的關係吧: