• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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.DUMP_INFO;
20 
21 import android.annotation.IntDef;
22 import android.car.builtin.media.AudioManagerHelper;
23 import android.car.builtin.util.Slogf;
24 import android.media.AudioAttributes;
25 import android.util.ArrayMap;
26 import android.util.ArraySet;
27 import android.util.SparseArray;
28 
29 import com.android.car.CarLog;
30 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport;
31 import com.android.car.internal.annotation.AttributeUsage;
32 import com.android.car.internal.util.IndentingPrintWriter;
33 import com.android.internal.annotations.VisibleForTesting;
34 import com.android.internal.util.Preconditions;
35 
36 import java.lang.annotation.Retention;
37 import java.lang.annotation.RetentionPolicy;
38 import java.util.ArrayList;
39 import java.util.Collections;
40 import java.util.List;
41 import java.util.Map;
42 import java.util.Objects;
43 import java.util.Set;
44 
45 /**
46  * Groupings of {@link AttributeUsage}s to simplify configuration of car audio routing, volume
47  * groups, and focus interactions for similar usages.
48  */
49 public final class CarAudioContext {
50 
51     private static final String TAG = CarLog.tagFor(CarAudioContext.class);
52 
53     /*
54      * Shouldn't be used
55      * ::android::hardware::automotive::audiocontrol::V1_0::ContextNumber.INVALID
56      */
57     private static final int INVALID = 0;
58     /*
59      * Music playback
60      * ::android::hardware::automotive::audiocontrol::V1_0::ContextNumber.INVALID implicitly + 1
61      */
62     static final int MUSIC = 1;
63     /*
64      * Navigation directions
65      * ::android::hardware::automotive::audiocontrol::V1_0::ContextNumber.MUSIC implicitly + 1
66      */
67     static final int NAVIGATION = 2;
68     /*
69      * Voice command session
70      * ::android::hardware::automotive::audiocontrol::V1_0::ContextNumber.NAVIGATION implicitly + 1
71      */
72     static final int VOICE_COMMAND = 3;
73     /*
74      * Voice call ringing
75      * ::android::hardware::automotive::audiocontrol::V1_0::ContextNumber
76      *     .VOICE_COMMAND implicitly + 1
77      */
78     static final int CALL_RING = 4;
79     /*
80      * Voice call
81      * ::android::hardware::automotive::audiocontrol::V1_0::ContextNumber.CALL_RING implicitly + 1
82      */
83     static final int CALL = 5;
84     /*
85      * Alarm sound from Android
86      * ::android::hardware::automotive::audiocontrol::V1_0::ContextNumber.CALL implicitly + 1
87      */
88     static final int ALARM = 6;
89     /*
90      * Notifications
91      * ::android::hardware::automotive::audiocontrol::V1_0::ContextNumber.ALARM implicitly + 1
92      */
93     static final int NOTIFICATION = 7;
94     /*
95      * System sounds
96      * ::android::hardware::automotive::audiocontrol::V1_0::ContextNumber
97      *     .NOTIFICATION implicitly + 1
98      */
99     static final int SYSTEM_SOUND = 8;
100     /*
101      * Emergency related sounds such as collision warnings
102      */
103     static final int EMERGENCY = 9;
104     /*
105      * Safety sounds such as obstacle detection when backing up or when changing lanes
106      */
107     static final int SAFETY = 10;
108     /*
109      * Vehicle Status related sounds such as check engine light or seat belt chimes
110      */
111     static final int VEHICLE_STATUS = 11;
112     /*
113      * Announcement such as traffic announcements
114      */
115     static final int ANNOUNCEMENT = 12;
116 
117     @IntDef({
118             INVALID,
119             MUSIC,
120             NAVIGATION,
121             VOICE_COMMAND,
122             CALL_RING,
123             CALL,
124             ALARM,
125             NOTIFICATION,
126             SYSTEM_SOUND,
127             EMERGENCY,
128             SAFETY,
129             VEHICLE_STATUS,
130             ANNOUNCEMENT
131     })
132     @Retention(RetentionPolicy.SOURCE)
133     public @interface AudioContext {
134     }
135 
136     private static final List<Integer> CONTEXTS = List.of(
137             // The items are in a sorted order
138             // Starting at one
139             MUSIC,
140             NAVIGATION,
141             VOICE_COMMAND,
142             CALL_RING,
143             CALL,
144             ALARM,
145             NOTIFICATION,
146             SYSTEM_SOUND,
147             EMERGENCY,
148             SAFETY,
149             VEHICLE_STATUS,
150             ANNOUNCEMENT
151     );
152 
153     // Contexts related to non-car audio system, this covers the general use case of context
154     // that would exist in the phone.
155     private static final List<Integer> NON_CAR_SYSTEM_CONTEXTS = List.of(
156             MUSIC,
157             NAVIGATION,
158             VOICE_COMMAND,
159             CALL_RING,
160             CALL,
161             ALARM,
162             NOTIFICATION,
163             SYSTEM_SOUND
164     );
165 
166     // Contexts related to car audio system, this covers the general use case of context
167     // that are generally related to car system.
168     private static final List<Integer> CAR_SYSTEM_CONTEXTS = List.of(
169             EMERGENCY,
170             SAFETY,
171             VEHICLE_STATUS,
172             ANNOUNCEMENT
173     );
174 
175     private static final AudioAttributes[] SYSTEM_ATTRIBUTES = new AudioAttributes[] {
176             getAudioAttributeFromUsage(AudioAttributes.USAGE_CALL_ASSISTANT),
177             getAudioAttributeFromUsage(AudioAttributes.USAGE_EMERGENCY),
178             getAudioAttributeFromUsage(AudioAttributes.USAGE_SAFETY),
179             getAudioAttributeFromUsage(AudioAttributes.USAGE_VEHICLE_STATUS),
180             getAudioAttributeFromUsage(AudioAttributes.USAGE_ANNOUNCEMENT)
181     };
182 
183     private static final CarAudioContextInfo CAR_CONTEXT_INFO_MUSIC =
184             new CarAudioContextInfo(new AudioAttributes[] {
185                     getAudioAttributeFromUsage(AudioAttributes.USAGE_UNKNOWN),
186                     getAudioAttributeFromUsage(AudioAttributes.USAGE_GAME),
187                     getAudioAttributeFromUsage(AudioAttributes.USAGE_MEDIA)
188             }, "MUSIC", MUSIC);
189 
190     private static final CarAudioContextInfo CAR_CONTEXT_INFO_NAVIGATION =
191             new CarAudioContextInfo(new AudioAttributes[] {
192                     getAudioAttributeFromUsage(AudioAttributes
193                     .USAGE_ASSISTANCE_NAVIGATION_GUIDANCE)
194             }, "NAVIGATION", NAVIGATION);
195 
196     private static final CarAudioContextInfo CAR_CONTEXT_INFO_VOICE_COMMAND =
197             new CarAudioContextInfo(new AudioAttributes[] {
198                     getAudioAttributeFromUsage(AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY),
199                     getAudioAttributeFromUsage(AudioAttributes.USAGE_ASSISTANT)
200             }, "VOICE_COMMAND", VOICE_COMMAND);
201 
202     private static final CarAudioContextInfo CAR_CONTEXT_INFO_CALL_RING =
203             new CarAudioContextInfo(new AudioAttributes[] {
204                     getAudioAttributeFromUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE)
205             }, "CALL_RING", CALL_RING);
206 
207     private static final CarAudioContextInfo CAR_CONTEXT_INFO_CALL =
208             new CarAudioContextInfo(new AudioAttributes[] {
209                     getAudioAttributeFromUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION),
210                     getAudioAttributeFromUsage(AudioAttributes.USAGE_CALL_ASSISTANT),
211                     getAudioAttributeFromUsage(AudioAttributes
212                             .USAGE_VOICE_COMMUNICATION_SIGNALLING),
213             }, "CALL", CALL);
214 
215     private static final CarAudioContextInfo CAR_CONTEXT_INFO_ALARM =
216             new CarAudioContextInfo(new AudioAttributes[]{
217                     getAudioAttributeFromUsage(AudioAttributes.USAGE_ALARM)
218             }, "ALARM", ALARM);
219 
220     private static final CarAudioContextInfo CAR_CONTEXT_INFO_NOTIFICATION =
221             new CarAudioContextInfo(new AudioAttributes[]{
222                     getAudioAttributeFromUsage(AudioAttributes.USAGE_NOTIFICATION),
223                     getAudioAttributeFromUsage(AudioAttributes.USAGE_NOTIFICATION_EVENT)
224             }, "NOTIFICATION", NOTIFICATION);
225 
226     private static final CarAudioContextInfo CAR_CONTEXT_INFO_SYSTEM_SOUND =
227             new CarAudioContextInfo(new AudioAttributes[]{
228                     getAudioAttributeFromUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
229             }, "SYSTEM_SOUND", SYSTEM_SOUND);
230 
231     private static final CarAudioContextInfo CAR_CONTEXT_INFO_EMERGENCY =
232             new CarAudioContextInfo(new AudioAttributes[]{
233                     getAudioAttributeFromUsage(AudioAttributes.USAGE_EMERGENCY)
234             }, "EMERGENCY", EMERGENCY);
235 
236     private static final CarAudioContextInfo CAR_CONTEXT_INFO_SAFETY =
237             new CarAudioContextInfo(new AudioAttributes[]{
238                     getAudioAttributeFromUsage(AudioAttributes.USAGE_SAFETY)
239             }, "SAFETY", SAFETY);
240 
241     private static final CarAudioContextInfo CAR_CONTEXT_INFO_VEHICLE_STATUS =
242             new CarAudioContextInfo(new AudioAttributes[]{
243                     getAudioAttributeFromUsage(AudioAttributes.USAGE_VEHICLE_STATUS)
244             }, "VEHICLE_STATUS", VEHICLE_STATUS);
245 
246     private static final CarAudioContextInfo CAR_CONTEXT_INFO_ANNOUNCEMENT =
247             new CarAudioContextInfo(new AudioAttributes[]{
248                     getAudioAttributeFromUsage(AudioAttributes.USAGE_ANNOUNCEMENT)
249             }, "ANNOUNCEMENT", ANNOUNCEMENT);
250 
251     private static final CarAudioContextInfo CAR_CONTEXT_INFO_INVALID =
252             new CarAudioContextInfo(new AudioAttributes[]{
253                     getAudioAttributeFromUsage(AudioManagerHelper.getUsageVirtualSource())
254             }, "INVALID", INVALID);
255 
256     private static final List<CarAudioContextInfo> CAR_CONTEXT_INFO = List.of(
257             CAR_CONTEXT_INFO_MUSIC,
258             CAR_CONTEXT_INFO_NAVIGATION,
259             CAR_CONTEXT_INFO_VOICE_COMMAND,
260             CAR_CONTEXT_INFO_CALL_RING,
261             CAR_CONTEXT_INFO_CALL,
262             CAR_CONTEXT_INFO_ALARM,
263             CAR_CONTEXT_INFO_NOTIFICATION,
264             CAR_CONTEXT_INFO_SYSTEM_SOUND,
265             CAR_CONTEXT_INFO_EMERGENCY,
266             CAR_CONTEXT_INFO_SAFETY,
267             CAR_CONTEXT_INFO_VEHICLE_STATUS,
268             CAR_CONTEXT_INFO_ANNOUNCEMENT,
269             CAR_CONTEXT_INFO_INVALID
270     );
271 
272     @VisibleForTesting
273     static final SparseArray<List<Integer>> sContextsToDuck =
274             new SparseArray<>(/* initialCapacity= */ 13);
275 
276     static {
277         // INVALID ducks nothing
sContextsToDuck.append(INVALID, Collections.emptyList())278         sContextsToDuck.append(INVALID, Collections.emptyList());
279         // MUSIC ducks nothing
sContextsToDuck.append(MUSIC, Collections.emptyList())280         sContextsToDuck.append(MUSIC, Collections.emptyList());
sContextsToDuck.append(NAVIGATION, List.of( MUSIC, CALL_RING, CALL, ALARM, NOTIFICATION, SYSTEM_SOUND, VEHICLE_STATUS, ANNOUNCEMENT ))281         sContextsToDuck.append(NAVIGATION, List.of(
282                 MUSIC,
283                 CALL_RING,
284                 CALL,
285                 ALARM,
286                 NOTIFICATION,
287                 SYSTEM_SOUND,
288                 VEHICLE_STATUS,
289                 ANNOUNCEMENT
290         ));
sContextsToDuck.append(VOICE_COMMAND, List.of( CALL_RING ))291         sContextsToDuck.append(VOICE_COMMAND, List.of(
292                 CALL_RING
293         ));
sContextsToDuck.append(CALL_RING, Collections.emptyList())294         sContextsToDuck.append(CALL_RING, Collections.emptyList());
sContextsToDuck.append(CALL, List.of( CALL_RING, ALARM, NOTIFICATION, VEHICLE_STATUS ))295         sContextsToDuck.append(CALL, List.of(
296                 CALL_RING,
297                 ALARM,
298                 NOTIFICATION,
299                 VEHICLE_STATUS
300         ));
sContextsToDuck.append(ALARM, List.of( MUSIC ))301         sContextsToDuck.append(ALARM, List.of(
302                 MUSIC
303         ));
sContextsToDuck.append(NOTIFICATION, List.of( MUSIC, ALARM, ANNOUNCEMENT ))304         sContextsToDuck.append(NOTIFICATION, List.of(
305                 MUSIC,
306                 ALARM,
307                 ANNOUNCEMENT
308         ));
sContextsToDuck.append(SYSTEM_SOUND, List.of( MUSIC, ALARM, ANNOUNCEMENT ))309         sContextsToDuck.append(SYSTEM_SOUND, List.of(
310                 MUSIC,
311                 ALARM,
312                 ANNOUNCEMENT
313         ));
sContextsToDuck.append(EMERGENCY, List.of( CALL ))314         sContextsToDuck.append(EMERGENCY, List.of(
315                 CALL
316         ));
sContextsToDuck.append(SAFETY, List.of( MUSIC, NAVIGATION, VOICE_COMMAND, CALL_RING, CALL, ALARM, NOTIFICATION, SYSTEM_SOUND, VEHICLE_STATUS, ANNOUNCEMENT ))317         sContextsToDuck.append(SAFETY, List.of(
318                 MUSIC,
319                 NAVIGATION,
320                 VOICE_COMMAND,
321                 CALL_RING,
322                 CALL,
323                 ALARM,
324                 NOTIFICATION,
325                 SYSTEM_SOUND,
326                 VEHICLE_STATUS,
327                 ANNOUNCEMENT
328         ));
sContextsToDuck.append(VEHICLE_STATUS, List.of( MUSIC, CALL_RING, ANNOUNCEMENT ))329         sContextsToDuck.append(VEHICLE_STATUS, List.of(
330                 MUSIC,
331                 CALL_RING,
332                 ANNOUNCEMENT
333         ));
334         // ANNOUNCEMENT ducks nothing
sContextsToDuck.append(ANNOUNCEMENT, Collections.emptyList())335         sContextsToDuck.append(ANNOUNCEMENT, Collections.emptyList());
336     }
337 
getSystemUsages()338     static int[] getSystemUsages() {
339         return covertAttributesToUsage(SYSTEM_ATTRIBUTES);
340     }
341 
342     private static final SparseArray<String> CONTEXT_NAMES = new SparseArray<>(CONTEXTS.size() + 1);
343     private static final SparseArray<AudioAttributes[]> CONTEXT_TO_ATTRIBUTES = new SparseArray<>();
344     private static final Map<AudioAttributesWrapper, Integer> AUDIO_ATTRIBUTE_TO_CONTEXT =
345             new ArrayMap<>();
346     private static final List<AudioAttributesWrapper> ALL_SUPPORTED_ATTRIBUTES = new ArrayList<>();
347 
348     static {
349         for (int index = 0; index < CAR_CONTEXT_INFO.size(); index++) {
350             CarAudioContextInfo info = CAR_CONTEXT_INFO.get(index);
info.getName()351             CONTEXT_NAMES.append(info.getId(), info.getName());
info.getAudioAttributes()352             CONTEXT_TO_ATTRIBUTES.put(info.getId(), info.getAudioAttributes());
353 
354             AudioAttributes[] attributes = info.getAudioAttributes();
355             for (int attributeIndex = 0; attributeIndex < attributes.length; attributeIndex++) {
356                 AudioAttributesWrapper attributesWrapper =
357                         new AudioAttributesWrapper(attributes[attributeIndex]);
358                 if (AUDIO_ATTRIBUTE_TO_CONTEXT.containsKey(attributesWrapper)) {
359                     int mappedContext = AUDIO_ATTRIBUTE_TO_CONTEXT.get(attributesWrapper);
Slogf.wtf(TAG, "%s already mapped to context %s, can not remap to context %s", attributesWrapper, mappedContext, info.getId())360                     Slogf.wtf(TAG, "%s already mapped to context %s, can not remap to context %s",
361                             attributesWrapper, mappedContext, info.getId());
362                 }
AUDIO_ATTRIBUTE_TO_CONTEXT.put(attributesWrapper, info.getId())363                 AUDIO_ATTRIBUTE_TO_CONTEXT.put(attributesWrapper, info.getId());
364                 if (isInvalidContextId(info.getId())) {
365                     continue;
366                 }
367                 ALL_SUPPORTED_ATTRIBUTES.add(attributesWrapper);
368             }
369         }
370     }
371 
372     private final List<CarAudioContextInfo> mCarAudioContextInfos;
373     private final Map<AudioAttributesWrapper, Integer> mAudioAttributesToContext =
374             new ArrayMap<>();
375     private final SparseArray<String> mContextToNames = new SparseArray<>();
376     private final SparseArray<AudioAttributes[]> mContextToAttributes = new SparseArray<>();
377 
378     /**
379      * Creates a car audio context which contains the logical grouping of
380      * audio attributes into contexts
381      *
382      * @param carAudioContexts list of audio attributes grouping
383      */
CarAudioContext(List<CarAudioContextInfo> carAudioContexts)384     public CarAudioContext(List<CarAudioContextInfo> carAudioContexts) {
385         Objects.requireNonNull(carAudioContexts,
386                 "Car audio contexts must not be null");
387         Preconditions.checkArgument(!carAudioContexts.isEmpty(),
388                 "Car audio contexts must not be empty");
389         mCarAudioContextInfos = carAudioContexts;
390         for (int index = 0; index < carAudioContexts.size(); index++) {
391             CarAudioContextInfo info = carAudioContexts.get(index);
392             mContextToNames.put(info.getId(), info.getName());
393             mContextToAttributes.put(info.getId(), info.getAudioAttributes());
394 
395             AudioAttributes[] attributes = info.getAudioAttributes();
396             for (int attributeIndex = 0; attributeIndex < attributes.length; attributeIndex++) {
397                 AudioAttributesWrapper attributesWrapper =
398                         new AudioAttributesWrapper(attributes[attributeIndex]);
399                 if (mAudioAttributesToContext.containsKey(attributesWrapper)) {
400                     int mappedContext = mAudioAttributesToContext.get(attributesWrapper);
401                     Slogf.wtf(TAG, "%s already mapped to context %s, can not remap to context %s",
402                             attributesWrapper, mappedContext, info.getId());
403                 }
404                 if (isInvalidContextId(info.getId())) {
405                     continue;
406                 }
407                 mAudioAttributesToContext.put(attributesWrapper, info.getId());
408             }
409         }
410     }
411 
evaluateAudioAttributesToDuck( List<AudioAttributes> activePlaybackAttributes)412     static List<AudioAttributes> evaluateAudioAttributesToDuck(
413             List<AudioAttributes> activePlaybackAttributes) {
414         ArraySet<AudioAttributesWrapper> attributesToDuck = new ArraySet<>();
415         List<AudioAttributesWrapper> wrappers = new ArrayList<>(activePlaybackAttributes.size());
416         for (int index = 0; index < activePlaybackAttributes.size(); index++) {
417             AudioAttributesWrapper wrapper =
418                     new AudioAttributesWrapper(activePlaybackAttributes.get(index));
419             wrappers.add(wrapper);
420             int context = AUDIO_ATTRIBUTE_TO_CONTEXT.getOrDefault(wrapper, INVALID);
421             if (isInvalidContextId(context)) {
422                 continue;
423             }
424             List<Integer> contextsToDuck = sContextsToDuck.get(context);
425             for (int contextIndex = 0; contextIndex < contextsToDuck.size(); contextIndex++) {
426                 AudioAttributes[] duckedAttributes =
427                         CONTEXT_TO_ATTRIBUTES.get(contextsToDuck.get(contextIndex));
428                 for (int i = 0; i < duckedAttributes.length; i++) {
429                     attributesToDuck.add(new AudioAttributesWrapper(duckedAttributes[i]));
430                 }
431             }
432         }
433         attributesToDuck.retainAll(wrappers);
434 
435         List<AudioAttributes> duckedAudioAttributes = new ArrayList<>(attributesToDuck.size());
436         for (int index = 0; index < attributesToDuck.size(); index++) {
437             duckedAudioAttributes.add(attributesToDuck.valueAt(index).getAudioAttributes());
438         }
439 
440         return duckedAudioAttributes;
441     }
442 
443     /**
444      * Checks if the audio attribute usage is valid, throws an {@link IllegalArgumentException}
445      * if the {@code usage} is not valid.
446      *
447      * @param usage audio attribute usage to check
448      * @throws IllegalArgumentException in case of invalid audio attribute usage
449      */
checkAudioAttributeUsage(@ttributeUsage int usage)450     public static void checkAudioAttributeUsage(@AttributeUsage int usage)
451             throws IllegalArgumentException {
452         if (isValidAudioAttributeUsage(usage)) {
453             return;
454         }
455 
456         throw new IllegalArgumentException("Invalid audio attribute " + usage);
457     }
458 
459     /**
460      * Determines if the audio attribute usage is valid
461      *
462      * @param usage audio attribute usage to check
463      * @return {@code true} if valid, {@code false} otherwise
464      */
isValidAudioAttributeUsage(@ttributeUsage int usage)465     public static boolean isValidAudioAttributeUsage(@AttributeUsage int usage) {
466         switch (usage) {
467             case AudioAttributes.USAGE_UNKNOWN:
468             case AudioAttributes.USAGE_MEDIA:
469             case AudioAttributes.USAGE_VOICE_COMMUNICATION:
470             case AudioAttributes.USAGE_VOICE_COMMUNICATION_SIGNALLING:
471             case AudioAttributes.USAGE_ALARM:
472             case AudioAttributes.USAGE_NOTIFICATION:
473             case AudioAttributes.USAGE_NOTIFICATION_RINGTONE:
474             case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST:
475             case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_INSTANT:
476             case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_DELAYED:
477             case AudioAttributes.USAGE_NOTIFICATION_EVENT:
478             case AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY:
479             case AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE:
480             case AudioAttributes.USAGE_ASSISTANCE_SONIFICATION:
481             case AudioAttributes.USAGE_GAME:
482             case AudioAttributes.USAGE_ASSISTANT:
483             case AudioAttributes.USAGE_CALL_ASSISTANT:
484             case AudioAttributes.USAGE_EMERGENCY:
485             case AudioAttributes.USAGE_SAFETY:
486             case AudioAttributes.USAGE_VEHICLE_STATUS:
487             case AudioAttributes.USAGE_ANNOUNCEMENT:
488                 return true;
489             default:
490                 // Virtual usage is hidden and thus it must be taken care here.
491                 return usage == AudioManagerHelper.getUsageVirtualSource();
492         }
493     }
494 
495     /**
496      * Checks if the audio context is within the valid range from MUSIC to SYSTEM_SOUND
497      */
preconditionCheckAudioContext(@udioContext int audioContext)498     void preconditionCheckAudioContext(@AudioContext int audioContext) {
499 
500         Preconditions.checkArgument(!isInvalidContextId(audioContext)
501                         && mContextToAttributes.indexOfKey(audioContext) >= 0,
502                 "Car audio context %d is invalid", audioContext);
503     }
504 
getAudioAttributesForContext(@udioContext int carAudioContext)505     AudioAttributes[] getAudioAttributesForContext(@AudioContext int carAudioContext) {
506         preconditionCheckAudioContext(carAudioContext);
507         return mContextToAttributes.get(carAudioContext);
508     }
509 
getContextForAttributes(AudioAttributes attributes)510     @AudioContext int getContextForAttributes(AudioAttributes attributes) {
511         return getContextForAudioAttribute(attributes);
512     }
513 
514     /**
515      * @return Context number for a given audio usage, {@code INVALID} if the given usage is
516      * unrecognized.
517      */
getContextForAudioAttribute(AudioAttributes attributes)518     public @AudioContext int getContextForAudioAttribute(AudioAttributes attributes) {
519         return mAudioAttributesToContext.getOrDefault(
520                 new AudioAttributesWrapper(attributes), INVALID);
521     }
522 
523     /**
524      * Returns an audio attribute for a given usage
525      * @param usage input usage, can be an audio attribute system usage
526      */
getAudioAttributeFromUsage(@ttributeUsage int usage)527     public static AudioAttributes getAudioAttributeFromUsage(@AttributeUsage int usage) {
528         AudioAttributes.Builder builder = new AudioAttributes.Builder();
529         if (AudioAttributes.isSystemUsage(usage)) {
530             builder.setSystemUsage(usage);
531         } else {
532             builder.setUsage(usage);
533         }
534         return builder.build();
535     }
536 
537     /**
538      * Returns an audio attribute wrapper for a given usage
539      * @param usage input usage, can be an audio attribute system usage
540      */
getAudioAttributeWrapperFromUsage( @ttributeUsage int usage)541     public static AudioAttributesWrapper getAudioAttributeWrapperFromUsage(
542             @AttributeUsage int usage) {
543         return new AudioAttributesWrapper(getAudioAttributeFromUsage(usage));
544     }
545 
getUniqueContextsForAudioAttributes(List<AudioAttributes> audioAttributes)546     Set<Integer> getUniqueContextsForAudioAttributes(List<AudioAttributes> audioAttributes) {
547         Objects.requireNonNull(audioAttributes, "Audio attributes can not be null");
548         Set<Integer> uniqueContexts = new ArraySet<>();
549         for (int index = 0; index < audioAttributes.size(); index++) {
550             uniqueContexts.add(getContextForAudioAttribute(audioAttributes.get(index)));
551         }
552 
553         uniqueContexts.remove(INVALID);
554         return uniqueContexts;
555     }
556 
557     @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
dump(IndentingPrintWriter writer)558     void dump(IndentingPrintWriter writer) {
559         writer.println("CarAudioContext");
560         writer.increaseIndent();
561         for (int index = 0; index < mCarAudioContextInfos.size(); index++) {
562             mCarAudioContextInfos.get(index).dump(writer);
563         }
564         writer.decreaseIndent();
565     }
566 
isNotificationAudioAttribute(AudioAttributes attributes)567     static boolean isNotificationAudioAttribute(AudioAttributes attributes) {
568         AudioAttributesWrapper wrapper = new AudioAttributesWrapper(attributes);
569         return getAudioAttributeWrapperFromUsage(AudioAttributes.USAGE_NOTIFICATION).equals(wrapper)
570                 || getAudioAttributeWrapperFromUsage(AudioAttributes.USAGE_NOTIFICATION_EVENT)
571                         .equals(wrapper);
572     }
573 
isCriticalAudioAudioAttribute(AudioAttributes attributes)574     static boolean isCriticalAudioAudioAttribute(AudioAttributes attributes) {
575         AudioAttributesWrapper wrapper = new AudioAttributesWrapper(attributes);
576         return getAudioAttributeWrapperFromUsage(AudioAttributes.USAGE_EMERGENCY).equals(wrapper)
577                 || getAudioAttributeWrapperFromUsage(AudioAttributes.USAGE_SAFETY).equals(wrapper);
578     }
579 
isRingerOrCallAudioAttribute(AudioAttributes attributes)580     static boolean isRingerOrCallAudioAttribute(AudioAttributes attributes) {
581         AudioAttributesWrapper wrapper = new AudioAttributesWrapper(attributes);
582         return getAudioAttributeWrapperFromUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE)
583                 .equals(wrapper)
584                 || getAudioAttributeWrapperFromUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION)
585                         .equals(wrapper)
586                 || getAudioAttributeWrapperFromUsage(AudioAttributes.USAGE_CALL_ASSISTANT)
587                 .equals(wrapper)
588                 || getAudioAttributeWrapperFromUsage(
589                         AudioAttributes.USAGE_VOICE_COMMUNICATION_SIGNALLING)
590                         .equals(wrapper);
591 
592     }
593 
toString(@udioContext int audioContext)594     String toString(@AudioContext int audioContext) {
595         String name = mContextToNames.get(audioContext);
596         if (name != null) {
597             return name;
598         }
599         return "Unsupported Context 0x" + Integer.toHexString(audioContext);
600     }
601 
getUniqueAttributesHoldingFocus( List<AudioAttributes> audioAttributes)602     static List<AudioAttributes> getUniqueAttributesHoldingFocus(
603             List<AudioAttributes> audioAttributes) {
604         Set<AudioAttributesWrapper> uniqueAudioAttributes = new ArraySet<>();
605         List<AudioAttributes> uniqueAttributes = new ArrayList<>(uniqueAudioAttributes.size());
606         for (int index = 0; index < audioAttributes.size(); index++) {
607             AudioAttributes audioAttribute = audioAttributes.get(index);
608             if (uniqueAudioAttributes.contains(new AudioAttributesWrapper(audioAttribute))) {
609                 continue;
610             }
611             uniqueAudioAttributes.add(new AudioAttributesWrapper(audioAttributes.get(index)));
612             uniqueAttributes.add(new AudioAttributes.Builder(audioAttribute).build());
613         }
614 
615         return uniqueAttributes;
616     }
617 
getAllContextsIds()618     List<Integer> getAllContextsIds() {
619         List<Integer> contextIds = new ArrayList<>(mContextToAttributes.size());
620         for (int index = 0; index < mContextToAttributes.size(); index++) {
621             if (isInvalidContextId(mContextToAttributes.keyAt(index))) {
622                 continue;
623             }
624             contextIds.add(mContextToAttributes.keyAt(index));
625         }
626         return contextIds;
627     }
628 
getNonCarSystemContextIds()629     static List<Integer> getNonCarSystemContextIds() {
630         return NON_CAR_SYSTEM_CONTEXTS;
631     }
632 
getCarSystemContextIds()633     static List<Integer> getCarSystemContextIds() {
634         return CAR_SYSTEM_CONTEXTS;
635     }
636 
637     /**
638      * Return static list of logical audio attributes grouping.
639      */
getAllContextsInfo()640     public static List<CarAudioContextInfo> getAllContextsInfo() {
641         return CAR_CONTEXT_INFO;
642     }
643 
getAllNonCarSystemContextsInfo()644     static List<CarAudioContextInfo> getAllNonCarSystemContextsInfo() {
645         return List.of(
646                 CAR_CONTEXT_INFO_MUSIC,
647                 CAR_CONTEXT_INFO_NAVIGATION,
648                 CAR_CONTEXT_INFO_VOICE_COMMAND,
649                 CAR_CONTEXT_INFO_CALL_RING,
650                 CAR_CONTEXT_INFO_CALL,
651                 CAR_CONTEXT_INFO_ALARM,
652                 CAR_CONTEXT_INFO_NOTIFICATION,
653                 CAR_CONTEXT_INFO_SYSTEM_SOUND
654         );
655     }
656 
getAllCarSystemContextsInfo()657     static List<CarAudioContextInfo> getAllCarSystemContextsInfo() {
658         return List.of(
659                 CAR_CONTEXT_INFO_EMERGENCY,
660                 CAR_CONTEXT_INFO_SAFETY,
661                 CAR_CONTEXT_INFO_VEHICLE_STATUS,
662                 CAR_CONTEXT_INFO_ANNOUNCEMENT
663         );
664     }
665 
getContextsToDuck(@udioContext int context)666     static List<Integer> getContextsToDuck(@AudioContext int context) {
667         return sContextsToDuck.get(context);
668     }
669 
getInvalidContext()670     static @AudioContext int getInvalidContext() {
671         return INVALID;
672     }
673 
isInvalidContextId(@udioContext int id)674     static boolean isInvalidContextId(@AudioContext int id) {
675         return id == INVALID;
676     }
677 
validateAllAudioAttributesSupported(List<Integer> contexts)678     boolean validateAllAudioAttributesSupported(List<Integer> contexts) {
679         ArraySet<AudioAttributesWrapper> supportedAudioAttributes =
680                 new ArraySet<>(ALL_SUPPORTED_ATTRIBUTES);
681 
682         for (int contextIndex = 0; contextIndex < contexts.size(); contextIndex++) {
683             int contextId = contexts.get(contextIndex);
684             AudioAttributes[] attributes = getAudioAttributesForContext(contextId);
685             List<AudioAttributesWrapper> wrappers = new ArrayList<>(attributes.length);
686             for (int index = 0; index < attributes.length; index++) {
687                 wrappers.add(new AudioAttributesWrapper(attributes[index]));
688             }
689 
690             supportedAudioAttributes.removeAll(wrappers);
691         }
692 
693         for (int index = 0; index < supportedAudioAttributes.size(); index++) {
694             AudioAttributesWrapper wrapper =  supportedAudioAttributes.valueAt(index);
695             Slogf.e(CarLog.TAG_AUDIO,
696                     "AudioAttribute %s not supported in current configuration", wrapper);
697         }
698 
699         return supportedAudioAttributes.isEmpty();
700     }
701 
covertAttributesToUsage(AudioAttributes[] audioAttributes)702     private static int[] covertAttributesToUsage(AudioAttributes[] audioAttributes) {
703         int[] usages = new int[audioAttributes.length];
704         for (int index = 0; index < audioAttributes.length; index++) {
705             usages[index] = audioAttributes[index].getSystemUsage();
706         }
707         return usages;
708     }
709 
710     /**
711      * Class wraps an audio attributes object. This can be used for comparing audio attributes.
712      * Current the audio attributes class compares all the attributes in the two objects.
713      * In automotive only the audio attribute usage is currently used, thus this class can be used
714      * to compare that audio attribute usage.
715      */
716     public static final class AudioAttributesWrapper {
717 
718         private final AudioAttributes mAudioAttributes;
719 
720         @VisibleForTesting
AudioAttributesWrapper(AudioAttributes audioAttributes)721         AudioAttributesWrapper(AudioAttributes audioAttributes) {
722             mAudioAttributes = audioAttributes;
723         }
724 
725         @Override
equals(Object object)726         public boolean equals(Object object) {
727             if (this == object) return true;
728             if (object == null || !(object instanceof AudioAttributesWrapper)) {
729                 return false;
730             }
731 
732             AudioAttributesWrapper that = (AudioAttributesWrapper) object;
733 
734             return mAudioAttributes.getSystemUsage() == that.mAudioAttributes.getSystemUsage();
735         }
736 
737         @Override
hashCode()738         public int hashCode() {
739             return Integer.hashCode(mAudioAttributes.getSystemUsage());
740         }
741 
742         @Override
toString()743         public String toString() {
744             return mAudioAttributes.toString();
745         }
746 
747         /**
748          * Returns the audio attributes for the wrapper
749          */
getAudioAttributes()750         public AudioAttributes getAudioAttributes() {
751             return mAudioAttributes;
752         }
753     }
754 }
755