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 android.media.AudioAttributes;
20 import android.media.AudioAttributes.AttributeUsage;
21 import android.util.SparseArray;
22 import android.util.SparseIntArray;
23 
24 import androidx.annotation.IntDef;
25 
26 import com.android.internal.util.Preconditions;
27 
28 import java.lang.annotation.Retention;
29 import java.lang.annotation.RetentionPolicy;
30 import java.util.Arrays;
31 
32 /**
33  * Groupings of {@link AttributeUsage}s to simplify configuration of car audio routing, volume
34  * groups, and focus interactions for similar usages.
35  */
36 public final class CarAudioContext {
37     /*
38      * Shouldn't be used
39      * ::android::hardware::automotive::audiocontrol::V1_0::ContextNumber.INVALID
40      */
41     static final int INVALID = 0;
42     /*
43      * Music playback
44      * ::android::hardware::automotive::audiocontrol::V1_0::ContextNumber.INVALID implicitly + 1
45      */
46     static final int MUSIC = 1;
47     /*
48      * Navigation directions
49      * ::android::hardware::automotive::audiocontrol::V1_0::ContextNumber.MUSIC implicitly + 1
50      */
51     static final int NAVIGATION = 2;
52     /*
53      * Voice command session
54      * ::android::hardware::automotive::audiocontrol::V1_0::ContextNumber.NAVIGATION implicitly + 1
55      */
56     static final int VOICE_COMMAND = 3;
57     /*
58      * Voice call ringing
59      * ::android::hardware::automotive::audiocontrol::V1_0::ContextNumber
60      *     .VOICE_COMMAND implicitly + 1
61      */
62     static final int CALL_RING = 4;
63     /*
64      * Voice call
65      * ::android::hardware::automotive::audiocontrol::V1_0::ContextNumber.CALL_RING implicitly + 1
66      */
67     static final int CALL = 5;
68     /*
69      * Alarm sound from Android
70      * ::android::hardware::automotive::audiocontrol::V1_0::ContextNumber.CALL implicitly + 1
71      */
72     static final int ALARM = 6;
73     /*
74      * Notifications
75      * ::android::hardware::automotive::audiocontrol::V1_0::ContextNumber.ALARM implicitly + 1
76      */
77     static final int NOTIFICATION = 7;
78     /*
79      * System sounds
80      * ::android::hardware::automotive::audiocontrol::V1_0::ContextNumber
81      *     .NOTIFICATION implicitly + 1
82      */
83     static final int SYSTEM_SOUND = 8;
84     /*
85      * Emergency related sounds such as collision warnings
86      */
87     static final int EMERGENCY = 9;
88     /*
89      * Safety sounds such as obstacle detection when backing up or when changing lanes
90      */
91     static final int SAFETY = 10;
92     /*
93      * Vehicle Status related sounds such as check engine light or seat belt chimes
94      */
95     static final int VEHICLE_STATUS = 11;
96     /*
97      * Announcement such as traffic announcements
98      */
99     static final int ANNOUNCEMENT = 12;
100 
101     static final int[] CONTEXTS = {
102             MUSIC,
103             NAVIGATION,
104             VOICE_COMMAND,
105             CALL_RING,
106             CALL,
107             ALARM,
108             NOTIFICATION,
109             SYSTEM_SOUND,
110             EMERGENCY,
111             SAFETY,
112             VEHICLE_STATUS,
113             ANNOUNCEMENT
114     };
115 
116     private static final SparseArray<String> CONTEXT_NAMES = new SparseArray<>(CONTEXTS.length + 1);
117     static {
CONTEXT_NAMES.append(INVALID, "INVALID")118         CONTEXT_NAMES.append(INVALID, "INVALID");
CONTEXT_NAMES.append(MUSIC, "MUSIC")119         CONTEXT_NAMES.append(MUSIC, "MUSIC");
CONTEXT_NAMES.append(NAVIGATION, "NAVIGATION")120         CONTEXT_NAMES.append(NAVIGATION, "NAVIGATION");
CONTEXT_NAMES.append(VOICE_COMMAND, "VOICE_COMMAND")121         CONTEXT_NAMES.append(VOICE_COMMAND, "VOICE_COMMAND");
CONTEXT_NAMES.append(CALL_RING, "CALL_RING")122         CONTEXT_NAMES.append(CALL_RING, "CALL_RING");
CONTEXT_NAMES.append(CALL, "CALL")123         CONTEXT_NAMES.append(CALL, "CALL");
CONTEXT_NAMES.append(ALARM, "ALARM")124         CONTEXT_NAMES.append(ALARM, "ALARM");
CONTEXT_NAMES.append(NOTIFICATION, "NOTIFICATION")125         CONTEXT_NAMES.append(NOTIFICATION, "NOTIFICATION");
CONTEXT_NAMES.append(SYSTEM_SOUND, "SYSTEM_SOUND")126         CONTEXT_NAMES.append(SYSTEM_SOUND, "SYSTEM_SOUND");
CONTEXT_NAMES.append(EMERGENCY, "EMERGENCY")127         CONTEXT_NAMES.append(EMERGENCY, "EMERGENCY");
CONTEXT_NAMES.append(SAFETY, "SAFETY")128         CONTEXT_NAMES.append(SAFETY, "SAFETY");
CONTEXT_NAMES.append(VEHICLE_STATUS, "VEHICLE_STATUS")129         CONTEXT_NAMES.append(VEHICLE_STATUS, "VEHICLE_STATUS");
CONTEXT_NAMES.append(ANNOUNCEMENT, "ANNOUNCEMENT")130         CONTEXT_NAMES.append(ANNOUNCEMENT, "ANNOUNCEMENT");
131     }
132 
133     private static final SparseArray<int[]> CONTEXT_TO_USAGES = new SparseArray<>();
134 
135     static {
CONTEXT_TO_USAGES.put(MUSIC, new int[]{ AudioAttributes.USAGE_UNKNOWN, AudioAttributes.USAGE_GAME, AudioAttributes.USAGE_MEDIA })136         CONTEXT_TO_USAGES.put(MUSIC,
137                 new int[]{
138                         AudioAttributes.USAGE_UNKNOWN,
139                         AudioAttributes.USAGE_GAME,
140                         AudioAttributes.USAGE_MEDIA
141                 });
142 
CONTEXT_TO_USAGES.put(NAVIGATION, new int[]{ AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE })143         CONTEXT_TO_USAGES.put(NAVIGATION,
144                 new int[]{
145                         AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE
146                 });
147 
CONTEXT_TO_USAGES.put(VOICE_COMMAND, new int[]{ AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY, AudioAttributes.USAGE_ASSISTANT })148         CONTEXT_TO_USAGES.put(VOICE_COMMAND,
149                 new int[]{
150                         AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY,
151                         AudioAttributes.USAGE_ASSISTANT
152                 });
153 
CONTEXT_TO_USAGES.put(CALL_RING, new int[]{ AudioAttributes.USAGE_NOTIFICATION_RINGTONE })154         CONTEXT_TO_USAGES.put(CALL_RING,
155                 new int[]{
156                         AudioAttributes.USAGE_NOTIFICATION_RINGTONE
157                 });
158 
CONTEXT_TO_USAGES.put(CALL, new int[]{ AudioAttributes.USAGE_VOICE_COMMUNICATION, AudioAttributes.USAGE_VOICE_COMMUNICATION_SIGNALLING })159         CONTEXT_TO_USAGES.put(CALL,
160                 new int[]{
161                         AudioAttributes.USAGE_VOICE_COMMUNICATION,
162                         AudioAttributes.USAGE_VOICE_COMMUNICATION_SIGNALLING
163                 });
164 
CONTEXT_TO_USAGES.put(ALARM, new int[]{ AudioAttributes.USAGE_ALARM })165         CONTEXT_TO_USAGES.put(ALARM,
166                 new int[]{
167                         AudioAttributes.USAGE_ALARM
168                 });
169 
CONTEXT_TO_USAGES.put(NOTIFICATION, new int[]{ AudioAttributes.USAGE_NOTIFICATION, AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST, AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_INSTANT, AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_DELAYED, AudioAttributes.USAGE_NOTIFICATION_EVENT })170         CONTEXT_TO_USAGES.put(NOTIFICATION,
171                 new int[]{
172                         AudioAttributes.USAGE_NOTIFICATION,
173                         AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST,
174                         AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_INSTANT,
175                         AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_DELAYED,
176                         AudioAttributes.USAGE_NOTIFICATION_EVENT
177                 });
178 
CONTEXT_TO_USAGES.put(SYSTEM_SOUND, new int[]{ AudioAttributes.USAGE_ASSISTANCE_SONIFICATION })179         CONTEXT_TO_USAGES.put(SYSTEM_SOUND,
180                 new int[]{
181                         AudioAttributes.USAGE_ASSISTANCE_SONIFICATION
182                 });
183 
CONTEXT_TO_USAGES.put(EMERGENCY, new int[]{ AudioAttributes.USAGE_EMERGENCY })184         CONTEXT_TO_USAGES.put(EMERGENCY,
185                 new int[]{
186                         AudioAttributes.USAGE_EMERGENCY
187                 });
188 
CONTEXT_TO_USAGES.put(SAFETY, new int[]{ AudioAttributes.USAGE_SAFETY })189         CONTEXT_TO_USAGES.put(SAFETY,
190                 new int[]{
191                         AudioAttributes.USAGE_SAFETY
192                 });
193 
CONTEXT_TO_USAGES.put(VEHICLE_STATUS, new int[]{ AudioAttributes.USAGE_VEHICLE_STATUS })194         CONTEXT_TO_USAGES.put(VEHICLE_STATUS,
195                 new int[]{
196                         AudioAttributes.USAGE_VEHICLE_STATUS
197                 });
198 
CONTEXT_TO_USAGES.put(ANNOUNCEMENT, new int[]{ AudioAttributes.USAGE_ANNOUNCEMENT })199         CONTEXT_TO_USAGES.put(ANNOUNCEMENT,
200                 new int[]{
201                         AudioAttributes.USAGE_ANNOUNCEMENT
202                 });
203 
CONTEXT_TO_USAGES.put(INVALID, new int[]{ AudioAttributes.USAGE_VIRTUAL_SOURCE })204         CONTEXT_TO_USAGES.put(INVALID,
205                 new int[]{
206                         AudioAttributes.USAGE_VIRTUAL_SOURCE
207                 });
208     }
209 
210     private static final SparseIntArray USAGE_TO_CONTEXT = new SparseIntArray();
211 
212     static {
213         for (int i = 0; i < CONTEXT_TO_USAGES.size(); i++) {
214             @AudioContext int audioContext = CONTEXT_TO_USAGES.keyAt(i);
215             @AttributeUsage int[] usages = CONTEXT_TO_USAGES.valueAt(i);
216             for (@AttributeUsage int usage : usages) {
USAGE_TO_CONTEXT.put(usage, audioContext)217                 USAGE_TO_CONTEXT.put(usage, audioContext);
218             }
219         }
220     }
221 
222     /**
223      * Checks if the audio context is within the valid range from MUSIC to SYSTEM_SOUND
224      */
preconditionCheckAudioContext(@udioContext int audioContext)225     static void preconditionCheckAudioContext(@AudioContext int audioContext) {
226         Preconditions.checkArgument(Arrays.binarySearch(CONTEXTS, audioContext) >= 0,
227                 "audioContext %d is invalid", audioContext);
228     }
229 
getUsagesForContext(@udioContext int carAudioContext)230     static @AttributeUsage int[] getUsagesForContext(@AudioContext int carAudioContext) {
231         preconditionCheckAudioContext(carAudioContext);
232         return CONTEXT_TO_USAGES.get(carAudioContext);
233     }
234 
235     /**
236      * @return Context number for a given audio usage, {@code INVALID} if the given usage is
237      * unrecognized.
238      */
getContextForUsage(@ttributeUsage int audioUsage)239     static @AudioContext int getContextForUsage(@AttributeUsage int audioUsage) {
240         return USAGE_TO_CONTEXT.get(audioUsage, INVALID);
241     }
242 
toString(@udioContext int audioContext)243     static String toString(@AudioContext int audioContext) {
244         String name = CONTEXT_NAMES.get(audioContext);
245         if (name != null) {
246             return name;
247         }
248         return "Unsupported Context 0x" + Integer.toHexString(audioContext);
249     }
250 
251     @IntDef({
252             INVALID,
253             MUSIC,
254             NAVIGATION,
255             VOICE_COMMAND,
256             CALL_RING,
257             CALL,
258             ALARM,
259             NOTIFICATION,
260             SYSTEM_SOUND,
261             EMERGENCY,
262             SAFETY,
263             VEHICLE_STATUS,
264             ANNOUNCEMENT
265     })
266     @Retention(RetentionPolicy.SOURCE)
267     public @interface AudioContext {
268     }
269 };
270