安卓音频接口从APP到Hal的调用流程

本文基于安卓14源码以setParameters这个接口为例追踪其从APP到HAL层的调用流程。

APP层调用

复制代码
AudioManager mAudioManager = (AudioManager)getContext().getSystemService(Context.AUDIO_SERVICE);
mAudioManager.setParameters("key-value");

==》/frameworks/base/media/java/android/media/AudioManager.java

复制代码
/**
 * Sets a variable number of parameter values to audio hardware.
 *
 * @param keyValuePairs list of parameters key value pairs in the form:
 *    key1=value1;key2=value2;...
 *
 */
public void setParameters(String keyValuePairs) {
    AudioSystem.setParameters(keyValuePairs);
}

==>/frameworks/base/media/java/android/media/AudioSystem.java

复制代码
/**
 * @hide
 * Sets a group generic audio configuration parameters. The use of these parameters
 * are platform dependent, see libaudio
 *
 * param keyValuePairs  list of parameters key value pairs in the form:
 *    key1=value1;key2=value2;...
 */
@UnsupportedAppUsage
public static native int setParameters(String keyValuePairs);

AudioSystem.java中setParameters是native方法,通过JNI调用到native层。

==》/frameworks/base/core/jni/android_media_AudioSystem.cpp

复制代码
android_media_AudioSystem_setParameters(JNIEnv *env, jobject thiz, jstring keyValuePairs)
{
    const jchar* c_keyValuePairs = env->GetStringCritical(keyValuePairs, 0);
    String8 c_keyValuePairs8;
    if (keyValuePairs) {
        c_keyValuePairs8 = String8(
            reinterpret_cast<const char16_t*>(c_keyValuePairs),
            env->GetStringLength(keyValuePairs));
        env->ReleaseStringCritical(keyValuePairs, c_keyValuePairs);
    }
    int status = check_AudioSystem_Command(AudioSystem::setParameters(c_keyValuePairs8));
    return (jint) status;
}

===》/frameworks/av/media/libaudioclient/AudioSystem.cpp

复制代码
status_t AudioSystem::setParameters(audio_io_handle_t ioHandle, const String8& keyValuePairs) {
    const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
    if (af == 0) return PERMISSION_DENIED;
    return af->setParameters(ioHandle, keyValuePairs);
}

获取AudioFlinger的binder对象,调用AudioFlinger的setParameters方法

==》/frameworks/av/services/audioflinger/AudioFlinger.cpp

复制代码
status_t AudioFlinger::setParameters(audio_io_handle_t ioHandle, const String8& keyValuePairs)
{
    // check calling permissions
    if (!settingsAllowed()) {
        return PERMISSION_DENIED;
    }
    String8 filteredKeyValuePairs = keyValuePairs;
    filterReservedParameters(filteredKeyValuePairs, IPCThreadState::self()->getCallingUid());

    // AUDIO_IO_HANDLE_NONE means the parameters are global to the audio hardware interface
    if (ioHandle == AUDIO_IO_HANDLE_NONE) {
        Mutex::Autolock _l(mLock);
        // result will remain NO_INIT if no audio device is present
        status_t final_result = NO_INIT;
        {
            AutoMutex lock(mHardwareLock);
            mHardwareStatus = AUDIO_HW_SET_PARAMETER;
            for (size_t i = 0; i < mAudioHwDevs.size(); i++) {
                sp<DeviceHalInterface> dev = mAudioHwDevs.valueAt(i)->hwDevice();
                status_t result = dev->setParameters(filteredKeyValuePairs);
                // return success if at least one audio device accepts the parameters as not all
                // HALs are requested to support all parameters. If no audio device supports the
                // requested parameters, the last error is reported.
                if (final_result != NO_ERROR) {
                    final_result = result;
                }
            }
            mHardwareStatus = AUDIO_HW_IDLE;
        }
        // disable AEC and NS if the device is a BT SCO headset supporting those pre processings
        AudioParameter param = AudioParameter(filteredKeyValuePairs);
        String8 value;
        if (param.get(String8(AudioParameter::keyBtNrec), value) == NO_ERROR) {
            bool btNrecIsOff = (value == AudioParameter::valueOff);
            if (mBtNrecIsOff.exchange(btNrecIsOff) != btNrecIsOff) {
                for (size_t i = 0; i < mRecordThreads.size(); i++) {
                    mRecordThreads.valueAt(i)->checkBtNrec();
                }
            }
        }
        String8 screenState;
        if (param.get(String8(AudioParameter::keyScreenState), screenState) == NO_ERROR) {
            bool isOff = (screenState == AudioParameter::valueOff);
            if (isOff != (AudioFlinger::mScreenState & 1)) {
                AudioFlinger::mScreenState = ((AudioFlinger::mScreenState & ~1) + 2) | isOff;
            }
        }
        return final_result;
    }

    // hold a strong ref on thread in case closeOutput() or closeInput() is called
    // and the thread is exited once the lock is released
    sp<ThreadBase> thread;
    {
        Mutex::Autolock _l(mLock);
        thread = checkPlaybackThread_l(ioHandle);
        if (thread == 0) {
            thread = checkRecordThread_l(ioHandle);
            if (thread == 0) {
                thread = checkMmapThread_l(ioHandle);
            }
        } else if (thread == primaryPlaybackThread_l()) {
            // indicate output device change to all input threads for pre processing
            AudioParameter param = AudioParameter(filteredKeyValuePairs);
            int value;
            if ((param.getInt(String8(AudioParameter::keyRouting), value) == NO_ERROR) &&
                    (value != 0)) {
                broadcastParametersToRecordThreads_l(filteredKeyValuePairs);
            }
        }
    }
    if (thread != 0) {
        status_t result = thread->setParameters(filteredKeyValuePairs);
        forwardParametersToDownstreamPatches_l(thread->id(), filteredKeyValuePairs);
        return result;
    }
    return BAD_VALUE;
}

