• 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.car.media.CarAudioManager.PRIMARY_AUDIO_ZONE;
19 
20 import android.annotation.NonNull;
21 import android.media.AudioDeviceAttributes;
22 import android.media.AudioDeviceInfo;
23 import android.text.TextUtils;
24 import android.util.SparseArray;
25 import android.util.SparseIntArray;
26 import android.util.Xml;
27 
28 import com.android.car.audio.CarAudioContext.AudioContext;
29 import com.android.internal.util.Preconditions;
30 
31 import org.xmlpull.v1.XmlPullParser;
32 import org.xmlpull.v1.XmlPullParserException;
33 
34 import java.io.IOException;
35 import java.io.InputStream;
36 import java.util.ArrayList;
37 import java.util.Arrays;
38 import java.util.HashMap;
39 import java.util.HashSet;
40 import java.util.List;
41 import java.util.Map;
42 import java.util.Objects;
43 import java.util.Set;
44 import java.util.stream.Collectors;
45 
46 /**
47  * A helper class loads all audio zones from the configuration XML file.
48  */
49 /* package */ class CarAudioZonesHelper {
50     private static final String NAMESPACE = null;
51     private static final String TAG_ROOT = "carAudioConfiguration";
52     private static final String TAG_AUDIO_ZONES = "zones";
53     private static final String TAG_AUDIO_ZONE = "zone";
54     private static final String TAG_VOLUME_GROUPS = "volumeGroups";
55     private static final String TAG_VOLUME_GROUP = "group";
56     private static final String TAG_AUDIO_DEVICE = "device";
57     private static final String TAG_CONTEXT = "context";
58     private static final String ATTR_VERSION = "version";
59     private static final String ATTR_IS_PRIMARY = "isPrimary";
60     private static final String ATTR_ZONE_NAME = "name";
61     private static final String ATTR_DEVICE_ADDRESS = "address";
62     private static final String ATTR_CONTEXT_NAME = "context";
63     private static final String ATTR_ZONE_ID = "audioZoneId";
64     private static final String ATTR_OCCUPANT_ZONE_ID = "occupantZoneId";
65     private static final String TAG_INPUT_DEVICES = "inputDevices";
66     private static final String TAG_INPUT_DEVICE = "inputDevice";
67     private static final int INVALID_VERSION = -1;
68     private static final int SUPPORTED_VERSION_1 = 1;
69     private static final int SUPPORTED_VERSION_2 = 2;
70     private static final SparseIntArray SUPPORTED_VERSIONS;
71 
72 
73     private static final Map<String, Integer> CONTEXT_NAME_MAP;
74 
75     static {
76         CONTEXT_NAME_MAP = new HashMap<>(CarAudioContext.CONTEXTS.length);
77         CONTEXT_NAME_MAP.put("music", CarAudioContext.MUSIC);
78         CONTEXT_NAME_MAP.put("navigation", CarAudioContext.NAVIGATION);
79         CONTEXT_NAME_MAP.put("voice_command", CarAudioContext.VOICE_COMMAND);
80         CONTEXT_NAME_MAP.put("call_ring", CarAudioContext.CALL_RING);
81         CONTEXT_NAME_MAP.put("call", CarAudioContext.CALL);
82         CONTEXT_NAME_MAP.put("alarm", CarAudioContext.ALARM);
83         CONTEXT_NAME_MAP.put("notification", CarAudioContext.NOTIFICATION);
84         CONTEXT_NAME_MAP.put("system_sound", CarAudioContext.SYSTEM_SOUND);
85         CONTEXT_NAME_MAP.put("emergency", CarAudioContext.EMERGENCY);
86         CONTEXT_NAME_MAP.put("safety", CarAudioContext.SAFETY);
87         CONTEXT_NAME_MAP.put("vehicle_status", CarAudioContext.VEHICLE_STATUS);
88         CONTEXT_NAME_MAP.put("announcement", CarAudioContext.ANNOUNCEMENT);
89 
90         SUPPORTED_VERSIONS = new SparseIntArray(2);
SUPPORTED_VERSIONS.put(SUPPORTED_VERSION_1, SUPPORTED_VERSION_1)91         SUPPORTED_VERSIONS.put(SUPPORTED_VERSION_1, SUPPORTED_VERSION_1);
SUPPORTED_VERSIONS.put(SUPPORTED_VERSION_2, SUPPORTED_VERSION_2)92         SUPPORTED_VERSIONS.put(SUPPORTED_VERSION_2, SUPPORTED_VERSION_2);
93     }
94 
95     // Same contexts as defined in android.hardware.automotive.audiocontrol.V1_0.ContextNumber
96     static final int[] LEGACY_CONTEXTS = new int[]{
97             CarAudioContext.MUSIC,
98             CarAudioContext.NAVIGATION,
99             CarAudioContext.VOICE_COMMAND,
100             CarAudioContext.CALL_RING,
101             CarAudioContext.CALL,
102             CarAudioContext.ALARM,
103             CarAudioContext.NOTIFICATION,
104             CarAudioContext.SYSTEM_SOUND
105     };
106 
isLegacyContext(@udioContext int audioContext)107     private static boolean isLegacyContext(@AudioContext int audioContext) {
108         return Arrays.binarySearch(LEGACY_CONTEXTS, audioContext) >= 0;
109     }
110 
111     private static final List<Integer> NON_LEGACY_CONTEXTS = new ArrayList<>(
112             CarAudioContext.CONTEXTS.length - LEGACY_CONTEXTS.length);
113 
114     static {
115         for (@AudioContext int audioContext : CarAudioContext.CONTEXTS) {
116             if (!isLegacyContext(audioContext)) {
117                 NON_LEGACY_CONTEXTS.add(audioContext);
118             }
119         }
120     }
121 
bindNonLegacyContexts(CarVolumeGroup group, CarAudioDeviceInfo info)122     static void bindNonLegacyContexts(CarVolumeGroup group, CarAudioDeviceInfo info) {
123         for (@AudioContext int audioContext : NON_LEGACY_CONTEXTS) {
124             group.bind(audioContext, info);
125         }
126     }
127 
128     private final CarAudioSettings mCarAudioSettings;
129     private final Map<String, CarAudioDeviceInfo> mAddressToCarAudioDeviceInfo;
130     private final Map<String, AudioDeviceInfo> mAddressToInputAudioDeviceInfo;
131     private final InputStream mInputStream;
132     private final SparseIntArray mZoneIdToOccupantZoneIdMapping;
133     private final Set<Integer> mAudioZoneIds;
134     private final Set<String> mInputAudioDevices;
135 
136     private int mNextSecondaryZoneId;
137     private int mCurrentVersion;
138 
139     /**
140      * <p><b>Note: <b/> CarAudioZonesHelper is expected to be used from a single thread. This
141      * should be the same thread that originally called new CarAudioZonesHelper.
142      */
CarAudioZonesHelper(@onNull CarAudioSettings carAudioSettings, @NonNull InputStream inputStream, @NonNull List<CarAudioDeviceInfo> carAudioDeviceInfos, @NonNull AudioDeviceInfo[] inputDeviceInfo)143     CarAudioZonesHelper(@NonNull CarAudioSettings carAudioSettings,
144             @NonNull InputStream inputStream,
145             @NonNull List<CarAudioDeviceInfo> carAudioDeviceInfos,
146             @NonNull AudioDeviceInfo[] inputDeviceInfo) {
147         mCarAudioSettings = Objects.requireNonNull(carAudioSettings);
148         mInputStream = Objects.requireNonNull(inputStream);
149         Objects.requireNonNull(carAudioDeviceInfos);
150         Objects.requireNonNull(inputDeviceInfo);
151         mAddressToCarAudioDeviceInfo = CarAudioZonesHelper.generateAddressToInfoMap(
152                 carAudioDeviceInfos);
153         mAddressToInputAudioDeviceInfo =
154                 CarAudioZonesHelper.generateAddressToInputAudioDeviceInfoMap(inputDeviceInfo);
155         mNextSecondaryZoneId = PRIMARY_AUDIO_ZONE + 1;
156         mZoneIdToOccupantZoneIdMapping = new SparseIntArray();
157         mAudioZoneIds = new HashSet<>();
158         mInputAudioDevices = new HashSet<>();
159     }
160 
getCarAudioZoneIdToOccupantZoneIdMapping()161     SparseIntArray getCarAudioZoneIdToOccupantZoneIdMapping() {
162         return mZoneIdToOccupantZoneIdMapping;
163     }
164 
loadAudioZones()165     SparseArray<CarAudioZone> loadAudioZones() throws IOException, XmlPullParserException {
166         return parseCarAudioZones(mInputStream);
167     }
168 
generateAddressToInfoMap( List<CarAudioDeviceInfo> carAudioDeviceInfos)169     private static Map<String, CarAudioDeviceInfo> generateAddressToInfoMap(
170             List<CarAudioDeviceInfo> carAudioDeviceInfos) {
171         return carAudioDeviceInfos.stream()
172                 .filter(info -> !TextUtils.isEmpty(info.getAddress()))
173                 .collect(Collectors.toMap(CarAudioDeviceInfo::getAddress, info -> info));
174     }
175 
generateAddressToInputAudioDeviceInfoMap( @onNull AudioDeviceInfo[] inputAudioDeviceInfos)176     private static Map<String, AudioDeviceInfo> generateAddressToInputAudioDeviceInfoMap(
177             @NonNull AudioDeviceInfo[] inputAudioDeviceInfos) {
178         HashMap<String, AudioDeviceInfo> deviceAddressToInputDeviceMap =
179                 new HashMap<>(inputAudioDeviceInfos.length);
180         for (int i = 0; i < inputAudioDeviceInfos.length; ++i) {
181             AudioDeviceInfo device = inputAudioDeviceInfos[i];
182             if (device.isSource()) {
183                 deviceAddressToInputDeviceMap.put(device.getAddress(), device);
184             }
185         }
186         return deviceAddressToInputDeviceMap;
187     }
188 
parseCarAudioZones(InputStream stream)189     private SparseArray<CarAudioZone> parseCarAudioZones(InputStream stream)
190             throws XmlPullParserException, IOException {
191         XmlPullParser parser = Xml.newPullParser();
192         parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, NAMESPACE != null);
193         parser.setInput(stream, null);
194 
195         // Ensure <carAudioConfiguration> is the root
196         parser.nextTag();
197         parser.require(XmlPullParser.START_TAG, NAMESPACE, TAG_ROOT);
198 
199         // Version check
200         final int versionNumber = Integer.parseInt(
201                 parser.getAttributeValue(NAMESPACE, ATTR_VERSION));
202 
203         if (SUPPORTED_VERSIONS.get(versionNumber, INVALID_VERSION) == INVALID_VERSION) {
204             throw new IllegalArgumentException("Latest Supported version:"
205                     + SUPPORTED_VERSION_2 + " , got version:" + versionNumber);
206         }
207 
208         mCurrentVersion = versionNumber;
209 
210         // Get all zones configured under <zones> tag
211         while (parser.next() != XmlPullParser.END_TAG) {
212             if (parser.getEventType() != XmlPullParser.START_TAG) continue;
213             if (TAG_AUDIO_ZONES.equals(parser.getName())) {
214                 return parseAudioZones(parser);
215             } else {
216                 skip(parser);
217             }
218         }
219         throw new RuntimeException(TAG_AUDIO_ZONES + " is missing from configuration");
220     }
221 
parseAudioZones(XmlPullParser parser)222     private SparseArray<CarAudioZone> parseAudioZones(XmlPullParser parser)
223             throws XmlPullParserException, IOException {
224         SparseArray<CarAudioZone> carAudioZones = new SparseArray<>();
225 
226         while (parser.next() != XmlPullParser.END_TAG) {
227             if (parser.getEventType() != XmlPullParser.START_TAG) continue;
228             if (TAG_AUDIO_ZONE.equals(parser.getName())) {
229                 CarAudioZone zone = parseAudioZone(parser);
230                 verifyOnlyOnePrimaryZone(zone, carAudioZones);
231                 carAudioZones.put(zone.getId(), zone);
232             } else {
233                 skip(parser);
234             }
235         }
236 
237         verifyPrimaryZonePresent(carAudioZones);
238         return carAudioZones;
239     }
240 
verifyOnlyOnePrimaryZone(CarAudioZone newZone, SparseArray<CarAudioZone> zones)241     private void verifyOnlyOnePrimaryZone(CarAudioZone newZone, SparseArray<CarAudioZone> zones) {
242         if (newZone.getId() == PRIMARY_AUDIO_ZONE && zones.contains(PRIMARY_AUDIO_ZONE)) {
243             throw new RuntimeException("More than one zone parsed with primary audio zone ID: "
244                             + PRIMARY_AUDIO_ZONE);
245         }
246     }
247 
verifyPrimaryZonePresent(SparseArray<CarAudioZone> zones)248     private void verifyPrimaryZonePresent(SparseArray<CarAudioZone> zones) {
249         if (!zones.contains(PRIMARY_AUDIO_ZONE)) {
250             throw new RuntimeException("Primary audio zone is required");
251         }
252     }
253 
parseAudioZone(XmlPullParser parser)254     private CarAudioZone parseAudioZone(XmlPullParser parser)
255             throws XmlPullParserException, IOException {
256         final boolean isPrimary = Boolean.parseBoolean(
257                 parser.getAttributeValue(NAMESPACE, ATTR_IS_PRIMARY));
258         final String zoneName = parser.getAttributeValue(NAMESPACE, ATTR_ZONE_NAME);
259         final int audioZoneId = getZoneId(isPrimary, parser);
260         parseOccupantZoneId(audioZoneId, parser);
261         final CarAudioZone zone = new CarAudioZone(audioZoneId, zoneName);
262         while (parser.next() != XmlPullParser.END_TAG) {
263             if (parser.getEventType() != XmlPullParser.START_TAG) continue;
264             // Expect one <volumeGroups> in one audio zone
265             if (TAG_VOLUME_GROUPS.equals(parser.getName())) {
266                 parseVolumeGroups(parser, zone);
267             } else if (TAG_INPUT_DEVICES.equals(parser.getName())) {
268                 parseInputAudioDevices(parser, zone);
269             } else {
270                 skip(parser);
271             }
272         }
273         return zone;
274     }
275 
getZoneId(boolean isPrimary, XmlPullParser parser)276     private int getZoneId(boolean isPrimary, XmlPullParser parser) {
277         String audioZoneIdString = parser.getAttributeValue(NAMESPACE, ATTR_ZONE_ID);
278         if (isVersionOne()) {
279             Preconditions.checkArgument(audioZoneIdString == null,
280                     "Invalid audio attribute %s"
281                             + ", Please update car audio configurations file "
282                             + "to version to 2 to use it.", ATTR_ZONE_ID);
283             return isPrimary ? PRIMARY_AUDIO_ZONE
284                     : getNextSecondaryZoneId();
285         }
286         // Primary zone does not need to define it
287         if (isPrimary && audioZoneIdString == null) {
288             return PRIMARY_AUDIO_ZONE;
289         }
290         Objects.requireNonNull(audioZoneIdString, () ->
291                 "Requires " + ATTR_ZONE_ID + " for all audio zones.");
292         int zoneId = parsePositiveIntAttribute(ATTR_ZONE_ID, audioZoneIdString);
293         //Verify that primary zone id is PRIMARY_AUDIO_ZONE
294         if (isPrimary) {
295             Preconditions.checkArgument(zoneId == PRIMARY_AUDIO_ZONE,
296                     "Primary zone %s must be %d or it can be left empty.",
297                     ATTR_ZONE_ID, PRIMARY_AUDIO_ZONE);
298         } else {
299             Preconditions.checkArgument(zoneId != PRIMARY_AUDIO_ZONE,
300                     "%s can only be %d for primary zone.",
301                     ATTR_ZONE_ID, PRIMARY_AUDIO_ZONE);
302         }
303         validateAudioZoneIdIsUnique(zoneId);
304         return zoneId;
305     }
306 
parseOccupantZoneId(int audioZoneId, XmlPullParser parser)307     private void parseOccupantZoneId(int audioZoneId, XmlPullParser parser) {
308         String occupantZoneIdString = parser.getAttributeValue(NAMESPACE, ATTR_OCCUPANT_ZONE_ID);
309         if (isVersionOne()) {
310             Preconditions.checkArgument(occupantZoneIdString == null,
311                     "Invalid audio attribute %s"
312                             + ", Please update car audio configurations file "
313                             + "to version to 2 to use it.", ATTR_OCCUPANT_ZONE_ID);
314             return;
315         }
316         //Occupant id not required for all zones
317         if (occupantZoneIdString == null) {
318             return;
319         }
320         int occupantZoneId = parsePositiveIntAttribute(ATTR_OCCUPANT_ZONE_ID, occupantZoneIdString);
321         validateOccupantZoneIdIsUnique(occupantZoneId);
322         mZoneIdToOccupantZoneIdMapping.put(audioZoneId, occupantZoneId);
323     }
324 
parsePositiveIntAttribute(String attribute, String integerString)325     private int parsePositiveIntAttribute(String attribute, String integerString) {
326         try {
327             return Integer.parseUnsignedInt(integerString);
328         } catch (NumberFormatException | IndexOutOfBoundsException e) {
329             throw new IllegalArgumentException(attribute + " must be a positive integer, but was \""
330                     + integerString + "\" instead.", e);
331         }
332     }
333 
parseInputAudioDevices(XmlPullParser parser, CarAudioZone zone)334     private void parseInputAudioDevices(XmlPullParser parser, CarAudioZone zone)
335             throws IOException, XmlPullParserException {
336         if (isVersionOne()) {
337             throw new IllegalStateException(
338                     TAG_INPUT_DEVICES + " are not supported in car_audio_configuration.xml version "
339                             + SUPPORTED_VERSION_1);
340         }
341         while (parser.next() != XmlPullParser.END_TAG) {
342             if (parser.getEventType() != XmlPullParser.START_TAG) continue;
343             if (TAG_INPUT_DEVICE.equals(parser.getName())) {
344                 String audioDeviceAddress =
345                         parser.getAttributeValue(NAMESPACE, ATTR_DEVICE_ADDRESS);
346                 validateInputAudioDeviceAddress(audioDeviceAddress);
347                 AudioDeviceInfo info = mAddressToInputAudioDeviceInfo.get(audioDeviceAddress);
348                 Preconditions.checkArgument(info != null,
349                         "%s %s of %s does not exist, add input device to"
350                                 + " audio_policy_configuration.xml.",
351                         ATTR_DEVICE_ADDRESS, audioDeviceAddress, TAG_INPUT_DEVICE);
352                 zone.addInputAudioDevice(new AudioDeviceAttributes(info));
353             }
354             skip(parser);
355         }
356     }
357 
validateInputAudioDeviceAddress(String audioDeviceAddress)358     private void validateInputAudioDeviceAddress(String audioDeviceAddress) {
359         Objects.requireNonNull(audioDeviceAddress, () ->
360                 TAG_INPUT_DEVICE + " " + ATTR_DEVICE_ADDRESS + " attribute must be present.");
361         Preconditions.checkArgument(!audioDeviceAddress.isEmpty(),
362                 "%s %s attribute can not be empty.",
363                 TAG_INPUT_DEVICE, ATTR_DEVICE_ADDRESS);
364         if (mInputAudioDevices.contains(audioDeviceAddress)) {
365             throw new IllegalArgumentException(TAG_INPUT_DEVICE + " " + audioDeviceAddress
366                     + " repeats, " + TAG_INPUT_DEVICES + " can not repeat.");
367         }
368         mInputAudioDevices.add(audioDeviceAddress);
369     }
370 
validateOccupantZoneIdIsUnique(int occupantZoneId)371     private void validateOccupantZoneIdIsUnique(int occupantZoneId) {
372         if (mZoneIdToOccupantZoneIdMapping.indexOfValue(occupantZoneId) > -1) {
373             throw new IllegalArgumentException(ATTR_OCCUPANT_ZONE_ID + " " + occupantZoneId
374                     + " is already associated with a zone");
375         }
376     }
377 
validateAudioZoneIdIsUnique(int audioZoneId)378     private void validateAudioZoneIdIsUnique(int audioZoneId) {
379         if (mAudioZoneIds.contains(audioZoneId)) {
380             throw new IllegalArgumentException(ATTR_ZONE_ID + " " + audioZoneId
381                     + " is already associated with a zone");
382         }
383         mAudioZoneIds.add(audioZoneId);
384     }
385 
parseVolumeGroups(XmlPullParser parser, CarAudioZone zone)386     private void parseVolumeGroups(XmlPullParser parser, CarAudioZone zone)
387             throws XmlPullParserException, IOException {
388         int groupId = 0;
389         while (parser.next() != XmlPullParser.END_TAG) {
390             if (parser.getEventType() != XmlPullParser.START_TAG) continue;
391             if (TAG_VOLUME_GROUP.equals(parser.getName())) {
392                 zone.addVolumeGroup(parseVolumeGroup(parser, zone.getId(), groupId));
393                 groupId++;
394             } else {
395                 skip(parser);
396             }
397         }
398     }
399 
parseVolumeGroup(XmlPullParser parser, int zoneId, int groupId)400     private CarVolumeGroup parseVolumeGroup(XmlPullParser parser, int zoneId, int groupId)
401             throws XmlPullParserException, IOException {
402         CarVolumeGroup group = new CarVolumeGroup(mCarAudioSettings, zoneId, groupId);
403         while (parser.next() != XmlPullParser.END_TAG) {
404             if (parser.getEventType() != XmlPullParser.START_TAG) continue;
405             if (TAG_AUDIO_DEVICE.equals(parser.getName())) {
406                 String address = parser.getAttributeValue(NAMESPACE, ATTR_DEVICE_ADDRESS);
407                 validateOutputDeviceExist(address);
408                 parseVolumeGroupContexts(parser, group, address);
409             } else {
410                 skip(parser);
411             }
412         }
413         return group;
414     }
415 
validateOutputDeviceExist(String address)416     private void validateOutputDeviceExist(String address) {
417         if (!mAddressToCarAudioDeviceInfo.containsKey(address)) {
418             throw new IllegalStateException(String.format(
419                     "Output device address %s does not belong to any configured output device.",
420                     address));
421         }
422     }
423 
parseVolumeGroupContexts( XmlPullParser parser, CarVolumeGroup group, String address)424     private void parseVolumeGroupContexts(
425             XmlPullParser parser, CarVolumeGroup group, String address)
426             throws XmlPullParserException, IOException {
427         while (parser.next() != XmlPullParser.END_TAG) {
428             if (parser.getEventType() != XmlPullParser.START_TAG) continue;
429             if (TAG_CONTEXT.equals(parser.getName())) {
430                 @AudioContext int carAudioContext = parseCarAudioContext(
431                         parser.getAttributeValue(NAMESPACE, ATTR_CONTEXT_NAME));
432                 validateCarAudioContextSupport(carAudioContext);
433                 CarAudioDeviceInfo info = mAddressToCarAudioDeviceInfo.get(address);
434                 group.bind(carAudioContext, info);
435 
436                 // If V1, default new contexts to same device as DEFAULT_AUDIO_USAGE
437                 if (isVersionOne() && carAudioContext == CarAudioService.DEFAULT_AUDIO_CONTEXT) {
438                     bindNonLegacyContexts(group, info);
439                 }
440             }
441             // Always skip to upper level since we're at the lowest.
442             skip(parser);
443         }
444     }
445 
isVersionOne()446     private boolean isVersionOne() {
447         return mCurrentVersion == SUPPORTED_VERSION_1;
448     }
449 
skip(XmlPullParser parser)450     private void skip(XmlPullParser parser) throws XmlPullParserException, IOException {
451         if (parser.getEventType() != XmlPullParser.START_TAG) {
452             throw new IllegalStateException();
453         }
454         int depth = 1;
455         while (depth != 0) {
456             switch (parser.next()) {
457                 case XmlPullParser.END_TAG:
458                     depth--;
459                     break;
460                 case XmlPullParser.START_TAG:
461                     depth++;
462                     break;
463             }
464         }
465     }
466 
parseCarAudioContext(String context)467     private static @AudioContext int parseCarAudioContext(String context) {
468         return CONTEXT_NAME_MAP.getOrDefault(context.toLowerCase(), CarAudioContext.INVALID);
469     }
470 
validateCarAudioContextSupport(@udioContext int audioContext)471     private void validateCarAudioContextSupport(@AudioContext int audioContext) {
472         if (isVersionOne() && NON_LEGACY_CONTEXTS.contains(audioContext)) {
473             throw new IllegalArgumentException(String.format(
474                     "Non-legacy audio contexts such as %s are not supported in "
475                             + "car_audio_configuration.xml version %d",
476                     CarAudioContext.toString(audioContext), SUPPORTED_VERSION_1));
477         }
478     }
479 
getNextSecondaryZoneId()480     private int getNextSecondaryZoneId() {
481         int zoneId = mNextSecondaryZoneId;
482         mNextSecondaryZoneId += 1;
483         return zoneId;
484     }
485 }
486