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.BOILERPLATE_CODE;
20 
21 import static com.google.common.collect.Sets.newHashSet;
22 
23 import android.media.AudioAttributes;
24 import android.media.MediaRecorder;
25 import android.media.audiopolicy.AudioProductStrategy;
26 import android.media.audiopolicy.AudioVolumeGroup;
27 import android.os.Parcel;
28 
29 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport;
30 
31 import java.util.ArrayList;
32 import java.util.List;
33 
34 @ExcludeFromCodeCoverageGeneratedReport(reason = BOILERPLATE_CODE)
35 public final class CoreAudioRoutingUtils {
36 
37     public static final int MUSIC_MIN_INDEX = 0;
38     public static final int MUSIC_MAX_INDEX = 40;
39     public static final int MUSIC_AM_INIT_INDEX = 8;
40     public static final int NAV_MIN_INDEX = 5;
41     public static final int NAV_MAX_INDEX = 35;
42     public static final int OEM_MIN_INDEX = 1;
43     public static final int OEM_MAX_INDEX = 15;
44     public static final int MUSIC_CAR_GROUP_ID = 0;
45     public static final int OEM_CAR_GROUP_ID = 2;
46     public static final int NAV_CAR_GROUP_ID = 1;
47     public static final String MUSIC_DEVICE_ADDRESS = "MUSIC_DEVICE_ADDRESS";
48     public static final String NAV_DEVICE_ADDRESS = "NAV_DEVICE_ADDRESS";
49     public static final String OEM_DEVICE_ADDRESS = "OEM_DEVICE_ADDRESS";
50     public static final List<AudioVolumeGroup> VOLUME_GROUPS;
51     public static final List<AudioProductStrategy> PRODUCT_STRATEGIES;
52 
53     public static final AudioAttributes MUSIC_ATTRIBUTES = new AudioAttributes.Builder()
54             .setUsage(AudioAttributes.USAGE_MEDIA)
55             .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
56             .build();
57 
58     public static final AudioAttributes MOVIE_ATTRIBUTES = new AudioAttributes.Builder()
59             .setContentType(AudioAttributes.CONTENT_TYPE_MOVIE)
60             .build();
61     public static final int MUSIC_STRATEGY_ID = 777;
62     public static final String MUSIC_CONTEXT_NAME = "MUSIC_CONTEXT";
63     public static final int MUSIC_GROUP_ID = 666;
64     public static final String MUSIC_GROUP_NAME = "MUSIC_GROUP";
65     public static final AudioProductStrategy MUSIC_STRATEGY;
66     public static final AudioVolumeGroup MUSIC_GROUP;
67     public static final CarAudioContextInfo MEDIA_CONTEXT_INFO;
68 
69     public static final AudioAttributes NAV_ATTRIBUTES = new AudioAttributes.Builder()
70             .setUsage(AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE)
71             .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
72             .build();
73     public static final int NAV_STRATEGY_ID = 99;
74     public static final String NAV_CONTEXT_NAME = "NAV_CONTEXT";
75     public static final int NAV_GROUP_ID = 8;
76     public static final String NAV_GROUP_NAME = "NAV_GROUP";
77     public static final AudioProductStrategy NAV_STRATEGY;
78     public static final AudioVolumeGroup NAV_GROUP;
79     public static final CarAudioContextInfo NAV_CONTEXT_INFO;
80 
81     public static final AudioAttributes OEM_ATTRIBUTES;
82     public static final int OEM_STRATEGY_ID = 1979;
83     public static final String OEM_CONTEXT_NAME = "OEM_CONTEXT";
84     public static final int OEM_GROUP_ID = 55;
85     public static final String OEM_GROUP_NAME = "OEM_GROUP";
86     public static final String OEM_FORMATTED_TAGS = "oem=extension_1979";
87     public static final AudioProductStrategy OEM_STRATEGY;
88     public static final AudioVolumeGroup OEM_GROUP;
89     public static final CarAudioContextInfo OEM_CONTEXT_INFO;
90 
91     static final String INVALID_CONTEXT_NAME = "INVALID_CONTEXT";
92     static final int INVALID_STRATEGY_ID = 999999;
93     static final int INVALID_GROUP_ID = 999999;
94     static final String INVALID_GROUP_NAME = "INVALID_GROUP";
95 
96     static final int INVALID_STRATEGY = -1;
97 
98     static final AudioAttributes UNSUPPORTED_ATTRIBUTES = new AudioAttributes.Builder()
99             .setUsage(AudioAttributes.USAGE_GAME)
100             .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
101             .build();
102 
103     static {
104         OEM_ATTRIBUTES = new AudioAttributes.Builder()
105                 .setUsage(AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE)
106                 .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
107                 .replaceTags(newHashSet(OEM_FORMATTED_TAGS))
108                 .build();
109         // Note: constructors are private, use marshalling public API to generate the mocks
110         Parcel parcel = Parcel.obtain();
111         // marshall AudioProductStrategy data
112         parcel.writeString(MUSIC_CONTEXT_NAME);
113         parcel.writeInt(MUSIC_STRATEGY_ID);
114 
115         // nb attributes groups
116         parcel.writeInt(1);
117         parcel.writeInt(MUSIC_GROUP_ID);
118         // stream type
119         parcel.writeInt(0);
120         // nb attributes
121         parcel.writeInt(2);
122 
123         parcel.writeInt(AudioAttributes.USAGE_MEDIA);
124         parcel.writeInt(AudioAttributes.CONTENT_TYPE_MUSIC);
125         parcel.writeInt(MediaRecorder.AudioSource.AUDIO_SOURCE_INVALID);
126         parcel.writeInt(AudioAttributes.FLAG_MUTE_HAPTIC);
127         parcel.writeInt(AudioAttributes.FLATTEN_TAGS);
128         // mFormattedTags
129         parcel.writeString("");
130         // ATTR_PARCEL_IS_NULL_BUNDLE
131         parcel.writeInt(-1977);
132 
133         parcel.writeInt(AudioAttributes.USAGE_UNKNOWN);
134         parcel.writeInt(AudioAttributes.CONTENT_TYPE_UNKNOWN);
135         parcel.writeInt(MediaRecorder.AudioSource.AUDIO_SOURCE_INVALID);
136         parcel.writeInt(AudioAttributes.FLAG_MUTE_HAPTIC);
137         parcel.writeInt(AudioAttributes.FLATTEN_TAGS);
138         // mFormattedTags
139         parcel.writeString("");
140         // ATTR_PARCEL_IS_NULL_BUNDLE
141         parcel.writeInt(-1977);
142 
143         parcel.setDataPosition(0);
144         MUSIC_STRATEGY = AudioProductStrategy.CREATOR.createFromParcel(parcel);
parcel.recycle()145         parcel.recycle();
146 
147         parcel = Parcel.obtain();
148         // marshall AudioProductStrategy data
149         parcel.writeString(NAV_CONTEXT_NAME);
150         parcel.writeInt(NAV_STRATEGY_ID);
151 
152         // nb attributes groups
153         parcel.writeInt(1);
154         parcel.writeInt(NAV_GROUP_ID);
155         // stream type
156         parcel.writeInt(0);
157         // nb attributes
158         parcel.writeInt(2);
159 
160         parcel.writeInt(AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE);
161         parcel.writeInt(AudioAttributes.CONTENT_TYPE_SPEECH);
162         parcel.writeInt(MediaRecorder.AudioSource.AUDIO_SOURCE_INVALID);
163         parcel.writeInt(AudioAttributes.FLAG_MUTE_HAPTIC);
164         parcel.writeInt(AudioAttributes.FLATTEN_TAGS);
165         // mFormattedTags
166         parcel.writeString("");
167         // ATTR_PARCEL_IS_NULL_BUNDLE
168         parcel.writeInt(-1977);
169 
170         parcel.writeInt(AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE);
171         parcel.writeInt(AudioAttributes.CONTENT_TYPE_UNKNOWN);
172         parcel.writeInt(MediaRecorder.AudioSource.AUDIO_SOURCE_INVALID);
173         parcel.writeInt(AudioAttributes.FLAG_MUTE_HAPTIC);
174         parcel.writeInt(AudioAttributes.FLATTEN_TAGS);
175         // mFormattedTags
176         parcel.writeString("");
177         // ATTR_PARCEL_IS_NULL_BUNDLE
178         parcel.writeInt(-1977);
179 
180         parcel.setDataPosition(0);
181         NAV_STRATEGY = AudioProductStrategy.CREATOR.createFromParcel(parcel);
parcel.recycle()182         parcel.recycle();
183 
184         parcel = Parcel.obtain();
185         // marshall AudioProductStrategy data
186         parcel.writeString(OEM_CONTEXT_NAME);
187         parcel.writeInt(OEM_STRATEGY_ID);
188 
189         // nb attributes groups
190         parcel.writeInt(1);
191         parcel.writeInt(OEM_GROUP_ID);
192         // stream type
193         parcel.writeInt(0);
194         // nb attributes
195         parcel.writeInt(1);
196         parcel.writeInt(AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE);
197         parcel.writeInt(AudioAttributes.CONTENT_TYPE_SPEECH);
198         parcel.writeInt(MediaRecorder.AudioSource.AUDIO_SOURCE_INVALID);
199         parcel.writeInt(AudioAttributes.FLAG_MUTE_HAPTIC);
200         parcel.writeInt(AudioAttributes.FLATTEN_TAGS);
201         parcel.writeString(OEM_FORMATTED_TAGS);
202         // ATTR_PARCEL_IS_NULL_BUNDLE
203         parcel.writeInt(-1977);
204 
205         parcel.setDataPosition(0);
206         OEM_STRATEGY = AudioProductStrategy.CREATOR.createFromParcel(parcel);
parcel.recycle()207         parcel.recycle();
208 
209         // Order matters, put the default in the middle to check default strategy is not selected
210         // if not explicitly requested.
211         PRODUCT_STRATEGIES = List.of(OEM_STRATEGY, MUSIC_STRATEGY, NAV_STRATEGY);
212 
213         parcel = Parcel.obtain();
214         // marshall AudioVolumeGroup data
215         parcel.writeString(MUSIC_GROUP_NAME);
216         parcel.writeInt(MUSIC_GROUP_ID);
217 
218         // nb attributes
219         parcel.writeInt(2);
220         parcel.writeInt(AudioAttributes.USAGE_MEDIA);
221         parcel.writeInt(AudioAttributes.CONTENT_TYPE_MUSIC);
222         parcel.writeInt(MediaRecorder.AudioSource.AUDIO_SOURCE_INVALID);
223         parcel.writeInt(AudioAttributes.FLAG_MUTE_HAPTIC);
224         parcel.writeInt(AudioAttributes.FLATTEN_TAGS);
225         // mFormattedTags
226         parcel.writeString("");
227         // ATTR_PARCEL_IS_NULL_BUNDLE
228         parcel.writeInt(-1977);
229 
230         parcel.writeInt(AudioAttributes.USAGE_UNKNOWN);
231         parcel.writeInt(AudioAttributes.CONTENT_TYPE_UNKNOWN);
232         parcel.writeInt(MediaRecorder.AudioSource.AUDIO_SOURCE_INVALID);
233         parcel.writeInt(AudioAttributes.FLAG_MUTE_HAPTIC);
234         parcel.writeInt(AudioAttributes.FLATTEN_TAGS);
235         // mFormattedTags
236         parcel.writeString("");
237         // ATTR_PARCEL_IS_NULL_BUNDLE
238         parcel.writeInt(-1977);
239 
240         // nb stream types
241         parcel.writeInt(0);
242 
243         parcel.setDataPosition(0);
244         MUSIC_GROUP = AudioVolumeGroup.CREATOR.createFromParcel(parcel);
parcel.recycle()245         parcel.recycle();
246 
247         parcel = Parcel.obtain();
248         // marshall AudioProductStrategy data
249         parcel.writeString(NAV_GROUP_NAME);
250         parcel.writeInt(NAV_GROUP_ID);
251 
252         // nb attributes
253         parcel.writeInt(1);
254         parcel.writeInt(AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE);
255         parcel.writeInt(AudioAttributes.CONTENT_TYPE_SPEECH);
256         parcel.writeInt(MediaRecorder.AudioSource.AUDIO_SOURCE_INVALID);
257         parcel.writeInt(AudioAttributes.FLAG_MUTE_HAPTIC);
258         parcel.writeInt(AudioAttributes.FLATTEN_TAGS);
259         // mFormattedTags
260         parcel.writeString("");
261         // ATTR_PARCEL_IS_NULL_BUNDLE
262         parcel.writeInt(-1977);
263 
264         parcel.writeInt(/* nb stream types= */ 0);
265 
266         parcel.setDataPosition(0);
267         NAV_GROUP = AudioVolumeGroup.CREATOR.createFromParcel(parcel);
parcel.recycle()268         parcel.recycle();
269 
270         parcel = Parcel.obtain();
271         // marshall AudioProductStrategy data
272         parcel.writeString(OEM_GROUP_NAME);
273         parcel.writeInt(OEM_GROUP_ID);
274 
275         // nb attributes
276         parcel.writeInt(1);
277         parcel.writeInt(AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE);
278         parcel.writeInt(AudioAttributes.CONTENT_TYPE_SPEECH);
279         parcel.writeInt(MediaRecorder.AudioSource.AUDIO_SOURCE_INVALID);
280         parcel.writeInt(AudioAttributes.FLAG_MUTE_HAPTIC);
281         parcel.writeInt(AudioAttributes.FLATTEN_TAGS);
282         // mFormattedTags
283         parcel.writeString("oem=extension_1979");
284         // ATTR_PARCEL_IS_NULL_BUNDLE
285         parcel.writeInt(-1977);
286 
287         // nb stream types
288         parcel.writeInt(0);
289 
290         parcel.setDataPosition(0);
291         OEM_GROUP = AudioVolumeGroup.CREATOR.createFromParcel(parcel);
parcel.recycle()292         parcel.recycle();
293 
294         VOLUME_GROUPS = List.of(MUSIC_GROUP, NAV_GROUP, OEM_GROUP);
295 
296         AudioAttributes[] oemAttributesArray = { OEM_ATTRIBUTES };
297         AudioAttributes[] musicAttributesArray = { MUSIC_ATTRIBUTES, MOVIE_ATTRIBUTES };
298         AudioAttributes[] navAttributesArray = { NAV_ATTRIBUTES };
299 
300         MEDIA_CONTEXT_INFO = new CarAudioContextInfo(musicAttributesArray, MUSIC_CONTEXT_NAME,
301                 MUSIC_STRATEGY_ID);
302         NAV_CONTEXT_INFO = new CarAudioContextInfo(navAttributesArray,  NAV_CONTEXT_NAME,
303                 NAV_STRATEGY_ID);
304         OEM_CONTEXT_INFO = new CarAudioContextInfo(oemAttributesArray, OEM_CONTEXT_NAME,
305                 OEM_STRATEGY_ID);
306     }
307 
CoreAudioRoutingUtils()308     private CoreAudioRoutingUtils() {
309         throw new UnsupportedOperationException("CoreAudioRoutingUtils class is non instantiable");
310     }
311 
getVolumeGroups()312     public static List<AudioVolumeGroup> getVolumeGroups() {
313         return VOLUME_GROUPS;
314     }
315 
getProductStrategies()316     public static List<AudioProductStrategy> getProductStrategies() {
317         return PRODUCT_STRATEGIES;
318     }
319 
getCarAudioContextInfos()320     public static List<CarAudioContextInfo> getCarAudioContextInfos() {
321         List<CarAudioContextInfo> carAudioContextInfos = new ArrayList<>(3);
322 
323         carAudioContextInfos.add(MEDIA_CONTEXT_INFO);
324         carAudioContextInfos.add(NAV_CONTEXT_INFO);
325         carAudioContextInfos.add(OEM_CONTEXT_INFO);
326 
327         return carAudioContextInfos;
328     }
329 }
330