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.Slog; 27 import android.util.SparseArray; 28 29 import com.android.car.CarLog; 30 31 import java.util.Arrays; 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)50 static void setupAudioDynamicRouting(AudioPolicy.Builder builder, 51 SparseArray<CarAudioZone> carAudioZones) { 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); 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 */ setupAudioDynamicRoutingForGroup(CarVolumeGroup group, AudioPolicy.Builder builder)65 private static void setupAudioDynamicRoutingForGroup(CarVolumeGroup group, 66 AudioPolicy.Builder builder) { 67 // Note that one can not register audio mix for same bus more than once. 68 for (String address : group.getAddresses()) { 69 boolean hasContext = false; 70 CarAudioDeviceInfo info = group.getCarAudioDeviceInfoForAddress(address); 71 AudioFormat mixFormat = new AudioFormat.Builder() 72 .setSampleRate(info.getSampleRate()) 73 .setEncoding(info.getEncodingFormat()) 74 .setChannelMask(info.getChannelCount()) 75 .build(); 76 AudioMixingRule.Builder mixingRuleBuilder = new AudioMixingRule.Builder(); 77 for (int carAudioContext : group.getContextsForAddress(address)) { 78 hasContext = true; 79 int[] usages = CarAudioContext.getUsagesForContext(carAudioContext); 80 for (int usage : usages) { 81 AudioAttributes attributes = buildAttributesWithUsage(usage); 82 mixingRuleBuilder.addRule(attributes, 83 AudioMixingRule.RULE_MATCH_ATTRIBUTE_USAGE); 84 } 85 if (Log.isLoggable(CarLog.TAG_AUDIO, Log.DEBUG)) { 86 Slog.d(CarLog.TAG_AUDIO, String.format( 87 "Address: %s AudioContext: %s sampleRate: %d channels: %d usages: %s", 88 address, carAudioContext, info.getSampleRate(), info.getChannelCount(), 89 Arrays.toString(usages))); 90 } 91 } 92 if (hasContext) { 93 // It's a valid case that an audio output address is defined in 94 // audio_policy_configuration and no context is assigned to it. 95 // In such case, do not build a policy mix with zero rules. 96 AudioMix audioMix = new AudioMix.Builder(mixingRuleBuilder.build()) 97 .setFormat(mixFormat) 98 .setDevice(info.getAudioDeviceInfo()) 99 .setRouteFlags(AudioMix.ROUTE_FLAG_RENDER) 100 .build(); 101 builder.addMix(audioMix); 102 } 103 } 104 } 105 buildAttributesWithUsage(@ttributeUsage int usage)106 private static AudioAttributes buildAttributesWithUsage(@AttributeUsage int usage) { 107 AudioAttributes.Builder attributesBuilder = new AudioAttributes.Builder(); 108 if (AudioAttributes.isSystemUsage(usage)) { 109 attributesBuilder.setSystemUsage(usage); 110 } else { 111 attributesBuilder.setUsage(usage); 112 } 113 return attributesBuilder.build(); 114 } 115 } 116