Android Audio分区——音频分区加载流程(三)

        前面文章介绍了车载多区音频基础,并且介绍了音频分区相关类及对应功能,这里我们就来看一下音频分区的解析过程。

一、音频分区加载

        音频分区的加载是在 CarAudioService 的初始化函数 init() 流程中进行的。

1、CarAudioService.java

源码位置:/packages/services/Car/service/src/com/android/car/audio/CarAudioService.java

init

@Override
public void init() {
    synchronized (mImplLock) {
        // 获取CarOccupantZoneService服务,该服务在车辆网中介绍过
        mOccupantZoneService = CarLocalServices.getService(CarOccupantZoneService.class);
        Car car = new Car(mContext, /* service= */null, /* handler= */ null);
        mOccupantZoneManager = new CarOccupantZoneManager(car, mOccupantZoneService);
        if (mUseDynamicRouting) {
            // 设置动态音频路由
            setupDynamicRoutingLocked();
            // 设置HAL音频焦点监听器
            setupHalAudioFocusListenerLocked();
        } else {
            // 动态路由为其用,设置传统模式下的音量变化监听器
            setupLegacyVolumeChangedListener();
        }

        // 恢复主静音状态
        if (mPersistMasterMuteState) {
            // 获取存储的主静音状态
            boolean storedMasterMute = mCarAudioSettings.getMasterMute();
            // 设置主静音状态
            setMasterMute(storedMasterMute, 0);
        }

        // 设置支持的系统用途
        mAudioManager.setSupportedSystemUsages(SYSTEM_USAGES);
    }
}

        这里主要是根据动态音频路由是否启用来进行不同的配置,确保了系统能够正确地处理音频输出和音频焦点的变化。 

setupDynamicRoutingLocked

private void setupDynamicRoutingLocked() {
    // 创建音频策略构建器
    final AudioPolicy.Builder builder = new AudioPolicy.Builder(mContext);
    builder.setLooper(Looper.getMainLooper());

    // 加载音频区域
    loadCarAudioZonesLocked();

    // 同步音频区域增益索引
    for (CarAudioZone zone : mCarAudioZones) {
        // 确保HAL获得初始值
        zone.synchronizeCurrentGainIndex();
    }

    // 设置动态路由规则
    final CarAudioDynamicRouting dynamicRouting = new CarAudioDynamicRouting(mCarAudioZones);
    dynamicRouting.setupAudioDynamicRouting(builder);

    // 设置音频策略音量回调
    builder.setAudioPolicyVolumeCallback(mAudioPolicyVolumeCallback);

    // 配置音频焦点处理
    if (sUseCarAudioFocus) {
        mFocusHandler = new CarZonesAudioFocus(mAudioManager, mContext.getPackageManager(),
                mCarAudioZones, mCarAudioSettings, ENABLE_DELAYED_AUDIO_FOCUS);
        builder.setAudioPolicyFocusListener(mFocusHandler);
        builder.setIsAudioFocusPolicy(true);
    }

    mAudioPolicy = builder.build();
    if (sUseCarAudioFocus) {
        // 连接音频策略和焦点监听器
        mFocusHandler.setOwningPolicy(this, mAudioPolicy);
    }

    // 注册音频策略
    int r = mAudioManager.registerAudioPolicy(mAudioPolicy);
    if (r != AudioManager.SUCCESS) {
        throw new RuntimeException("registerAudioPolicy failed " + r);
    }

    // 设置乘员区域信息
    setupOccupantZoneInfo();
}

        该方法用于设置动态音频路由,包括配置动态路由规则、设置音频策略、处理音频焦点事件等。这里我们主要看一下音频区的加载。

loadCarAudioZonesLocked

private CarAudioZone[] mCarAudioZones;
private String mCarAudioConfigurationPath;

private void loadCarAudioZonesLocked() {
    // 生成音频设备信息
    List<CarAudioDeviceInfo> carAudioDeviceInfos = generateCarAudioDeviceInfos();

    // 获取音频配置路径
    mCarAudioConfigurationPath = getAudioConfigurationPath();
    if (mCarAudioConfigurationPath != null) {
        // 从音频配置文件中加载音频区域
        mCarAudioZones = loadCarAudioConfigurationLocked(carAudioDeviceInfos);
    } else {
        // 从音量组配置中加载音频区域
        mCarAudioZones = loadVolumeGroupConfigurationWithAudioControlLocked(carAudioDeviceInfos);
    }

    // 验证加载的音频区域是否有效
    CarAudioZonesValidator.validate(mCarAudioZones);
}

        该方法用于加载所有可用的音频区域(CarAudioZone)。它首先生成音频设备信息,然后根据是否存在特定的音频配置路径来决定如何加载音频区域。这里我们先来看一下获取音频配置路径,然后再看音频区的加载流程。

