1、HIDL 的概念

  HIDL 读作 hide-l,全称是 Hardware Interface Definition Language。它在 Android Project Treble 中被起草,在 Android 8.0 中被全面使用,其诞生目的是使 Android 可以在不重新编译 HAL 的情况下对 Framework 进行 OTA 升级。 
  使用 HIDL 描述的 HAL 描述文件替换旧的用头文件描述的 HAL 文件的过程称为 * HAL 的 binder 化(binderization)。所有运行 Android O 的设备都必须只支持 binder 化后的 HAL 模块。 
  已发布的 HIDL package包位于 Android 代码库的hardware/interfaces/vendor/<vendorName>目录下。使用 HDIL 描述的 HAL 接口存放在这些目录下的.hal文件中。比如我们可以在hardware/interfaces/audio/2.0/目录下找到部分 Audio HAL 描述文件,如下:

  1. Android.bp
  2. Android.mk
  3. IDevice.hal
  4. IDevicesFactory.hal
  5. IPrimaryDevice.hal
  6. IStream.hal
  7. IStreamIn.hal
  8. IStreamOutCallback.hal
  9. IStreamOut.hal
  10. types.hal

另外在frameworks/av/media/下多了个文件夹  libaudiohal :

  1. Android.mk DeviceHalLocal.h DevicesFactoryHalLocal.h EffectHalHidl.h EffectsFactoryHalLocal.h StreamHalLocal.h
  2. ConversionHelperHidl.cpp DevicesFactoryHalHidl.cpp EffectBufferHalHidl.cpp EffectHalLocal.cpp HalDeathHandlerHidl.cpp
  3. ConversionHelperHidl.h DevicesFactoryHalHidl.h EffectBufferHalHidl.h EffectHalLocal.h include
  4. DeviceHalHidl.cpp DevicesFactoryHalHybrid.cpp EffectBufferHalLocal.cpp EffectsFactoryHalHidl.cpp StreamHalHidl.cpp
  5. DeviceHalHidl.h DevicesFactoryHalHybrid.h EffectBufferHalLocal.h EffectsFactoryHalHidl.h StreamHalHidl.h
  6. DeviceHalLocal.cpp DevicesFactoryHalLocal.cpp EffectHalHidl.cpp EffectsFactoryHalLocal.cpp StreamHalLocal.cpp

从文件名命名方式来看,一类是以Hidl结尾,一类是Local结尾,Local结尾的应该是兼容之前的方式,即谷歌在文档里描述的:

HIDL 旨在用于进程间通信 (IPC)。进程之间的通信经过 Binder 化。对于必须与进程相关联的代码库,还可以使用直通模式(在 Java 中不受支持):

  要将运行早期版本的 Android 的设备更新为使用 Android O,您可以将惯用的(和旧版)HAL 封装在一个新 HIDL 接口中,该接口将在绑定式模式和同进程(直通)模式提供 HAL。这种封装对于 HAL 和 Android 框架来说都是透明的。直通模式仅适用于 C++ 客户端和实现。运行早期版本的 Android 的设备没有用 Java 编写的 HAL,因此 Java HAL 自然而然经过 Binder 化。

直通式标头文件:

  编译 .hal 文件时,除了用于 Binder 通信的标头之外,hidl-gen 还会生成一个额外的直通标头文件 BsFoo.h;此标头定义了会被执行 dlopen 操作的函数。由于直通式 HAL 在它们被调用的同一进程中运行,因此在大多数情况下,直通方法由直接函数调用(同一线程)来调用。oneway 方法在各自的线程中运行,因为它们不需要等待 HAL 来处理它们(这意味着,在直通模式下使用 oneway 方法的所有 HAL 对于线程必须是安全的)。

  如果有一个 IFoo.halBsFoo.h 会封装 HIDL 生成的方法,以提供额外的功能(例如使 oneway 事务在其他线程中运行)。该文件类似于 BpFoo.h,不过,所需函数是直接调用的,并未使用 Binder 传递调用 IPC。未来,HAL 的实现可能提供多种实现结果,例如 FooFast HAL 和 FooAccurate HAL。在这种情况下,系统会针对每个额外的实现结果创建一个文件(例如 PTFooFast.cpp 和 PTFooAccurate.cpp)。

