1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.car.audio; 17 18 import android.car.builtin.util.Slogf; 19 import android.media.AudioAttributes; 20 import android.media.AudioFormat; 21 import android.media.AudioManager; 22 import android.media.audiopolicy.AudioMix; 23 import android.media.audiopolicy.AudioMixingRule; 24 import android.media.audiopolicy.AudioPolicy; 25 import android.util.Log; 26 import android.util.SparseArray; 27 28 import com.android.car.CarLog; 29 30 import java.util.Arrays; 31 import java.util.List; 32 33 /** 34 * Builds dynamic audio routing in a car from audio zone configuration. 35 */ 36 final class CarAudioDynamicRouting { 37 // For legacy stream type based volume control. 38 // Values in STREAM_TYPES and STREAM_TYPE_USAGES should be aligned. 39 static final int[] STREAM_TYPES = new int[] { 40 AudioManager.STREAM_MUSIC, 41 AudioManager.STREAM_ALARM, 42 AudioManager.STREAM_RING 43 }; 44 static final int[] STREAM_TYPE_USAGES = new int[] { 45 AudioAttributes.USAGE_MEDIA, 46 AudioAttributes.USAGE_ALARM, 47 AudioAttributes.USAGE_NOTIFICATION_RINGTONE 48 }; 49 setupAudioDynamicRouting(AudioPolicy.Builder builder, SparseArray<CarAudioZone> carAudioZones, CarAudioContext carAudioContext)50 static void setupAudioDynamicRouting(AudioPolicy.Builder builder, 51 SparseArray<CarAudioZone> carAudioZones, CarAudioContext carAudioContext) { 52 for (int i = 0; i < carAudioZones.size(); i++) { 53 CarAudioZone zone = carAudioZones.valueAt(i); 54 for (CarVolumeGroup group : zone.getVolumeGroups()) { 55 setupAudioDynamicRoutingForGroup(group, builder, carAudioContext); 56 } 57 } 58 } 59 60 /** 61 * Enumerates all physical buses in a given volume group and attach the mixing rules. 62 * @param group {@link CarVolumeGroup} instance to enumerate the buses with 63 * @param builder {@link AudioPolicy.Builder} to attach the mixing rules 64 * @param carAudioContext car audio context 65 */ setupAudioDynamicRoutingForGroup(CarVolumeGroup group, AudioPolicy.Builder builder, CarAudioContext carAudioContext)66 private static void setupAudioDynamicRoutingForGroup(CarVolumeGroup group, 67 AudioPolicy.Builder builder, CarAudioContext carAudioContext) { 68 // Note that one can not register audio mix for same bus more than once. 69 List<String> addresses = group.getAddresses(); 70 for (int index = 0; index < addresses.size(); index++) { 71 String address = addresses.get(index); 72 boolean hasContext = false; 73 CarAudioDeviceInfo info = group.getCarAudioDeviceInfoForAddress(address); 74 AudioFormat mixFormat = new AudioFormat.Builder() 75 .setSampleRate(info.getSampleRate()) 76 .setEncoding(info.getEncodingFormat()) 77 .setChannelMask(info.getChannelCount()) 78 .build(); 79 AudioMixingRule.Builder mixingRuleBuilder = new AudioMixingRule.Builder(); 80 List<Integer> contextIdsForAddress = group.getContextsForAddress(address); 81 for (int contextIndex = 0; contextIndex < contextIdsForAddress.size(); contextIndex++) { 82 @CarAudioContext.AudioContext int contextId = 83 contextIdsForAddress.get(contextIndex); 84 hasContext = true; 85 AudioAttributes[] allAudioAttributes = 86 carAudioContext.getAudioAttributesForContext(contextId); 87 for (int attrIndex = 0; attrIndex < allAudioAttributes.length; attrIndex++) { 88 AudioAttributes attributes = allAudioAttributes[attrIndex]; 89 mixingRuleBuilder.addRule(attributes, 90 AudioMixingRule.RULE_MATCH_ATTRIBUTE_USAGE); 91 } 92 if (Slogf.isLoggable(CarLog.TAG_AUDIO, Log.DEBUG)) { 93 Slogf.d(CarLog.TAG_AUDIO, "Address: %s AudioContext: %s sampleRate: %d " 94 + "channels: %d attributes: %s", address, carAudioContext, 95 info.getSampleRate(), info.getChannelCount(), 96 Arrays.toString(allAudioAttributes)); 97 } 98 } 99 if (hasContext) { 100 // It's a valid case that an audio output address is defined in 101 // audio_policy_configuration and no context is assigned to it. 102 // In such case, do not build a policy mix with zero rules. 103 AudioMix audioMix = new AudioMix.Builder(mixingRuleBuilder.build()) 104 .setFormat(mixFormat) 105 .setDevice(info.getAudioDeviceInfo()) 106 .setRouteFlags(AudioMix.ROUTE_FLAG_RENDER) 107 .build(); 108 builder.addMix(audioMix); 109 } 110 } 111 } 112 } 113