getAudioConfigurationPath

private static final String[] AUDIO_CONFIGURATION_PATHS = new String[] {
        "/vendor/etc/car_audio_configuration.xml",
        "/system/etc/car_audio_configuration.xml"
};

private String getAudioConfigurationPath() {
    for (String path : AUDIO_CONFIGURATION_PATHS) {
        File configuration = new File(path);
        // 检查文件是否存在
        if (configuration.exists()) {
            return path;
        }
    }
    return null;
}

        可以看到这里查找流程,先找到 /vendor/etc/ 路径下是否存在 car_audio_configuration.xml 文件,如果存在直接返回,不存在则查找 /system/etc/ 下的对应文件。vendor 和 system 代表厂商定制和系统配置,而 car_audio_configuration.xml 文件正是我们前面介绍的车载音频配置文件。

loadCarAudioConfigurationLocked

private CarAudioZone[] loadCarAudioConfigurationLocked(List<CarAudioDeviceInfo> carAudioDeviceInfos) {
    // 获取所有输入设备
    AudioDeviceInfo[] inputDevices = getAllInputDevices();
    // 打开音频配置文件
    try (InputStream inputStream = new FileInputStream(mCarAudioConfigurationPath)) {
        // 创建CarAudioZonesHelper实例
        CarAudioZonesHelper zonesHelper = new CarAudioZonesHelper(mCarAudioSettings,
                inputStream, carAudioDeviceInfos, inputDevices);
        // 加载音频区域映射,获取音频区域ID到乘员区域ID的映射。
        mAudioZoneIdToOccupantZoneIdMapping = zonesHelper.getCarAudioZoneIdToOccupantZoneIdMapping();
        // 加载音频区域
        return zonesHelper.loadAudioZones();
    } catch (IOException | XmlPullParserException e) {
        throw new RuntimeException("Failed to parse audio zone configuration", e);
    }
}

        该方法用于从音频配置文件中加载所有可用的音频区域(CarAudioZone)。它首先读取音频配置文件的内容,然后使用 CarAudioZonesHelper 类的 loadAudioZones() 函数来解析配置文件并加载音频区域。

2、CarAudioZonesHelper

源码位置:/packages/services/Car/service/src/com/android/car/audio/CarAudioZonesHelper.java

loadAudioZones

CarAudioZone[] loadAudioZones() throws IOException, XmlPullParserException {
    List<CarAudioZone> carAudioZones = new ArrayList<>();
    // 解析音频区域
    parseCarAudioZones(carAudioZones, mInputStream);
    return carAudioZones.toArray(new CarAudioZone[0]);
}

        这里首先创建一个 ArrayList 来存储解析得到的音频区域,然后调用 parseCarAudioZones() 方法来解析输入流中的数据,并将结果转换为 CarAudioZone 数组返回。 

parseCarAudioZones

private static final String TAG_ROOT = "carAudioConfiguration";
private static final String TAG_AUDIO_ZONES = "zones";
private static final int INVALID_VERSION = -1;
private static final int SUPPORTED_VERSION_2 = 2;

private void parseCarAudioZones(List<CarAudioZone> carAudioZones, InputStream stream)
        throws XmlPullParserException, IOException {
    // 创建XML解析器
    final XmlPullParser parser = Xml.newPullParser();
    parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, NAMESPACE != null);
    parser.setInput(stream, null);

    // 跳过文档中的注释或空白
    parser.nextTag();
    // 确保当前元素是<carAudioConfiguration>
    parser.require(XmlPullParser.START_TAG, NAMESPACE, TAG_ROOT);

    // 获取XML文档中的版本号
    final int versionNumber = Integer.parseInt(parser.getAttributeValue(NAMESPACE, ATTR_VERSION));

    if (SUPPORTED_VERSIONS.get(versionNumber, INVALID_VERSION) == INVALID_VERSION) {
        throw new IllegalArgumentException("Latest Supported version:" + SUPPORTED_VERSION_2 + " , got version:" + versionNumber);
    }

    mCurrentVersion = versionNumber;

    // 遍历XML文档直到遇到根元素的结束标签
    while (parser.next() != XmlPullParser.END_TAG) {
        if (parser.getEventType() != XmlPullParser.START_TAG) continue;
        // 如果当前元素是<audioZones>,则调用parseAudioZones()方法解析音频区域。
        if (TAG_AUDIO_ZONES.equals(parser.getName())) {
            // 解析音频区域
            parseAudioZones(parser, carAudioZones);
        } else {
            // 跳过该元素及其子元素
            skip(parser);
        }
    }
}

        这里首先确保 XML 文档的根元素是 <carAudioConfiguration>,然后检查版本号是否支持,接着解析所有的音频区域配置。 