如果ioHandle是AUDIO_IO_HANDLE_NONE,该参数表示音频参数操作不针对某个具体的音频流或设备句柄,而是作用于整个音频硬件接口。本文仅考虑此类情况(thread->setParameters调用过程有差异,但到HAL层的最终调用是一样的),进一步调用到DeviceHalInterface.setParameters。

DeviceHalAidl和DeviceHalHidl都继承了DeviceHalInterface方法,一个是基于AIDL,一个是基于HIDL,本文是基于安卓14的源码,用的是依然是HIDL(见FactoryHal.cpp中sAudioHALVersions 的定义,并未把AIDL放开),HIDL和AIDL的区别以及HAL的版本迭代会在后续的篇章中单独具体的说明。

复制代码
//frameworks/av/media/libaudiohal/FactoryHal.cpp
/**
 * Supported HAL versions, from most recent to least recent.
 * This list need to keep sync with AudioHalVersionInfo.VERSIONS in
 * media/java/android/media/AudioHalVersionInfo.java.
 */
static const std::array<AudioHalVersionInfo, 5> sAudioHALVersions = {
    // TODO: remove this comment to get AIDL
    // AudioHalVersionInfo(AudioHalVersionInfo::Type::AIDL, 1, 0),
    AudioHalVersionInfo(AudioHalVersionInfo::Type::HIDL, 7, 1),
    AudioHalVersionInfo(AudioHalVersionInfo::Type::HIDL, 7, 0),
    AudioHalVersionInfo(AudioHalVersionInfo::Type::HIDL, 6, 0),
    AudioHalVersionInfo(AudioHalVersionInfo::Type::HIDL, 5, 0),
    AudioHalVersionInfo(AudioHalVersionInfo::Type::HIDL, 4, 0),
};

===》/frameworks/av/media/libaudiohal/impl/DeviceHalHidl.cpp

复制代码
status_t DeviceHalHidl::setParameters(const String8& kvPairs) {
    TIME_CHECK();
    if (mDevice == 0) return NO_INIT;
    hidl_vec<ParameterValue> hidlParams;
    status_t status = parametersFromHal(kvPairs, &hidlParams);
    if (status != OK) return status;
    // TODO: change the API so that context and kvPairs are separated
    return processReturn("setParameters",
                         utils::setParameters(mDevice, {} /* context */, hidlParams));
}

===》frameworks/av/media/libaudiohal/impl/ParameterUtils.h

复制代码
template <class T>
Return<Result> setParameters(T& object, hidl_vec<ParameterValue> /*context*/,
                             hidl_vec<ParameterValue> keys) {
    return object->setParameters(keys);
}

这是个模板函数,最终调用mDevice->setParameters(keys)