2、Audio Record 调用分析:

(1) Java层调用Android的SDK中的API实例化一个AudioRecord对象

  -》 \android-8.0.0_r4\frameworks\av\media\libaudioclient\AudioRecord.cpp  -》  AudioRecord::AudioRecord

(2) 设置相应参数

  -》 mStatus = set(inputSource, sampleRate, format, channelMask, frameCount, cbf, user,
                    notificationFrames, false /*threadCanCallJava*/, sessionId, transferType, flags,
                    uid, pid, pAttributes);

(3)打开录音接口

 -》 // create the IAudioRecord
        status_t status = openRecord_l(0 /*epoch*/, mOpPackageName);

(4) 获取输入设备属性:

    status = AudioSystem::getInputForAttr(&mAttributes, &input,
                                        mSessionId,
                                        // FIXME compare to AudioTrack
                                        mClientPid,
                                        mClientUid,
                                        &config,
                                        mFlags, mSelectedDeviceId, &mPortId);

     其中是通过获取audio_policy_service建立binder接口:

     // establish binder interface to AudioPolicy service
     const sp<IAudioPolicyService> AudioSystem::get_audio_policy_service()

(5) 调用AudioPolicyManager的接口 : \android-8.0.0_r4\frameworks\av\services\audiopolicy\managerdefault\AudioPolicyManager.cpp

   aps->getInputForAttr -》 AudioPolicyManager::getInputForAttr

(6) 根据app传下的参数获取对应的设备类型:

  device = getDeviceAndMixForInputSource(inputSource, &policyMix);

  -》Engine::getDeviceForInputSource      (\android-8.0.0_r4\frameworks\av\services\audiopolicy\enginedefault\src\Engine.cpp)

一般录音软件是:AUDIO_SOURCE_MIC  ,google 语音引擎是:AUDIO_SOURCE_VOICE_RECOGNITION

   再根据当前系统支持的输入设备返回对应的录音设备:(默认的内置mic就是 AUDIO_DEVICE_IN_BUILTIN_MIC)

  1. if (mForceUse[AUDIO_POLICY_FORCE_FOR_RECORD] == AUDIO_POLICY_FORCE_BT_SCO &&
  2. availableDeviceTypes & AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET) {
  3. device = AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET;
  4. } else if (availableDeviceTypes & AUDIO_DEVICE_IN_WIRED_HEADSET) {
  5. device = AUDIO_DEVICE_IN_WIRED_HEADSET;
  6. } else if (availableDeviceTypes & AUDIO_DEVICE_IN_USB_HEADSET) {
  7. device = AUDIO_DEVICE_IN_USB_HEADSET;
  8. } else if (availableDeviceTypes & AUDIO_DEVICE_IN_USB_DEVICE) {
  9. device = AUDIO_DEVICE_IN_USB_DEVICE;
  10. } else if (availableDeviceTypes & AUDIO_DEVICE_IN_BUILTIN_MIC) {
  11. device = AUDIO_DEVICE_IN_BUILTIN_MIC;
  12. }

PS:通过 dumpsys media.audio_policy 指令查看当前系统所支持的设备模块及类型。

可通过\android-8.0.0_r4\frameworks\av\services\audiopolicy目录里面 audio_policy.conf或者audio_policy_configuration.xml配置设备加载,

使用.conf还是.xml取决于frameworks/av/services/audiopolicy/Android.mk 通过宏USE_XML_AUDIO_POLICY_CONF - 1 : 使用 .xml , 0: 使用 .conf。

(7) 根据返回的device类型获取对应录音设备:

  1.     *input = getInputForDevice(device, address, session, uid, inputSource,
  2.    config->sample_rate, config->format, config->channel_mask, flags,
  3.    policyMix);

