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.media.AudioAttributes; 19 import android.media.AudioAttributes.AttributeUsage; 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 32 /** 33 * Builds dynamic audio routing in a car from audio zone configuration. 34 */ 35 final class CarAudioDynamicRouting { 36 // For legacy stream type based volume control. 37 // Values in STREAM_TYPES and STREAM_TYPE_USAGES should be aligned. 38 static final int[] STREAM_TYPES = new int[] { 39 AudioManager.STREAM_MUSIC, 40 AudioManager.STREAM_ALARM, 41 AudioManager.STREAM_RING 42 }; 43 static final int[] STREAM_TYPE_USAGES = new int[] { 44 AudioAttributes.USAGE_MEDIA, 45 AudioAttributes.USAGE_ALARM, 46 AudioAttributes.USAGE_NOTIFICATION_RINGTONE 47 }; 48 setupAudioDynamicRouting(AudioPolicy.Builder builder, SparseArray<CarAudioZone> carAudioZones)49 static void setupAudioDynamicRouting(AudioPolicy.Builder builder, 50 SparseArray<CarAudioZone> carAudioZones) { 51 for (int i = 0; i < carAudioZones.size(); i++) { 52 CarAudioZone zone = carAudioZones.valueAt(i); 53 for (CarVolumeGroup group : zone.getVolumeGroups()) { 54 setupAudioDynamicRoutingForGroup(group, builder); 55 } 56 } 57 } 58 59 /** 60 * Enumerates all physical buses in a given volume group and attach the mixing rules. 61 * @param group {@link CarVolumeGroup} instance to enumerate the buses with 62 * @param builder {@link AudioPolicy.Builder} to attach the mixing rules 63 */ setupAudioDynamicRoutingForGroup(CarVolumeGroup group, AudioPolicy.Builder builder)64 private static void setupAudioDynamicRoutingForGroup(CarVolumeGroup group, 65 AudioPolicy.Builder builder) { 66 // Note that one can not register audio mix for same bus more than once. 67 for (String address : group.getAddresses()) { 68 boolean hasContext = false; 69 CarAudioDeviceInfo info = group.getCarAudioDeviceInfoForAddress(address); 70 AudioFormat mixFormat = new AudioFormat.Builder() 71 .setSampleRate(info.getSampleRate()) 72 .setEncoding(info.getEncodingFormat()) 73 .setChannelMask(info.getChannelCount()) 74 .build(); 75 AudioMixingRule.Builder mixingRuleBuilder = new AudioMixingRule.Builder(); 76 for (int carAudioContext : group.getContextsForAddress(address)) { 77 hasContext = true; 78 int[] usages = CarAudioContext.getUsagesForContext(carAudioContext); 79 for (int usage : usages) { 80 AudioAttributes attributes = buildAttributesWithUsage(usage); 81 mixingRuleBuilder.addRule(attributes, 82 AudioMixingRule.RULE_MATCH_ATTRIBUTE_USAGE); 83 } 84 if (Log.isLoggable(CarLog.TAG_AUDIO, Log.DEBUG)) { 85 Log.d(CarLog.TAG_AUDIO, String.format( 86 "Address: %s AudioContext: %s sampleRate: %d channels: %d usages: %s", 87 address, carAudioContext, info.getSampleRate(), info.getChannelCount(), 88 Arrays.toString(usages))); 89 } 90 } 91 if (hasContext) { 92 // It's a valid case that an audio output address is defined in 93 // audio_policy_configuration and no context is assigned to it. 94 // In such case, do not build a policy mix with zero rules. 95 AudioMix audioMix = new AudioMix.Builder(mixingRuleBuilder.build()) 96 .setFormat(mixFormat) 97 .setDevice(info.getAudioDeviceInfo()) 98 .setRouteFlags(AudioMix.ROUTE_FLAG_RENDER) 99 .build(); 100 builder.addMix(audioMix); 101 } 102 } 103 } 104 buildAttributesWithUsage(@ttributeUsage int usage)105 private static AudioAttributes buildAttributesWithUsage(@AttributeUsage int usage) { 106 AudioAttributes.Builder attributesBuilder = new AudioAttributes.Builder(); 107 if (AudioAttributes.isSystemUsage(usage)) { 108 attributesBuilder.setSystemUsage(usage); 109 } else { 110 attributesBuilder.setUsage(usage); 111 } 112 return attributesBuilder.build(); 113 } 114 } 115