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