(8) 调用getInputProfile函数根据传进来的声音采样率、声音格式、通道掩码等参数与获得的设备支持的Input Profile比较返回一个与设备Profile匹配的IOProfile-》

  1.      profile = getInputProfile(device, address,
  2.     profileSamplingRate, profileFormat, profileChannelMask,
  3.      profileFlags);

(9) 根据返回的profile调用初始化时加载好的client接口:

  1. status_t status = mpClientInterface->openInput(profile->getModuleHandle(),
  2. &input,
  3. &config,
  4. &device,
  5. address,
  6. halInputSource,
  7. profileFlags);

PS: mpClientInterface是由AudioPolicyService中加载对应模块传递过来:

  1. void AudioPolicyService::onFirstRef()
  2. {
  3. {
  4. Mutex::Autolock _l(mLock);
  5.  
  6. // start tone playback thread
  7. mTonePlaybackThread = new AudioCommandThread(String8("ApmTone"), this);
  8. // start audio commands thread
  9. mAudioCommandThread = new AudioCommandThread(String8("ApmAudio"), this);
  10. // start output activity command thread
  11. mOutputCommandThread = new AudioCommandThread(String8("ApmOutput"), this);
  12.  
  13. mAudioPolicyClient = new AudioPolicyClient(this);
  14. mAudioPolicyManager = createAudioPolicyManager(mAudioPolicyClient); //对应的模块加载放在AudioPolicyManager的构造函数中
  15. }
  16. // load audio processing modules
  17. sp<AudioPolicyEffects>audioPolicyEffects = new AudioPolicyEffects();
  18. {
  19. Mutex::Autolock _l(mLock);
  20. mAudioPolicyEffects = audioPolicyEffects;
  21. }
  22. }

