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