| /* |
| * Copyright (C) 2020 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.car.audio; |
| |
| import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.BOILERPLATE_CODE; |
| |
| import android.annotation.IntDef; |
| import android.media.AudioAttributes; |
| import android.media.AudioAttributes.AttributeUsage; |
| import android.util.SparseArray; |
| import android.util.SparseIntArray; |
| |
| import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport; |
| import com.android.internal.util.Preconditions; |
| |
| import java.lang.annotation.Retention; |
| import java.lang.annotation.RetentionPolicy; |
| import java.util.Arrays; |
| import java.util.HashSet; |
| import java.util.Set; |
| |
| /** |
| * Groupings of {@link AttributeUsage}s to simplify configuration of car audio routing, volume |
| * groups, and focus interactions for similar usages. |
| */ |
| public final class CarAudioContext { |
| /* |
| * Shouldn't be used |
| * ::android::hardware::automotive::audiocontrol::V1_0::ContextNumber.INVALID |
| */ |
| static final int INVALID = 0; |
| /* |
| * Music playback |
| * ::android::hardware::automotive::audiocontrol::V1_0::ContextNumber.INVALID implicitly + 1 |
| */ |
| static final int MUSIC = 1; |
| /* |
| * Navigation directions |
| * ::android::hardware::automotive::audiocontrol::V1_0::ContextNumber.MUSIC implicitly + 1 |
| */ |
| static final int NAVIGATION = 2; |
| /* |
| * Voice command session |
| * ::android::hardware::automotive::audiocontrol::V1_0::ContextNumber.NAVIGATION implicitly + 1 |
| */ |
| static final int VOICE_COMMAND = 3; |
| /* |
| * Voice call ringing |
| * ::android::hardware::automotive::audiocontrol::V1_0::ContextNumber |
| * .VOICE_COMMAND implicitly + 1 |
| */ |
| static final int CALL_RING = 4; |
| /* |
| * Voice call |
| * ::android::hardware::automotive::audiocontrol::V1_0::ContextNumber.CALL_RING implicitly + 1 |
| */ |
| static final int CALL = 5; |
| /* |
| * Alarm sound from Android |
| * ::android::hardware::automotive::audiocontrol::V1_0::ContextNumber.CALL implicitly + 1 |
| */ |
| static final int ALARM = 6; |
| /* |
| * Notifications |
| * ::android::hardware::automotive::audiocontrol::V1_0::ContextNumber.ALARM implicitly + 1 |
| */ |
| static final int NOTIFICATION = 7; |
| /* |
| * System sounds |
| * ::android::hardware::automotive::audiocontrol::V1_0::ContextNumber |
| * .NOTIFICATION implicitly + 1 |
| */ |
| static final int SYSTEM_SOUND = 8; |
| /* |
| * Emergency related sounds such as collision warnings |
| */ |
| static final int EMERGENCY = 9; |
| /* |
| * Safety sounds such as obstacle detection when backing up or when changing lanes |
| */ |
| static final int SAFETY = 10; |
| /* |
| * Vehicle Status related sounds such as check engine light or seat belt chimes |
| */ |
| static final int VEHICLE_STATUS = 11; |
| /* |
| * Announcement such as traffic announcements |
| */ |
| static final int ANNOUNCEMENT = 12; |
| |
| static final int[] CONTEXTS = { |
| MUSIC, |
| NAVIGATION, |
| VOICE_COMMAND, |
| CALL_RING, |
| CALL, |
| ALARM, |
| NOTIFICATION, |
| SYSTEM_SOUND, |
| EMERGENCY, |
| SAFETY, |
| VEHICLE_STATUS, |
| ANNOUNCEMENT |
| }; |
| |
| private static final SparseArray<String> CONTEXT_NAMES = new SparseArray<>(CONTEXTS.length + 1); |
| static { |
| CONTEXT_NAMES.append(INVALID, "INVALID"); |
| CONTEXT_NAMES.append(MUSIC, "MUSIC"); |
| CONTEXT_NAMES.append(NAVIGATION, "NAVIGATION"); |
| CONTEXT_NAMES.append(VOICE_COMMAND, "VOICE_COMMAND"); |
| CONTEXT_NAMES.append(CALL_RING, "CALL_RING"); |
| CONTEXT_NAMES.append(CALL, "CALL"); |
| CONTEXT_NAMES.append(ALARM, "ALARM"); |
| CONTEXT_NAMES.append(NOTIFICATION, "NOTIFICATION"); |
| CONTEXT_NAMES.append(SYSTEM_SOUND, "SYSTEM_SOUND"); |
| CONTEXT_NAMES.append(EMERGENCY, "EMERGENCY"); |
| CONTEXT_NAMES.append(SAFETY, "SAFETY"); |
| CONTEXT_NAMES.append(VEHICLE_STATUS, "VEHICLE_STATUS"); |
| CONTEXT_NAMES.append(ANNOUNCEMENT, "ANNOUNCEMENT"); |
| } |
| |
| private static final SparseArray<int[]> CONTEXT_TO_USAGES = new SparseArray<>(); |
| |
| static { |
| CONTEXT_TO_USAGES.put(MUSIC, |
| new int[]{ |
| AudioAttributes.USAGE_UNKNOWN, |
| AudioAttributes.USAGE_GAME, |
| AudioAttributes.USAGE_MEDIA |
| }); |
| |
| CONTEXT_TO_USAGES.put(NAVIGATION, |
| new int[]{ |
| AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE |
| }); |
| |
| CONTEXT_TO_USAGES.put(VOICE_COMMAND, |
| new int[]{ |
| AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY, |
| AudioAttributes.USAGE_ASSISTANT |
| }); |
| |
| CONTEXT_TO_USAGES.put(CALL_RING, |
| new int[]{ |
| AudioAttributes.USAGE_NOTIFICATION_RINGTONE |
| }); |
| |
| CONTEXT_TO_USAGES.put(CALL, |
| new int[]{ |
| AudioAttributes.USAGE_VOICE_COMMUNICATION, |
| AudioAttributes.USAGE_VOICE_COMMUNICATION_SIGNALLING |
| }); |
| |
| CONTEXT_TO_USAGES.put(ALARM, |
| new int[]{ |
| AudioAttributes.USAGE_ALARM |
| }); |
| |
| 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 |
| }); |
| |
| CONTEXT_TO_USAGES.put(SYSTEM_SOUND, |
| new int[]{ |
| AudioAttributes.USAGE_ASSISTANCE_SONIFICATION |
| }); |
| |
| CONTEXT_TO_USAGES.put(EMERGENCY, |
| new int[]{ |
| AudioAttributes.USAGE_EMERGENCY |
| }); |
| |
| CONTEXT_TO_USAGES.put(SAFETY, |
| new int[]{ |
| AudioAttributes.USAGE_SAFETY |
| }); |
| |
| CONTEXT_TO_USAGES.put(VEHICLE_STATUS, |
| new int[]{ |
| AudioAttributes.USAGE_VEHICLE_STATUS |
| }); |
| |
| CONTEXT_TO_USAGES.put(ANNOUNCEMENT, |
| new int[]{ |
| AudioAttributes.USAGE_ANNOUNCEMENT |
| }); |
| |
| CONTEXT_TO_USAGES.put(INVALID, |
| new int[]{ |
| AudioAttributes.USAGE_VIRTUAL_SOURCE |
| }); |
| } |
| |
| private static final SparseIntArray USAGE_TO_CONTEXT = new SparseIntArray(); |
| |
| static { |
| for (int i = 0; i < CONTEXT_TO_USAGES.size(); i++) { |
| @AudioContext int audioContext = CONTEXT_TO_USAGES.keyAt(i); |
| @AttributeUsage int[] usages = CONTEXT_TO_USAGES.valueAt(i); |
| for (@AttributeUsage int usage : usages) { |
| USAGE_TO_CONTEXT.put(usage, audioContext); |
| } |
| } |
| } |
| |
| private CarAudioContext() { |
| } |
| |
| /** |
| * Checks if the audio context is within the valid range from MUSIC to SYSTEM_SOUND |
| */ |
| static void preconditionCheckAudioContext(@AudioContext int audioContext) { |
| Preconditions.checkArgument(Arrays.binarySearch(CONTEXTS, audioContext) >= 0, |
| "audioContext %d is invalid", audioContext); |
| } |
| |
| static @AttributeUsage int[] getUsagesForContext(@AudioContext int carAudioContext) { |
| preconditionCheckAudioContext(carAudioContext); |
| return CONTEXT_TO_USAGES.get(carAudioContext); |
| } |
| |
| static @AudioContext int getContextForAttributes(AudioAttributes attributes) { |
| return getContextForUsage(attributes.getSystemUsage()); |
| } |
| |
| /** |
| * @return Context number for a given audio usage, {@code INVALID} if the given usage is |
| * unrecognized. |
| */ |
| static @AudioContext int getContextForUsage(@AttributeUsage int audioUsage) { |
| return USAGE_TO_CONTEXT.get(audioUsage, INVALID); |
| } |
| |
| static Set<Integer> getUniqueContextsForUsages(int[] usages) { |
| Set<Integer> uniqueContexts = new HashSet<>(); |
| for (int i = 0; i < usages.length; i++) { |
| uniqueContexts.add(getContextForUsage(usages[i])); |
| } |
| |
| return uniqueContexts; |
| } |
| |
| static boolean isCriticalAudioContext(@CarAudioContext.AudioContext int audioContext) { |
| return CarAudioContext.EMERGENCY == audioContext || CarAudioContext.SAFETY == audioContext; |
| } |
| |
| @ExcludeFromCodeCoverageGeneratedReport(reason = BOILERPLATE_CODE) |
| static String toString(@AudioContext int audioContext) { |
| String name = CONTEXT_NAMES.get(audioContext); |
| if (name != null) { |
| return name; |
| } |
| return "Unsupported Context 0x" + Integer.toHexString(audioContext); |
| } |
| |
| @IntDef({ |
| INVALID, |
| MUSIC, |
| NAVIGATION, |
| VOICE_COMMAND, |
| CALL_RING, |
| CALL, |
| ALARM, |
| NOTIFICATION, |
| SYSTEM_SOUND, |
| EMERGENCY, |
| SAFETY, |
| VEHICLE_STATUS, |
| ANNOUNCEMENT |
| }) |
| @Retention(RetentionPolicy.SOURCE) |
| public @interface AudioContext { |
| } |
| }; |