mDevice是sp<::android::hardware::audio::CPP_VERSION::IDevice>类型,是在DeviceHalHidl构造函数中初始化的。

+++++++++++++++++++++++++中断分割线++++++++++++++++++++++++++++++++++

我们先来看下mDevice是怎么初始化的?

在AudioFlinger 加载hw模块时会调用DevicesFactoryHal打开设备:

复制代码
AudioHwDevice* AudioFlinger::loadHwModule_l(const char *name)
{
    .....
    sp<DeviceHalInterface> dev;
    int rc = mDevicesFactoryHal->openDevice(name, &dev);
    if (rc) {
        return nullptr;
    }
    ........
}

===》/frameworks/av/media/libaudiohal/impl/DevicesFactoryHalHidl.cpp

复制代码
status_t DevicesFactoryHalHidl::openDevice(const char *name, sp<DeviceHalInterface> *device) {
    auto factories = copyDeviceFactories();
    if (factories.empty()) return NO_INIT;
    status_t status;
    auto hidlId = idFromHal(name, &status);
    if (status != OK) return status;
    Result retval = Result::NOT_INITIALIZED;
    for (const auto& factory : factories) {
        Return<void> ret;
        if (strcmp(name, AUDIO_HARDWARE_MODULE_ID_PRIMARY) == 0) {
            // In V7.1 it's not possible to cast IDevice back to IPrimaryDevice,
            // thus openPrimaryDevice must be used.
#if MAJOR_VERSION == 7 && MINOR_VERSION == 1
            ret = factory->openPrimaryDevice_7_1(
#else
            ret = factory->openPrimaryDevice(
#endif
                    [&](Result r,
                        const sp<::android::hardware::audio::CPP_VERSION::IPrimaryDevice>& result) {
                        retval = r;
                        if (retval == Result::OK) {
                            *device = new DeviceHalHidl(result);
                        }
                    });
        } else {
#if MAJOR_VERSION == 7 && MINOR_VERSION == 1
            ret = factory->openDevice_7_1(
#else
            ret = factory->openDevice(
#endif
                    hidlId,
                    [&](Result r,
                        const sp<::android::hardware::audio::CPP_VERSION::IDevice>& result) {
                        retval = r;
                        if (retval == Result::OK) {
                            *device = new DeviceHalHidl(result);
                        }
                    });
        }
        if (!ret.isOk()) return FAILED_TRANSACTION;
        switch (retval) {
            // Device was found and was initialized successfully.
            case Result::OK: return OK;
            // Device was found but failed to initalize.
            case Result::NOT_INITIALIZED: return NO_INIT;
            // Otherwise continue iterating.
            default: ;
        }
    }
    return BAD_VALUE;
}

先判断模块类型,本例中向AUDIO_HARDWARE_MODULE_ID_PRIMARY设备中设置参数;

再判断hal版本:

根据安卓14代码中Android.bp和frameworks/av/media/libaudiohal/FactoryHal.cpp中createHalService的实现,传入的是最近的hal版本,即libaudiohal用的是7.1版本,即MAJOR_VERSION = 7, MINOR_VERSION = 1

复制代码
bool createHalService(const AudioHalVersionInfo& version, bool isDevice, void** rawInterface) {
    const std::string libName = "libaudiohal@" + version.toVersionString() + ".so";
    handle = dlopen(libName.c_str(), dlMode);
     .......
}

cc_library_shared {
    name: "libaudiohal@7.1",
    defaults: [
        "latest_android_hardware_audio_core_sounddose_ndk_shared",
        "latest_android_hardware_audio_sounddose_ndk_shared",
        "libaudiohal_default",
        "libaudiohal_hidl_default"
    ],
    srcs: [
        ":audio_core_hal_client_sources",
        "EffectsFactoryHalEntry.cpp",
    ],
    static_libs: [
        "android.hardware.audio.common@7.0",
        "android.hardware.audio.common@7.0-util",
        "android.hardware.audio.common@7.1-enums",
        "android.hardware.audio.common@7.1-util",
        "android.hardware.audio.effect@7.0",
        "android.hardware.audio.effect@7.0-util",
        "android.hardware.audio@7.0",
        "android.hardware.audio@7.1",
        "android.hardware.audio@7.1-util",
        "libaudiohal.effect@7.0",
    ],
    shared_libs: [
        "libbinder_ndk",
    ],
    cflags: [
        "-DMAJOR_VERSION=7",
        "-DMINOR_VERSION=1",
        "-DCOMMON_TYPES_MINOR_VERSION=0",
        "-DCORE_TYPES_MINOR_VERSION=0",
        "-include common/all-versions/VersionMacro.h",
    ]
}

回到DevicesFactoryHalHidl.cpp openDevice中,最终调用factory->openPrimaryDevice_7_1,然后调用DeviceHalHidl的构造函数,传入::android::hardware::audio::CPP_VERSION::IPrimaryDevice类型的result,对应DeviceHalHidl 构造函数:

复制代码
DeviceHalHidl::DeviceHalHidl(
        const sp<::android::hardware::audio::CPP_VERSION::IPrimaryDevice>& device)
        : CoreConversionHelperHidl("DeviceHalHidl"),
#if MAJOR_VERSION <= 6 || (MAJOR_VERSION == 7 && MINOR_VERSION == 0)
          mDevice(device),
#endif
          mPrimaryDevice(device),
          mSoundDoseWrapper(std::make_unique<DeviceHalHidl::SoundDoseWrapper>()) {
#if MAJOR_VERSION == 7 && MINOR_VERSION == 1
    auto getDeviceRet = mPrimaryDevice->getDevice();
    if (getDeviceRet.isOk()) {
        mDevice = getDeviceRet;
    } else {
        ALOGE("Call to IPrimaryDevice.getDevice has failed: %s",
                getDeviceRet.description().c_str());
    }
#endif
}

传入的参数赋值给mPrimaryDevice,而mDevice根据mPrimaryDevice转化而来。

++++++++++++++++++++++++++中断结束+++++++++++++++++++++++

mDevice->setParameter会调用到hal层

===》hardware\interfaces\audio\core\all-versions\default\Device.cpp

复制代码
Return<Result> Device::setParameters(const hidl_vec<ParameterValue>& context,
                                     const hidl_vec<ParameterValue>& parameters) {
    return setParametersImpl(context, parameters);
}

===》/hardware/interfaces/audio/core/all-versions/default/ParametersUtil.cpp

复制代码
Result ParametersUtil::setParametersImpl(const hidl_vec<ParameterValue>& context,
                                         const hidl_vec<ParameterValue>& parameters) {
    AudioParameter params;
    for (auto& pair : context) {
        params.add(String8(pair.key.c_str()), String8(pair.value.c_str()));
    }
    for (size_t i = 0; i < parameters.size(); ++i) {
        params.add(String8(parameters[i].key.c_str()), String8(parameters[i].value.c_str()));
    }
    return setParams(params);
}

Result ParametersUtil::setParams(const AudioParameter& param) {
    int halStatus = halSetParameters(param.toString().string());
    return util::analyzeStatus(halStatus);
}

===》继续调用到hardware\interfaces\audio\core\all-versions\default\Device.cpp的halSetParameters

复制代码
int Device::halSetParameters(const char* keysAndValues) {
    return mDevice->set_parameters(mDevice, keysAndValues);
}

mDevice是audio_hw_device_t*类型,在hardware/libhardware/modules/audio/audio_hw.c中定义了 adev->device.set_parameters = adev_set_parameters;

而adev_set_parameters具体的实现是由第三方厂商实现的,一般在hardware/厂商/audio目录下。

至此,setParameters从APP到HAL的调用流程分析完成。

相关推荐
快点好好学习吧1 小时前
CPU 从 L1/L2 缓存读取 MySQL 代码指令的庖丁解牛
android·mysql·缓存
CYRUS STUDIO1 小时前
Frida 检测与对抗实战:进程、maps、线程、符号全特征清除
android·逆向·frida
恋猫de小郭2 小时前
Android CLI ,谷歌为 Android 开发者专研的 AI Agent,提速三倍
android·前端·flutter
守月满空山雪照窗2 小时前
Android CTS 深度解析:兼容性测试体系、架构与实践
android·架构
浮生世界2 小时前
Android 动态替换桌面 Logo 实践记录(`activity-alias`)
android
海天鹰2 小时前
字符串数组保存到Map使用避免超出范围崩溃
android
su_ym81103 小时前
Android 与 Linux 对比
android·linux·framework
默|笙3 小时前
【Linux】线程同步与互斥_日志与线程池
android·linux·运维
fengci.3 小时前
蜀道山2024上半部分
android