• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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 
17 package com.android.car.audio;
18 
19 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.PRIVATE_CONSTRUCTOR;
20 
21 import android.annotation.Nullable;
22 import android.car.builtin.media.AudioManagerHelper;
23 import android.car.builtin.util.Slogf;
24 import android.media.AudioAttributes;
25 import android.media.audiopolicy.AudioProductStrategy;
26 import android.media.audiopolicy.AudioVolumeGroup;
27 import android.util.SparseArray;
28 
29 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport;
30 import com.android.internal.util.Preconditions;
31 
32 import java.util.List;
33 import java.util.Objects;
34 
35 /**
36  * Helper for audio related operations for core audio routing and volume management
37  * based on {@link AudioProductStrategy} and {@link AudioVolumeGroup}
38  */
39 final class CoreAudioHelper {
40     static final String TAG = "CoreAudioHelper";
41 
42     private static final boolean DEBUG = false;
43 
44     @ExcludeFromCodeCoverageGeneratedReport(reason = PRIVATE_CONSTRUCTOR)
CoreAudioHelper()45     private CoreAudioHelper() {
46         throw new UnsupportedOperationException("CoreAudioHelper class is non-instantiable, "
47                 + "contains static members only");
48     }
49 
50     /** Invalid strategy id returned when none matches a given request. */
51     static final int INVALID_STRATEGY = -1;
52 
53     /** Invalid group id returned when none matches a given request. */
54     static final int INVALID_GROUP_ID = -1;
55 
56     /**
57      * Default {@link AudioAttributes} used to identify the default {@link AudioProductStrategy}
58      * and {@link AudioVolumeGroup}.
59      */
60     static final AudioAttributes DEFAULT_ATTRIBUTES = new AudioAttributes.Builder().build();
61 
getAudioProductStrategies()62     private static List<AudioProductStrategy> getAudioProductStrategies() {
63         return AudioManagerWrapper.getAudioProductStrategies();
64     }
getAudioVolumeGroups()65     private static List<AudioVolumeGroup> getAudioVolumeGroups() {
66         return AudioManagerWrapper.getAudioVolumeGroups();
67     }
68 
69     /**
70      * Identifies the {@link AudioProductStrategy} supporting the given {@link AudioAttributes}.
71      *
72      * @param attributes {@link AudioAttributes} to look for.
73      * @return the id of the {@link AudioProductStrategy} supporting the
74      * given {@link AudioAttributes} if found, {@link #INVALID_STRATEGY} id otherwise.
75      */
getStrategyForAudioAttributes(AudioAttributes attributes)76     public static int getStrategyForAudioAttributes(AudioAttributes attributes) {
77         Preconditions.checkNotNull(attributes, "Audio Attributes can not be null");
78         for (int index = 0; index < getAudioProductStrategies().size(); index++) {
79             AudioProductStrategy strategy = getAudioProductStrategies().get(index);
80             if (strategy.supportsAudioAttributes(attributes)) {
81                 return strategy.getId();
82             }
83         }
84         return INVALID_STRATEGY;
85     }
86 
getStrategyForContextName(String contextName)87     public static int getStrategyForContextName(String contextName) {
88         Preconditions.checkNotNull(contextName, "Context name can not be null");
89         for (int index = 0; index < getAudioProductStrategies().size(); index++) {
90             AudioProductStrategy strategy = getAudioProductStrategies().get(index);
91             if (Objects.equals(strategy.getName(), contextName)) {
92                 return strategy.getId();
93             }
94         }
95         return INVALID_STRATEGY;
96     }
97 
98     /**
99      * Identifies the {@link AudioProductStrategy} supporting the given {@link AudioAttributes}
100      * and fallbacking on the default strategy supporting {@code DEFAULT_ATTRIBUTES} otherwise.
101      *
102      * @param attributes {@link AudioAttributes} supported by the
103      * {@link AudioProductStrategy} to look for.
104      * @return the id of the {@link AudioProductStrategy} supporting the
105      * given {@link AudioAttributes}, otherwise the id of the default strategy, aka the
106      * strategy supporting {@code DEFAULT_ATTRIBUTES}, {@code INVALID_STRATEGY} id otherwise.
107      */
getStrategyForAudioAttributesOrDefault(AudioAttributes attributes)108     public static int getStrategyForAudioAttributesOrDefault(AudioAttributes attributes) {
109         int strategyId = getStrategyForAudioAttributes(attributes);
110         return strategyId == INVALID_STRATEGY
111                 ? getStrategyForAudioAttributes(DEFAULT_ATTRIBUTES) : strategyId;
112     }
113 
114     @Nullable
getProductStrategyForAudioAttributes( AudioAttributes attributes)115     static AudioProductStrategy getProductStrategyForAudioAttributes(
116             AudioAttributes attributes) {
117         Preconditions.checkNotNull(attributes, "Audio attributes can not be null");
118         for (int index = 0; index < getAudioProductStrategies().size(); index++) {
119             AudioProductStrategy strategy = getAudioProductStrategies().get(index);
120             if (!strategy.supportsAudioAttributes(attributes)) {
121                 continue;
122             }
123             return strategy;
124         }
125         return null;
126     }
127 
128     /**
129      * Gets the {@link AudioProductStrategy} referred by its unique identifier.
130      *
131      * @param strategyId id of the {@link AudioProductStrategy} to look for
132      * @return the {@link AudioProductStrategy} referred by the given id if found, {@code null}
133      * otherwise.
134      */
135     @Nullable
getStrategy(int strategyId)136     public static AudioProductStrategy getStrategy(int strategyId) {
137         for (int index = 0; index < getAudioProductStrategies().size(); index++) {
138             AudioProductStrategy strategy = getAudioProductStrategies().get(index);
139             if (strategy.getId() == strategyId) {
140                 return strategy;
141             }
142         }
143         return null;
144     }
145 
146     /**
147      * Checks if the {@link AudioProductStrategy} referred by it id is the default.
148      *
149      * @param strategyId to look for
150      * @return {@code true} if the {@link AudioProductStrategy} referred by
151      * its id is the default, aka supports {@code DEFAULT_ATTRIBUTES}, {@code false} otherwise.
152      */
isDefaultStrategy(int strategyId)153     public static boolean isDefaultStrategy(int strategyId) {
154         for (int index = 0; index < getAudioProductStrategies().size(); index++) {
155             AudioProductStrategy strategy = getAudioProductStrategies().get(index);
156             if (strategy.getId() == strategyId) {
157                 return strategy.supportsAudioAttributes(DEFAULT_ATTRIBUTES);
158             }
159         }
160         return false;
161     }
162 
163     /**
164      * Gets the {@link AudioVolumeGroup} referred by it name.
165      *
166      * @param groupName name of the {@link AudioVolumeGroup} to look for.
167      * @return the {@link AudioVolumeGroup} referred by the given id if found, {@code null}
168      * otherwise.
169      */
170     @Nullable
getVolumeGroup(String groupName)171     public static AudioVolumeGroup getVolumeGroup(String groupName) {
172         for (int index = 0; index < getAudioVolumeGroups().size(); index++) {
173             AudioVolumeGroup group = getAudioVolumeGroups().get(index);
174             if (DEBUG) {
175                 Slogf.d(TAG, "requested %s has %s,", groupName, group);
176             }
177             if (group.name().equals(groupName)) {
178                 return group;
179             }
180         }
181         return null;
182     }
183 
184     /**
185      * Gets the most representative {@link AudioAttributes} of a given {@link AudioVolumeGroup}
186      * referred by it s name.
187      * <p>When relying on core audio to control volume, Volume APIs are based on AudioAttributes,
188      * thus, selecting the most representative attributes (not default without tag, with tag as
189      * fallback, {@link #DEFAULT_ATTRIBUTES} otherwise) will help identify the request.
190      *
191      * @param groupName name of the {@link AudioVolumeGroup} to look for.
192      * @return the best {@link AudioAttributes} for a given volume group id,
193      * {@link #DEFAULT_ATTRIBUTES} otherwise.
194      */
selectAttributesForVolumeGroupName(String groupName)195     public static AudioAttributes selectAttributesForVolumeGroupName(String groupName) {
196         AudioVolumeGroup group = getVolumeGroup(groupName);
197         AudioAttributes bestAttributes = DEFAULT_ATTRIBUTES;
198         if (group == null) {
199             return bestAttributes;
200         }
201         for (int index = 0; index < group.getAudioAttributes().size(); index++) {
202             AudioAttributes attributes = group.getAudioAttributes().get(index);
203             // bestAttributes attributes are not default and without tag (most generic as possible)
204             if (!attributes.equals(DEFAULT_ATTRIBUTES)) {
205                 bestAttributes = attributes;
206                 if (Objects.equals(AudioManagerHelper.getFormattedTags(attributes), "")) {
207                     break;
208                 }
209             }
210         }
211         return bestAttributes;
212     }
213 
214     /**
215      * Gets the name of the {@link AudioVolumeGroup} supporting given {@link AudioAttributes},
216      * {@code null} is returned if none is found.
217      *
218      * @param attributes {@link AudioAttributes} supported by the group to look for.
219      *
220      * @return the name of the {@link AudioVolumeGroup} supporting the given audio attributes,
221      * {@code null} otherwise.
222      */
223     @Nullable
getVolumeGroupNameForAudioAttributes(AudioAttributes attributes)224     public static String getVolumeGroupNameForAudioAttributes(AudioAttributes attributes) {
225         Preconditions.checkNotNull(attributes, "Audio Attributes can not be null");
226         int volumeGroupId = getVolumeGroupIdForAudioAttributes(attributes);
227         return volumeGroupId != AudioVolumeGroup.DEFAULT_VOLUME_GROUP
228                 ? getVolumeGroupNameFromCoreId(volumeGroupId) : null;
229     }
230 
231     /**
232      * Gets the name of the {@link AudioVolumeGroup} referred by its id.
233      *
234      * @param coreGroupId id of the volume group to look for.
235      * @return the volume group id referred by its name if found, throws an exception otherwise.
236      */
237     @Nullable
getVolumeGroupNameFromCoreId(int coreGroupId)238     public static String getVolumeGroupNameFromCoreId(int coreGroupId) {
239         List<AudioVolumeGroup> audioVolumeGroups =
240                 AudioManagerWrapper.getAudioVolumeGroups();
241         SparseArray<String> groupIdToNames = new SparseArray<>() {
242             {
243                 for (int index = 0; index < audioVolumeGroups.size(); index++) {
244                     AudioVolumeGroup group = audioVolumeGroups.get(index);
245                     put(group.getId(), group.name());
246                 }
247             }
248         };
249         return groupIdToNames.get(coreGroupId);
250     }
251 
252     /**
253      * Gets the {@link AudioVolumeGroup} id associated to the given {@link AudioAttributes}.
254      *
255      * @param attributes {@link AudioAttributes} to be considered
256      * @return the id of the {@link AudioVolumeGroup} supporting the given {@link AudioAttributes}
257      * if found, {@link #INVALID_GROUP_ID} otherwise.
258      */
getVolumeGroupIdForAudioAttributes(AudioAttributes attributes)259     public static int getVolumeGroupIdForAudioAttributes(AudioAttributes attributes) {
260         Preconditions.checkNotNull(attributes, "Audio Attributes can not be null");
261         for (int index = 0; index < getAudioProductStrategies().size(); index++) {
262             AudioProductStrategy strategy = getAudioProductStrategies().get(index);
263             int volumeGroupId =
264                     AudioManagerHelper.getVolumeGroupIdForAudioAttributes(strategy, attributes);
265             Slogf.d(TAG, "getVolumeGroupIdForAudioAttributes %s %s,", volumeGroupId, strategy);
266             if (volumeGroupId != AudioVolumeGroup.DEFAULT_VOLUME_GROUP) {
267                 return volumeGroupId;
268             }
269         }
270         return INVALID_GROUP_ID;
271     }
272 }
273