parseAudioZones

private static final String TAG_AUDIO_ZONE = "zone";

private void parseAudioZones(XmlPullParser parser, List<CarAudioZone> carAudioZones)
        throws XmlPullParserException, IOException {
    while (parser.next() != XmlPullParser.END_TAG) {
        if (parser.getEventType() != XmlPullParser.START_TAG) continue;
        if (TAG_AUDIO_ZONE.equals(parser.getName())) {
            carAudioZones.add(parseAudioZone(parser));
        } else {
            skip(parser);
        }
    }
    Preconditions.checkArgument(mHasPrimaryZone, "Requires one primary zone");
    carAudioZones.sort(Comparator.comparing(CarAudioZone::getId));
}

       与上面的解析流程基本相同,只是解析的标签不同,这里通过解析 <zone>  标签并将数据保存到 carAudioZones 中。

二、车载音频分区配置

        对于前面文章中的 xml 文件解析,是否注意到了对应的文件路径 "/vendor/etc/" 和 "/system/etc/",在源码中并未找到该路径,是不是有些疑惑。其实这是设备的内存路径,对应的源码文件并不在次,而是通过代码拷贝过去的。

1、system文件

源码位置:/device/google/trout/hal/audio/6.0/car_audio_configuration.xml

<!--
  定义汽车中的音频配置,包括
    - 音频区
    - 上下文到音频总线的映射
    - 音量组
  在汽车环境中。
-->
<carAudioConfiguration version="2">
    <zones>
        <zone name="primary zone" isPrimary="true" occupantZoneId="0">
            <volumeGroups>
                <group>
                    <device address="bus0_media_out">
                        <context context="music"/>
                    </device>
                    <device address="bus3_call_ring_out">
                        <context context="call_ring"/>
                    </device>
                    <device address="bus6_notification_out">
                        <context context="notification"/>
                    </device>
                    <device address="bus7_system_sound_out">
                        <context context="system_sound"/>
                        <context context="emergency"/>
                        <context context="safety"/>
                        <context context="vehicle_status"/>
                        <context context="announcement"/>
                    </device>
                </group>
                <group>
                    <device address="bus1_navigation_out">
                        <context context="navigation"/>
                    </device>
                    <device address="bus2_voice_command_out">
                        <context context="voice_command"/>
                    </device>
                </group>
                <group>
                    <device address="bus4_call_out">
                        <context context="call"/>
                    </device>
                </group>
                <group>
                    <device address="bus5_alarm_out">
                        <context context="alarm"/>
                    </device>
                </group>
            </volumeGroups>
        </zone>
        <zone name="rear seat zone 1" audioZoneId="1">
            <volumeGroups>
                <group>
                    <device address="bus100_audio_zone_1">
                        <context context="music"/>
                        <context context="navigation"/>
                        <context context="voice_command"/>
                        <context context="call_ring"/>
                        <context context="call"/>
                        <context context="alarm"/>
                        <context context="notification"/>
                        <context context="system_sound"/>
                        <context context="emergency"/>
                        <context context="safety"/>
                        <context context="vehicle_status"/>
                        <context context="announcement"/>
                    </device>
                </group>
            </volumeGroups>
        </zone>
        <zone name="rear seat zone 2"  audioZoneId="2">
            <volumeGroups>
                <group>
                    <device address="bus200_audio_zone_2">
                        <context context="music"/>
                        <context context="navigation"/>
                        <context context="voice_command"/>
                        <context context="call_ring"/>
                        <context context="call"/>
                        <context context="alarm"/>
                        <context context="notification"/>
                        <context context="system_sound"/>
                        <context context="emergency"/>
                        <context context="safety"/>
                        <context context="vehicle_status"/>
                        <context context="announcement"/>
                    </device>
                </group>
            </volumeGroups>
        </zone>
    </zones>
</carAudioConfiguration>

文件拷贝

 源码位置:/device/google/trout/aosp_trout_common.mk

LOCAL_AUDIO_PRODUCT_COPY_FILES ?= \
    device/google/trout/hal/audio/6.0/car_audio_configuration.xml:$(TARGET_COPY_OUT_VENDOR)/etc/car_audio_configuration.xml \

2、vendor文件

        对于 vendor 下的配置文件都是车企定制的配置,根据需要去设置分组及音频流等信息,并且同样需要在 mk 文件中进行拷贝操作,这里就不再展示代码了。

源码位置:device/{硬件平台}/audio/{产品}/config/audio/car_audio_configuration.xml

  • 9
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

c小旭

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值