AudioPolicyManager构造函数中根据宏定义判断通过audio_policy.conf还是audio_policy_configuration.xml解析加载对应的so:

  1. #ifdef USE_XML_AUDIO_POLICY_CONF
  2. mVolumeCurves = new VolumeCurvesCollection();
  3. AudioPolicyConfig config(mHwModules, mAvailableOutputDevices, mAvailableInputDevices,
  4. mDefaultOutputDevice, speakerDrcEnabled,
  5. static_cast<VolumeCurvesCollection *>(mVolumeCurves));
  6. if (deserializeAudioPolicyXmlConfig(config) != NO_ERROR) {
  7. #else
  8. mVolumeCurves = new StreamDescriptorCollection();
  9. AudioPolicyConfig config(mHwModules, mAvailableOutputDevices, mAvailableInputDevices,
  10. mDefaultOutputDevice, speakerDrcEnabled);
  11. if ((ConfigParsingUtils::loadConfig(AUDIO_POLICY_VENDOR_CONFIG_FILE, config) != NO_ERROR) &&
  12. (ConfigParsingUtils::loadConfig(AUDIO_POLICY_CONFIG_FILE, config) != NO_ERROR)) {
  13. #endif

加载模块:

  1. mHwModules[i]->mHandle = mpClientInterface->loadHwModule(mHwModules[i]->getName());
  2. if (mHwModules[i]->mHandle == ) {
  3. ALOGW("could not open HW module %s", mHwModules[i]->getName());
  4. continue;
  5. }

具体加载过程:

AudioFlinger::loadHwModule_l

-》mDevicesFactoryHal->openDevice(name, &dev);

  -》DevicesFactoryHalHidl::openDevice(const char *name, sp<DeviceHalInterface> *device)

    -》DevicesFactory::openDevice(IDevicesFactory::Device device, openDevice_cb _hidl_cb) //对应hardware/interfaces/audio/2.0/目录下IDevicesFactory.hal所描述接口

      -》loadAudioInterface(moduleName, &halDevice);

        -》DevicesFactory::loadAudioInterface(const char *if_name, audio_hw_device_t **dev)

  1.   //加载so,后面最终通过dlopen加载了/system/lib/hw/下对应的so。
      rc = hw_get_module_by_class(AUDIO_HARDWARE_MODULE_ID, if_name, &mod);
  2. if (rc) {
  3. ALOGE("%s couldn't load audio hw module %s.%s (%s)", __func__,
  4. AUDIO_HARDWARE_MODULE_ID, if_name, strerror(-rc));
  5. goto out;
  6. }
       //实际调用到了audio_hw.c中adev_open(),只会被调用一次,也就是给硬件模块中的函数指针赋值open()。
  7. rc = audio_hw_device_open(mod, dev);
  8. if (rc) {
  9. ALOGE("%s couldn't open audio hw device in %s.%s (%s)", __func__,
  10. AUDIO_HARDWARE_MODULE_ID, if_name, strerror(-rc));
  11. goto out;
  12. }
  13. if ((*dev)->common.version < AUDIO_DEVICE_API_VERSION_MIN) {
  14. ALOGE("%s wrong audio hw device version %04x", __func__, (*dev)->common.version);
  15. rc = -EINVAL;
  16. audio_hw_device_close(*dev);
  17. goto out;
  18. }

(10) 再回到AudioPolicyManager::getInputForDevice 中的 mpClientInterface->openInput调用流程:

    AudioFlinger::openInput -》

      AudioFlinger::openInput_l -》

          sp<DeviceHalInterface> inHwHal = inHwDev->hwDevice();

           ...

            inHwHal->openInputStream -》

            DeviceHalHidl::openInputStream

  1. Return<void> ret = mDevice->openInputStream(
  2. handle,
  3. hidlDevice,
  4. hidlConfig,
  5. AudioInputFlag(flags),
  6. AudioSource(source),
  7. [&](Result r, const sp<IStreamIn>& result, const AudioConfig& suggestedConfig) {
  8. retval = r;
  9. if (retval == Result::OK) {
  10. *inStream = new StreamInHalHidl(result); //audio_hw.c中adev_open_input_stream的参数stream_in在这里创建并传入
  11. }
  12. HidlUtils::audioConfigToHal(suggestedConfig, config);
  13. });
  14. return processReturn("openInputStream", ret, retval);

-》最终调用到了audio_hw.c中的:

  1. static int adev_open_input_stream(struct audio_hw_device *dev,
  2. audio_io_handle_t handle,
  3. audio_devices_t devices,
  4. struct audio_config *config,
  5. struct audio_stream_in **stream_in,
  6. audio_input_flags_t flags,
  7. const char *address __unused,
  8. audio_source_t source )

adev_open_input_stream中对sp<StreamInHalInterface> *inStream指针的各成员进行赋值,进一步调用底层的接口获取音频数据。

PS:默认使用上层传下来的config参数,也可以手动更新stream_in的参数,比如 采样率:stream_in->config.rate,自己实现数据获取接口:stream_in->stream.read = my_read 等;

-end-

Android : android 8.0 audio 接口分析的更多相关文章

  1. Android 匿名共享内存Java接口分析

    在Android 匿名共享内存驱动源码分析中介绍了匿名共享内存的驱动实现过程,本文在Android匿名共享内存驱动基础上,介绍Android匿名共享内存对外Android系统的匿名共享内存子系统的主体 ...

  2. Android 匿名共享内存C++接口分析

    在上一篇Android 匿名共享内存C接口分析中介绍了Android系统的匿名共享内存C语言访问接口,本文在前文的基础上继续介绍Android系统的匿名共享内存提供的C++访问接口.在C++层通过引入 ...

  3. Android Retrofit 2.0 使用-补充篇

    推荐阅读,猛戳: 1.Android MVP 实例 2.Android Retrofit 2.0使用 3.RxJava 4.RxBus 5.Android MVP+Retrofit+RxJava实践小 ...

  4. [Android]Android端ORM框架——RapidORM(v2.0)

    以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/5626716.html [Android]Android端ORM ...

  5. Android Studio 2.0使用指南

    一.下载界面.[无激活码 无序列码 无毒请放心使用][需将JAVA程序升级到1.8] 网址:http://www.android-studio.org/index.php/download/andro ...

  6. Android Studio 1.0.2项目实战——从一个APP的开发过程认识Android Studio

    Android Studio 1.0.1刚刚发布不久,谷歌紧接着发布了Android Studio 1.0.2版本,和1.0.0一样,是一个Bug修复版本.在上一篇Android Studio 1.0 ...

  7. Android Studio 1.0.1 + Genymotion安卓模拟器打造高效安卓开发环境

    我们开发安卓大多是使用Eclipse和安卓SDK中自带的安卓模拟器.当然,Google早就推出了自己的安卓开发环境——Android studio,在不久前,Google发布了Android Stud ...

  8. [Android] 环境配置之正式版Android Studio 1.0

    昨天看见 Android Studio 1.0 正式版本发布了:心里挺高兴的. 算是忠实用户了吧,从去年开发者大会一开始出现 AS 后就开始使用了:也是从那时开始就基本没有用过 Eclipse 了:一 ...

  9. [Android] android studio 2.0即时运行功能探秘

    即时运行instant Run是android studio 2中,开发人员最关心的特性之一 在google发布studio 2.0之后,马上更新体验了一把,然而发现,并没快多少,说好的即时运行呢? ...

随机推荐

  1. laravel配置路由除了 / 都是404解决办法

    1.php.ini开启phpopenssl 2.conf  (nginx为例) location / { index index.html index.htm index.php l.php; #tr ...

  2. [Spring] 04 Denpendency Injection

    DI Dependency Injection 依赖注入:从程序代码中移除依赖关系的一种设计模式. 这样就可以更容易地管理和测试应用程序. DI使我们的程序编码 loosely coupled.松耦合 ...

  3. 自定义广播(BroadcastReceiver)事件 --Android开发

    本例演示自定义广播事件.我们需要做的是,在主活动中写发送广播的代码,然后在接收广播的类中写接收广播的代码. 1.主活动中点击按钮后发送广播 MainActivity.java: public clas ...

  4. Java接口简单理解

    1.接口: 接口成员变量默认声明方式:public.static.final 接口成员方法默认声明方式:public.abstract public interface Interface_class ...

  5. 虚拟机linux 如何挂在U盘,NTFS格式如何挂载

    今天突发奇想,想挂载U盘到虚拟机的Centos 7 上,但是出了些问题,下边我就来说下linux挂在U盘的步骤 电脑插上U盘 win + R运行 services.msc 找到虚拟机的USB服务并运行 ...

  6. Anya and Cubes CodeForces - 525E (双端搜索)

    大意: 给定$n$元素序列$a$, 可以任选不超过$k$个$a_i$变换为$a_i!$, 求变换后任选若干元素和为S的方案数. 分成两块暴搜, 复杂度$O(3^{\frac{n}{2}})$ #inc ...

  7. vue项目 sockjs-node一直报错问题

    vue3下 vue.config.js中 devServer: { host: '0.0.0.0', port: 8080, proxy: { '/': { target: 'http://127.0 ...

  8. kohana task 编写计划任务

    kohana 框架  我们经常使用gleez作为我们二次开发. 收先我们要把文件建在Task文件夹下,比如新建文件为:testcron <?phpdefined('SYSPATH') or di ...

  9. CF-787D-线段树建图+最短路

    http://codeforces.com/problemset/problem/787/D 题目大意是给出一个有向图,有N个节点,初始节点在S,询问S到所有点最短路.边的读入方式有三种, 1 u v ...

  10. 借助python工具从word文件中抽取相关表的定义,最后组装建表语句-非常好

    借助python工具从word文件中抽取表的定义,最后组装建表语句-非常好 --如有转载请以超链接的方式注明原文章出处,谢谢大家.请尊重每一位乐于分享的原创者 1.python脚本 ## -*- co ...