• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 static android.media.AudioAttributes.USAGE_MEDIA;
19 
20 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.PRIVATE_CONSTRUCTOR;
21 
22 import android.car.builtin.util.Slogf;
23 import android.media.AudioAttributes;
24 import android.media.AudioFormat;
25 import android.media.AudioManager;
26 import android.media.audiopolicy.AudioMix;
27 import android.media.audiopolicy.AudioMixingRule;
28 import android.media.audiopolicy.AudioPolicy;
29 import android.util.Log;
30 import android.util.SparseArray;
31 
32 import com.android.car.CarLog;
33 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport;
34 
35 import java.util.Arrays;
36 import java.util.List;
37 
38 /**
39  * Builds dynamic audio routing in a car from audio zone configuration.
40  */
41 final class CarAudioDynamicRouting {
42     // For legacy stream type based volume control.
43     // Values in STREAM_TYPES and STREAM_TYPE_USAGES should be aligned.
44     static final int[] STREAM_TYPES = new int[] {
45             AudioManager.STREAM_MUSIC,
46             AudioManager.STREAM_ALARM,
47             AudioManager.STREAM_RING
48     };
49     static final int[] STREAM_TYPE_USAGES = new int[] {
50             USAGE_MEDIA,
51             AudioAttributes.USAGE_ALARM,
52             AudioAttributes.USAGE_NOTIFICATION_RINGTONE
53     };
54 
setupAudioDynamicRouting(AudioPolicy.Builder builder, SparseArray<CarAudioZone> carAudioZones, CarAudioContext carAudioContext)55     static void setupAudioDynamicRouting(AudioPolicy.Builder builder,
56             SparseArray<CarAudioZone> carAudioZones, CarAudioContext carAudioContext) {
57         for (int i = 0; i < carAudioZones.size(); i++) {
58             List<CarAudioZoneConfig> zoneConfigs =
59                     carAudioZones.valueAt(i).getAllCarAudioZoneConfigs();
60             for (int configIndex = 0; configIndex < zoneConfigs.size(); configIndex++) {
61                 setupAudioDynamicRoutingForZoneConfig(builder, zoneConfigs.get(configIndex),
62                         carAudioContext);
63             }
64         }
65     }
66 
setupAudioDynamicRoutingForZoneConfig(AudioPolicy.Builder builder, CarAudioZoneConfig zoneConfig, CarAudioContext carAudioContext)67     private static void setupAudioDynamicRoutingForZoneConfig(AudioPolicy.Builder builder,
68             CarAudioZoneConfig zoneConfig, CarAudioContext carAudioContext) {
69         CarVolumeGroup[] volumeGroups = zoneConfig.getVolumeGroups();
70         for (int index = 0; index < volumeGroups.length; index++) {
71             setupAudioDynamicRoutingForGroup(builder, volumeGroups[index], carAudioContext);
72         }
73     }
74 
75     /**
76      * Enumerates all physical buses in a given volume group and attach the mixing rules.
77      * @param builder {@link AudioPolicy.Builder} to attach the mixing rules
78      * @param group {@link CarVolumeGroup} instance to enumerate the buses with
79      * @param carAudioContext car audio context
80      */
setupAudioDynamicRoutingForGroup(AudioPolicy.Builder builder, CarVolumeGroup group, CarAudioContext carAudioContext)81     private static void setupAudioDynamicRoutingForGroup(AudioPolicy.Builder builder,
82             CarVolumeGroup group, CarAudioContext carAudioContext) {
83         // Note that one can not register audio mix for same bus more than once.
84         List<String> addresses = group.getAddresses();
85         for (int index = 0; index < addresses.size(); index++) {
86             String address = addresses.get(index);
87             boolean hasContext = false;
88             CarAudioDeviceInfo info = group.getCarAudioDeviceInfoForAddress(address);
89             if (!info.canBeRoutedWithDynamicPolicyMix()) {
90                 if (Slogf.isLoggable(CarLog.TAG_AUDIO, Log.DEBUG)) {
91                     Slogf.d(CarLog.TAG_AUDIO, "Address: %s AudioContext: %s cannot be routed with "
92                             + "Dynamic Policy Mixing", address, carAudioContext);
93                 }
94                 continue;
95             }
96             AudioFormat mixFormat = createMixFormatFromDevice(info);
97             AudioMixingRule.Builder mixingRuleBuilder = new AudioMixingRule.Builder();
98             List<Integer> contextIdsForAddress = group.getContextsForAddress(address);
99             for (int contextIndex = 0; contextIndex < contextIdsForAddress.size(); contextIndex++) {
100                 @CarAudioContext.AudioContext int contextId =
101                         contextIdsForAddress.get(contextIndex);
102                 hasContext = true;
103                 AudioAttributes[] allAudioAttributes =
104                         carAudioContext.getAudioAttributesForContext(contextId);
105                 for (int attrIndex = 0; attrIndex < allAudioAttributes.length; attrIndex++) {
106                     AudioAttributes attributes = allAudioAttributes[attrIndex];
107                     mixingRuleBuilder.addRule(attributes,
108                             AudioMixingRule.RULE_MATCH_ATTRIBUTE_USAGE);
109                 }
110                 if (Slogf.isLoggable(CarLog.TAG_AUDIO, Log.DEBUG)) {
111                     Slogf.d(CarLog.TAG_AUDIO, "Address: %s AudioContext: %s sampleRate: %d "
112                             + "channels: %d attributes: %s", address, carAudioContext,
113                             info.getSampleRate(), info.getChannelCount(),
114                             Arrays.toString(allAudioAttributes));
115                 }
116             }
117             if (hasContext) {
118                 // It's a valid case that an audio output address is defined in
119                 // audio_policy_configuration and no context is assigned to it.
120                 // In such case, do not build a policy mix with zero rules.
121                 addMix(builder, info, mixFormat, mixingRuleBuilder);
122             }
123         }
124     }
125 
126     @ExcludeFromCodeCoverageGeneratedReport(reason = PRIVATE_CONSTRUCTOR)
CarAudioDynamicRouting()127     private CarAudioDynamicRouting() {
128         throw new UnsupportedOperationException("contains only static methods");
129     }
130 
setupAudioDynamicRoutingForMirrorDevice( AudioPolicy.Builder mirrorPolicyBuilder, List<CarAudioDeviceInfo> audioDeviceInfos)131     public static void setupAudioDynamicRoutingForMirrorDevice(
132             AudioPolicy.Builder mirrorPolicyBuilder, List<CarAudioDeviceInfo> audioDeviceInfos) {
133         for (int index = 0; index < audioDeviceInfos.size(); index++) {
134             AudioFormat mixFormat = createMixFormatFromDevice(audioDeviceInfos.get(index));
135             AudioMixingRule.Builder mixingRuleBuilder = new AudioMixingRule.Builder();
136             mixingRuleBuilder.addRule(CarAudioContext.getAudioAttributeFromUsage(USAGE_MEDIA),
137                     AudioMixingRule.RULE_MATCH_ATTRIBUTE_USAGE);
138 
139             addMix(mirrorPolicyBuilder, audioDeviceInfos.get(index), mixFormat, mixingRuleBuilder);
140         }
141     }
142 
createMixFormatFromDevice(CarAudioDeviceInfo mirrorDevice)143     private static AudioFormat createMixFormatFromDevice(CarAudioDeviceInfo mirrorDevice) {
144         AudioFormat mixFormat = new AudioFormat.Builder()
145                 .setSampleRate(mirrorDevice.getSampleRate())
146                 .setEncoding(mirrorDevice.getEncodingFormat())
147                 .setChannelMask(mirrorDevice.getChannelCount())
148                 .build();
149         return mixFormat;
150     }
151 
addMix(AudioPolicy.Builder mirrorPolicyBuilder, CarAudioDeviceInfo mirrorDevice, AudioFormat mixFormat, AudioMixingRule.Builder mixingRuleBuilder)152     private static void addMix(AudioPolicy.Builder mirrorPolicyBuilder,
153             CarAudioDeviceInfo mirrorDevice, AudioFormat mixFormat,
154             AudioMixingRule.Builder mixingRuleBuilder) {
155         AudioMix audioMix = new AudioMix.Builder(mixingRuleBuilder.build())
156                 .setFormat(mixFormat)
157                 .setDevice(mirrorDevice.getAudioDeviceInfo())
158                 .setRouteFlags(AudioMix.ROUTE_FLAG_RENDER)
159                 .build();
160         mirrorPolicyBuilder.addMix(audioMix);
161     }
162 }
163