Qidi 2017.10.20 (Markdown & Haroopad)
Audio 模块中专门为 TV 产品添加了一些代码,需要在 hdmi 的 HAL 代码中进行调用以完成某些功能。
在 Android 7.1 中的做法是在 hdmi 的 HAL 层链接上 audio 的 .so 库,然后在代码里直接调用 audio 的函数接口实现相应的操作。
audio 模块中为 TV 产品添加的代码中含有对音频设备节点的操作,而 Android 8.0 对权限的要求更加严格了,audio 的设备节点只可由 audio HAL 来进行操作。这一点可以从 system/sepolicy/public/hal_audio.te
文件中的如下语句看出:
# Only audio HAL may directly access the audio hardware
neverallow { halserverdomain -hal_audio_server } audio_device:chr_file *;
首先将 hdmi HAL 要调用的 audio 接口函数所在的 .so 链接到最基本的 audio HAL 对应的动态库 lib.primay.vendorName.so
中(其它平台上这个 .so 文件的名字也可能是别的,比如 audio.primary.default.so)。以我的项目为例,这个应该被链接的动态库是 libTVaudio.so
,对应 audio HAL 目录下的 Android.mk
文件作如下改动:
LOCAL_C_INCLUDES += \
external/tinyalsa/include \
system/media/audio_utils/include \
system/media/audio_effects/include \
system/media/audio_route/include \
frameworks/av/media/libeffects/lvm/lib/StereoWidening/lib \ frameworks/av/media/libeffects/lvm/lib/StereoWidening/src \
frameworks/av/media/libeffects/lvm/lib/Common/lib \
frameworks/av/media/libeffects/lvm/lib/Common/src \
+ libTVaudio \
libTVaudio/audio
LOCAL_STATIC_LIBRARIES += libmusicbundle
LOCAL_SHARED_LIBRARIES := \
- liblog libcutils libtinyalsa \
+ libTVaudio liblog libcutils libtinyalsa \
libaudioutils libdl libaudioroute libutils \
- libaudiospdif
+ libaudiospdif libmedia libmedia_helper \
+ libhardware libaudioclient libbinder
LOCAL_MODULE_TAGS := optional
include $(BUILD_SHARED_LIBRARY)
然后在 hdmi HAL 里通过 AudioSystem::setParameters()
函数调用来向 audio HAL 发送一个自定义的音频参数,比如 tvAudio=44100
(如果是调用其它模块的接口函数或对文件节点进行操作,那么应该使用其它模块对应的 service 对象,而不应该再是 AudioSystem)。示例代码如下:
//添加引用必要的头文件
#include
#include
#include "audio_amaudio.h"
//省略....
//添加调用接口
static int tv_audio_open(char *sample_rate)
{
ALOGD("Qidi - use AudioSystem::setParameters() to open amaudio function.");
AudioParameter param = AudioParameter();
String8 value = String8(sample_rate);
String8 key = String8("tvAudio");
param.add(key, value);
String8 keyValuePairs = param.toString();
if ( AudioSystem::setParameters(AUDIO_IO_HANDLE_NONE, keyValuePairs) == NO_ERROR ) {
ALOGD("Qidi - tv_hal set amaudio parameter successfully.");
return 0;
}
return -EINVAL;
}
static int tv_audio_close()
{
ALOGD("Qidi - use AudioSystem::setParameters() to close amaudio function.");
AudioParameter param = AudioParameter();
String8 value = String8("off");
String8 key = String8("tvAudio");
param.add(key, value);
String8 keyValuePairs = param.toString();
if ( AudioSystem::setParameters(AUDIO_IO_HANDLE_NONE, keyValuePairs) == NO_ERROR ) {
ALOGD("Qidi - tv_hal set amaudio parameter successfully.");
return 0;
} else {
ALOGE("Qidi - tv_hal set amaudio parameter failed.");
return -EINVAL;
}
}
#define AUDIO_48k 48000
static int tv_input_open_stream(struct tv_input_device *dev, int device_id,
tv_stream_t *stream)
{
if ( dev ) {
if (get_hdmi_stream(stream) != 0) {
return -EINVAL;
}
if ( NORMAL_STREAM_ID == stream->stream_id ) {
WriteSysfs(HDMI_ENABLE_SYSFS, HDMI_ENABLE);
if (tv_audio_open(AUDIO_48k) == 0) {
ALOGD("Qidi - tv_audio_open() is called successfully!\n");
return 0;
}
}
}
ALOGE("Qidi - tv_hal set amaudio parameter failed.");
return -EINVAL;
}
static int tv_input_close_stream(struct tv_input_device *dev, int device_id,
int stream_id)
{
if ( dev ) {
if ( NORMAL_STREAM_ID == stream_id ) {
if (tv_audio_close() != 0)
return -EINVAL;
WriteSysfs(HDMI_ENABLE_SYSFS, HDMI_DISABLE);
return 0;
}
......
}
接着在 audio HAL 中添加上对相应音频参数的处理逻辑即可。如下方代码所示:
//添加引用必要的头文件
#include "libTVaudio/audio_amaudio.h"
//省略...
//添加外部函数声明
extern int amAudioOpen(unsigned int sr, int input_device, int output_device);
extern int amAudioClose(void);
//省略...
//adev_set_parameters() 中添加对应参数处理
#if 1
// handle tvAudio invoke request from hdmi HAL in the method of
// AudioSystem::setParameters()
int sr_value = 0;
char *end;
ret = str_parms_get_str(parms, "tvAudio", value, sizeof(value));
if (ret >= 0) {
ALOGD("Qidi - %s() is handling tvAudio parameter: amaudio = %s",
__FUNCTION__, value);
if (strcmp(value, AUDIO_PARAMETER_VALUE_OFF) == 0) {
tvAudioClose();
} else {
if (str_parms_get_int(parms, "tvAudio", &sr_value)) {
ALOGE("Qidi - get tvAudio sample_rate failed!\n");
return 0;
}
if (sr_value >= 8000 && sr_value <= 48000) {
tvAudioOpen((unsigned int)sr_value,
CC_IN_USE_SPDIF_DEVICE, CC_OUT_USE_ALSA);
} else
ALOGE("Qidi - tvAudio sample_rate invalid!\n");
}
}
#endif
当然,最后还要在 hdmi HAL 模块的 Android.mk 中链接上调用 audio HAL 的 .so
库文件,即 libTVaudio.so
,和调用时要用到的其它库文件。如下所示:
LIB_VENDOR := $(wildcard vendor/vendorName)
LOCAL_C_INCLUDES += \
- $(LIB_VENDOR)/frameworks/services
+ $(GRALLOC_DIR) \
+ hardware/vendorName/audio/libTVaudio
LOCAL_MODULE_RELATIVE_PATH := hw
-LOCAL_SHARED_LIBRARIES := libcutils liblog libsystemcontrolservice libutils libbinder libui libhardware
+LOCAL_SHARED_LIBRARIES := libcutils liblog libutils libui libhardware libTVaudio libbinder libaudioclient \
+ libdl libmedia libmedia_helper
LOCAL_SRC_FILES := tv_input.cpp
LOCAL_MODULE := tv_input.vendorName
LOCAL_MODULE_TAGS := optional
+ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 26 && echo OK),OK)
+ LOCAL_PROPRIETARY_MODULE := true
+endif
include $(BUILD_SHARED_LIBRARY)
通过这种方式,去操作音频设备节点的对象从原来的 hdmi HAL 变成了 audio HAL,规避了没有操作权限的问题。