• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 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 android.car.media;
17 
18 import android.annotation.IntDef;
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.annotation.RequiresPermission;
22 import android.annotation.SystemApi;
23 import android.annotation.TestApi;
24 import android.car.Car;
25 import android.car.CarLibLog;
26 import android.car.CarManagerBase;
27 import android.car.annotation.AddedInOrBefore;
28 import android.car.annotation.ApiRequirements;
29 import android.media.AudioAttributes;
30 import android.media.AudioDeviceAttributes;
31 import android.media.AudioDeviceInfo;
32 import android.media.AudioManager;
33 import android.os.Bundle;
34 import android.os.Handler;
35 import android.os.IBinder;
36 import android.os.Looper;
37 import android.os.Message;
38 import android.os.RemoteException;
39 import android.util.Log;
40 
41 import com.android.car.internal.annotation.AttributeUsage;
42 
43 import java.lang.annotation.Retention;
44 import java.lang.annotation.RetentionPolicy;
45 import java.util.ArrayList;
46 import java.util.Collections;
47 import java.util.HashSet;
48 import java.util.List;
49 import java.util.Objects;
50 import java.util.Set;
51 import java.util.concurrent.CopyOnWriteArrayList;
52 
53 /**
54  * APIs for handling audio in a car.
55  *
56  * In a car environment, we introduced the support to turn audio dynamic routing on /off by
57  * setting the "audioUseDynamicRouting" attribute in config.xml
58  *
59  * When audio dynamic routing is enabled:
60  * - Audio devices are grouped into zones
61  * - There is at least one primary zone, and extra secondary zones such as RSE
62  *   (Reat Seat Entertainment)
63  * - Within each zone, audio devices are grouped into volume groups for volume control
64  * - Audio is assigned to an audio device based on its AudioAttributes usage
65  *
66  * When audio dynamic routing is disabled:
67  * - There is exactly one audio zone, which is the primary zone
68  * - Each volume group represents a controllable STREAM_TYPE, same as AudioManager
69  */
70 public final class CarAudioManager extends CarManagerBase {
71 
72     /**
73      * Zone id of the primary audio zone.
74      * @hide
75      */
76     @SystemApi
77     @AddedInOrBefore(majorVersion = 33)
78     public static final int PRIMARY_AUDIO_ZONE = 0x0;
79 
80     /**
81      * Zone id of the invalid audio zone.
82      * @hide
83      */
84     @SystemApi
85     @AddedInOrBefore(majorVersion = 33)
86     public static final int INVALID_AUDIO_ZONE = 0xffffffff;
87 
88     /**
89      * This is used to determine if dynamic routing is enabled via
90      * {@link #isAudioFeatureEnabled()}
91      */
92     @AddedInOrBefore(majorVersion = 33)
93     public static final int AUDIO_FEATURE_DYNAMIC_ROUTING = 0x1;
94 
95     /**
96      * This is used to determine if volume group muting is enabled via
97      * {@link #isAudioFeatureEnabled()}
98      *
99      * <p>
100      * If enabled, car volume group muting APIs can be used to mute each volume group,
101      * also car volume group muting changed callback will be called upon group mute changes. If
102      * disabled, car volume will toggle master mute instead.
103      */
104     @AddedInOrBefore(majorVersion = 33)
105     public static final int AUDIO_FEATURE_VOLUME_GROUP_MUTING = 0x2;
106 
107     /** @hide */
108     @IntDef(flag = false, prefix = "AUDIO_FEATURE", value = {
109             AUDIO_FEATURE_DYNAMIC_ROUTING,
110             AUDIO_FEATURE_VOLUME_GROUP_MUTING
111     })
112     @Retention(RetentionPolicy.SOURCE)
113     public @interface CarAudioFeature {}
114 
115     /**
116      * Volume Group ID when volume group not found.
117      * @hide
118      */
119     @AddedInOrBefore(majorVersion = 33)
120     public static final int INVALID_VOLUME_GROUP_ID = -1;
121 
122     /**
123      * Extra for {@link android.media.AudioAttributes.Builder#addBundle(Bundle)}: when used in an
124      * {@link android.media.AudioFocusRequest}, the requester should receive all audio focus events,
125      * including {@link android.media.AudioManager#AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK}.
126      * The requester must hold {@link Car#PERMISSION_RECEIVE_CAR_AUDIO_DUCKING_EVENTS}; otherwise,
127      * this extra is ignored.
128      *
129      * @hide
130      */
131     @SystemApi
132     @AddedInOrBefore(majorVersion = 33)
133     public static final String AUDIOFOCUS_EXTRA_RECEIVE_DUCKING_EVENTS =
134             "android.car.media.AUDIOFOCUS_EXTRA_RECEIVE_DUCKING_EVENTS";
135 
136     /**
137      * Extra for {@link android.media.AudioAttributes.Builder#addBundle(Bundle)}: when used in an
138      * {@link android.media.AudioFocusRequest}, the requester should receive all audio focus for the
139      * the zone. If the zone id is not defined: the audio focus request will default to the
140      * currently mapped zone for the requesting uid or {@link CarAudioManager.PRIMARY_AUDIO_ZONE}
141      * if no uid mapping currently exist.
142      *
143      * @hide
144      */
145     @AddedInOrBefore(majorVersion = 33)
146     public static final String AUDIOFOCUS_EXTRA_REQUEST_ZONE_ID =
147             "android.car.media.AUDIOFOCUS_EXTRA_REQUEST_ZONE_ID";
148 
149     private final ICarAudio mService;
150     private final CopyOnWriteArrayList<CarVolumeCallback> mCarVolumeCallbacks;
151     private final AudioManager mAudioManager;
152 
153     private final EventHandler mEventHandler;
154 
155     private final ICarVolumeCallback mCarVolumeCallbackImpl =
156             new android.car.media.ICarVolumeCallback.Stub() {
157         @Override
158         public void onGroupVolumeChanged(int zoneId, int groupId, int flags) {
159             mEventHandler.dispatchOnGroupVolumeChanged(zoneId, groupId, flags);
160         }
161 
162         @Override
163         public void onGroupMuteChanged(int zoneId, int groupId, int flags) {
164             mEventHandler.dispatchOnGroupMuteChanged(zoneId, groupId, flags);
165         }
166 
167         @Override
168         public void onMasterMuteChanged(int zoneId, int flags) {
169             mEventHandler.dispatchOnMasterMuteChanged(zoneId, flags);
170         }
171     };
172 
173     /**
174      * @return Whether dynamic routing is enabled or not.
175      *
176      * @deprecated use {@link #isAudioFeatureEnabled(AUDIO_FEATURE_DYNAMIC_ROUTING)} instead.
177      *
178      * @hide
179      */
180     @TestApi
181     @Deprecated
182     @AddedInOrBefore(majorVersion = 33)
isDynamicRoutingEnabled()183     public boolean isDynamicRoutingEnabled() {
184         return isAudioFeatureEnabled(AUDIO_FEATURE_DYNAMIC_ROUTING);
185     }
186 
187     /**
188      * Determines if an audio feature is enabled.
189      *
190      * @param audioFeature audio feature to query, can be {@link #AUDIO_FEATURE_DYNAMIC_ROUTING} or
191      * {@link #AUDIO_FEATURE_VOLUME_GROUP_MUTING}
192      * @return Returns {@code true} if the feature is enabled, {@code false} otherwise.
193      */
194     @AddedInOrBefore(majorVersion = 33)
isAudioFeatureEnabled(@arAudioFeature int audioFeature)195     public boolean isAudioFeatureEnabled(@CarAudioFeature int audioFeature) {
196         try {
197             return mService.isAudioFeatureEnabled(audioFeature);
198         } catch (RemoteException e) {
199             return handleRemoteExceptionFromCarService(e, false);
200         }
201     }
202 
203     /**
204      * Sets the volume index for a volume group in primary zone.
205      *
206      * @see {@link #setGroupVolume(int, int, int, int)}
207      * @hide
208      */
209     @SystemApi
210     @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
211     @AddedInOrBefore(majorVersion = 33)
setGroupVolume(int groupId, int index, int flags)212     public void setGroupVolume(int groupId, int index, int flags) {
213         setGroupVolume(PRIMARY_AUDIO_ZONE, groupId, index, flags);
214     }
215 
216     /**
217      * Sets the volume index for a volume group.
218      *
219      * @param zoneId The zone id whose volume group is affected.
220      * @param groupId The volume group id whose volume index should be set.
221      * @param index The volume index to set. See
222      *            {@link #getGroupMaxVolume(int, int)} for the largest valid value.
223      * @param flags One or more flags (e.g., {@link android.media.AudioManager#FLAG_SHOW_UI},
224      *              {@link android.media.AudioManager#FLAG_PLAY_SOUND})
225      * @hide
226      */
227     @SystemApi
228     @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
229     @AddedInOrBefore(majorVersion = 33)
setGroupVolume(int zoneId, int groupId, int index, int flags)230     public void setGroupVolume(int zoneId, int groupId, int index, int flags) {
231         try {
232             mService.setGroupVolume(zoneId, groupId, index, flags);
233         } catch (RemoteException e) {
234             handleRemoteExceptionFromCarService(e);
235         }
236     }
237 
238     /**
239      * Returns the maximum volume index for a volume group in primary zone.
240      *
241      * @see {@link #getGroupMaxVolume(int, int)}
242      * @hide
243      */
244     @SystemApi
245     @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
246     @AddedInOrBefore(majorVersion = 33)
getGroupMaxVolume(int groupId)247     public int getGroupMaxVolume(int groupId) {
248         return getGroupMaxVolume(PRIMARY_AUDIO_ZONE, groupId);
249     }
250 
251     /**
252      * Returns the maximum volume index for a volume group.
253      *
254      * @param zoneId The zone id whose volume group is queried.
255      * @param groupId The volume group id whose maximum volume index is returned.
256      * @return The maximum valid volume index for the given group.
257      * @hide
258      */
259     @SystemApi
260     @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
261     @AddedInOrBefore(majorVersion = 33)
getGroupMaxVolume(int zoneId, int groupId)262     public int getGroupMaxVolume(int zoneId, int groupId) {
263         try {
264             return mService.getGroupMaxVolume(zoneId, groupId);
265         } catch (RemoteException e) {
266             return handleRemoteExceptionFromCarService(e, 0);
267         }
268     }
269 
270     /**
271      * Returns the minimum volume index for a volume group in primary zone.
272      *
273      * @see {@link #getGroupMinVolume(int, int)}
274      * @hide
275      */
276     @SystemApi
277     @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
278     @AddedInOrBefore(majorVersion = 33)
getGroupMinVolume(int groupId)279     public int getGroupMinVolume(int groupId) {
280         return getGroupMinVolume(PRIMARY_AUDIO_ZONE, groupId);
281     }
282 
283     /**
284      * Returns the minimum volume index for a volume group.
285      *
286      * @param zoneId The zone id whose volume group is queried.
287      * @param groupId The volume group id whose minimum volume index is returned.
288      * @return The minimum valid volume index for the given group, non-negative
289      * @hide
290      */
291     @SystemApi
292     @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
293     @AddedInOrBefore(majorVersion = 33)
getGroupMinVolume(int zoneId, int groupId)294     public int getGroupMinVolume(int zoneId, int groupId) {
295         try {
296             return mService.getGroupMinVolume(zoneId, groupId);
297         } catch (RemoteException e) {
298             return handleRemoteExceptionFromCarService(e, 0);
299         }
300     }
301 
302     /**
303      * Returns the current volume index for a volume group in primary zone.
304      *
305      * @see {@link #getGroupVolume(int, int)}
306      * @hide
307      */
308     @SystemApi
309     @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
310     @AddedInOrBefore(majorVersion = 33)
getGroupVolume(int groupId)311     public int getGroupVolume(int groupId) {
312         return getGroupVolume(PRIMARY_AUDIO_ZONE, groupId);
313     }
314 
315     /**
316      * Returns the current volume index for a volume group.
317      *
318      * @param zoneId The zone id whose volume groups is queried.
319      * @param groupId The volume group id whose volume index is returned.
320      * @return The current volume index for the given group.
321      *
322      * @see #getGroupMaxVolume(int, int)
323      * @see #setGroupVolume(int, int, int, int)
324      * @hide
325      */
326     @SystemApi
327     @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
328     @AddedInOrBefore(majorVersion = 33)
getGroupVolume(int zoneId, int groupId)329     public int getGroupVolume(int zoneId, int groupId) {
330         try {
331             return mService.getGroupVolume(zoneId, groupId);
332         } catch (RemoteException e) {
333             return handleRemoteExceptionFromCarService(e, 0);
334         }
335     }
336 
337     /**
338      * Adjust the relative volume in the front vs back of the vehicle cabin.
339      *
340      * @param value in the range -1.0 to 1.0 for fully toward the back through
341      *              fully toward the front.  0.0 means evenly balanced.
342      *
343      * @see #setBalanceTowardRight(float)
344      * @hide
345      */
346     @SystemApi
347     @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
348     @AddedInOrBefore(majorVersion = 33)
setFadeTowardFront(float value)349     public void setFadeTowardFront(float value) {
350         try {
351             mService.setFadeTowardFront(value);
352         } catch (RemoteException e) {
353             handleRemoteExceptionFromCarService(e);
354         }
355     }
356 
357     /**
358      * Adjust the relative volume on the left vs right side of the vehicle cabin.
359      *
360      * @param value in the range -1.0 to 1.0 for fully toward the left through
361      *              fully toward the right.  0.0 means evenly balanced.
362      *
363      * @see #setFadeTowardFront(float)
364      * @hide
365      */
366     @SystemApi
367     @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
368     @AddedInOrBefore(majorVersion = 33)
setBalanceTowardRight(float value)369     public void setBalanceTowardRight(float value) {
370         try {
371             mService.setBalanceTowardRight(value);
372         } catch (RemoteException e) {
373             handleRemoteExceptionFromCarService(e);
374         }
375     }
376 
377     /**
378      * Queries the system configuration in order to report the available, non-microphone audio
379      * input devices.
380      *
381      * @return An array of strings representing the available input ports.
382      * Each port is identified by it's "address" tag in the audioPolicyConfiguration xml file.
383      * Empty array if we find nothing.
384      *
385      * @see #createAudioPatch(String, int, int)
386      * @see #releaseAudioPatch(CarAudioPatchHandle)
387      *
388      * @deprecated use {@link AudioManager#getDevices(int)} with
389      * {@link AudioManager#GET_DEVICES_INPUTS} instead
390      * @hide
391      */
392     @SystemApi
393     @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS)
394     @Deprecated
395     @AddedInOrBefore(majorVersion = 33)
getExternalSources()396     public @NonNull String[] getExternalSources() {
397         try {
398             return mService.getExternalSources();
399         } catch (RemoteException e) {
400             handleRemoteExceptionFromCarService(e);
401             return new String[0];
402         }
403     }
404 
405     /**
406      * Given an input port identified by getExternalSources(), request that it's audio signal
407      * be routed below the HAL to the output port associated with the given usage.  For example,
408      * The output of a tuner might be routed directly to the output buss associated with
409      * AudioAttributes.USAGE_MEDIA while the tuner is playing.
410      *
411      * @param sourceAddress the input port name obtained from getExternalSources().
412      * @param usage the type of audio represented by this source (usually USAGE_MEDIA).
413      * @param gainInMillibels How many steps above the minimum value defined for the source port to
414      *                       set the gain when creating the patch.
415      *                       This may be used for source balancing without affecting the user
416      *                       controlled volumes applied to the destination ports.  A value of
417      *                       0 indicates no gain change is requested.
418      * @return A handle for the created patch which can be used to later remove it.
419      *
420      * @see #getExternalSources()
421      * @see #releaseAudioPatch(CarAudioPatchHandle)
422      *
423      * @deprecated use {@link android.media.HwAudioSource} instead
424      * @hide
425      */
426     @SystemApi
427     @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS)
428     @Deprecated
429     @AddedInOrBefore(majorVersion = 33)
createAudioPatch(String sourceAddress, @AttributeUsage int usage, int gainInMillibels)430     public CarAudioPatchHandle createAudioPatch(String sourceAddress, @AttributeUsage int usage,
431             int gainInMillibels) {
432         try {
433             return mService.createAudioPatch(sourceAddress, usage, gainInMillibels);
434         } catch (RemoteException e) {
435             return handleRemoteExceptionFromCarService(e, null);
436         }
437     }
438 
439     /**
440      * Removes the association between an input port and an output port identified by the provided
441      * handle.
442      *
443      * @param patch CarAudioPatchHandle returned from createAudioPatch().
444      *
445      * @see #getExternalSources()
446      * @see #createAudioPatch(String, int, int)
447      *
448      * @deprecated use {@link android.media.HwAudioSource} instead
449      * @hide
450      */
451     @SystemApi
452     @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS)
453     @Deprecated
454     @AddedInOrBefore(majorVersion = 33)
releaseAudioPatch(CarAudioPatchHandle patch)455     public void releaseAudioPatch(CarAudioPatchHandle patch) {
456         try {
457             mService.releaseAudioPatch(patch);
458         } catch (RemoteException e) {
459             handleRemoteExceptionFromCarService(e);
460         }
461     }
462 
463     /**
464      * Gets the count of available volume groups in primary zone.
465      *
466      * @see {@link #getVolumeGroupCount(int)}
467      * @hide
468      */
469     @SystemApi
470     @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
471     @AddedInOrBefore(majorVersion = 33)
getVolumeGroupCount()472     public int getVolumeGroupCount() {
473         return getVolumeGroupCount(PRIMARY_AUDIO_ZONE);
474     }
475 
476     /**
477      * Gets the count of available volume groups in the system.
478      *
479      * @param zoneId The zone id whois count of volume groups is queried.
480      * @return Count of volume groups
481      * @hide
482      */
483     @SystemApi
484     @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
485     @AddedInOrBefore(majorVersion = 33)
getVolumeGroupCount(int zoneId)486     public int getVolumeGroupCount(int zoneId) {
487         try {
488             return mService.getVolumeGroupCount(zoneId);
489         } catch (RemoteException e) {
490             return handleRemoteExceptionFromCarService(e, 0);
491         }
492     }
493 
494     /**
495      * Gets the volume group id for a given {@link AudioAttributes} usage in primary zone.
496      *
497      * @see {@link #getVolumeGroupIdForUsage(int, int)}
498      * @hide
499      */
500     @SystemApi
501     @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
502     @AddedInOrBefore(majorVersion = 33)
getVolumeGroupIdForUsage(@ttributeUsage int usage)503     public int getVolumeGroupIdForUsage(@AttributeUsage int usage) {
504         return getVolumeGroupIdForUsage(PRIMARY_AUDIO_ZONE, usage);
505     }
506 
507     /**
508      * Gets the volume group id for a given {@link AudioAttributes} usage.
509      *
510      * @param zoneId The zone id whose volume group is queried.
511      * @param usage The {@link AudioAttributes} usage to get a volume group from.
512      * @return The volume group id where the usage belongs to
513      * @hide
514      */
515     @SystemApi
516     @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
517     @AddedInOrBefore(majorVersion = 33)
getVolumeGroupIdForUsage(int zoneId, @AttributeUsage int usage)518     public int getVolumeGroupIdForUsage(int zoneId, @AttributeUsage int usage) {
519         try {
520             return mService.getVolumeGroupIdForUsage(zoneId, usage);
521         } catch (RemoteException e) {
522             return handleRemoteExceptionFromCarService(e, 0);
523         }
524     }
525 
526     /**
527      * Gets array of {@link AudioAttributes} usages for a volume group in primary zone.
528      *
529      * @see {@link #getUsagesForVolumeGroupId(int, int)}
530      * @hide
531      */
532     @SystemApi
533     @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
534     @AddedInOrBefore(majorVersion = 33)
getUsagesForVolumeGroupId(int groupId)535     public @NonNull int[] getUsagesForVolumeGroupId(int groupId) {
536         return getUsagesForVolumeGroupId(PRIMARY_AUDIO_ZONE, groupId);
537     }
538 
539     /**
540      * Returns the volume group info associated with the zone id and group id.
541      *
542      * <p>The volume information, including mute, blocked, limited state will reflect the state
543      * of the volume group at the time of query.
544      *
545      * @param zoneId zone id for the group to query
546      * @param groupId group id for the group to query
547      * @throws IllegalArgumentException if the audio zone or group id are invalid
548      *
549      * @return the current volume group info
550      *
551      * @hide
552      */
553     @SystemApi
554     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3,
555             minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0)
556     @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
557     @Nullable
getVolumeGroupInfo(int zoneId, int groupId)558     public CarVolumeGroupInfo getVolumeGroupInfo(int zoneId, int groupId) {
559         try {
560             return mService.getVolumeGroupInfo(zoneId, groupId);
561         } catch (RemoteException e) {
562             return handleRemoteExceptionFromCarService(e, null);
563         }
564     }
565 
566     /**
567      * Returns a list of volume group info associated with the zone id.
568      *
569      * <p>The volume information, including mute, blocked, limited state will reflect the state
570      * of the volume group at the time of query.
571      *
572      * @param zoneId zone id for the group to query
573      * @throws IllegalArgumentException if the audio zone is invalid
574      *
575      * @return all the current volume group info's for the zone id
576      *
577      * @hide
578      */
579     @SystemApi
580     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3,
581             minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0)
582     @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
583     @NonNull
getVolumeGroupInfosForZone(int zoneId)584     public List<CarVolumeGroupInfo> getVolumeGroupInfosForZone(int zoneId) {
585         try {
586             return mService.getVolumeGroupInfosForZone(zoneId);
587         } catch (RemoteException e) {
588             return handleRemoteExceptionFromCarService(e, Collections.EMPTY_LIST);
589         }
590     }
591 
592     /**
593      * Returns a list of audio attributes associated with the volume group info.
594      *
595      * @param groupInfo group info to query
596      * @throws NullPointerException if the volume group info is {@code null}
597      *
598      * @return list of audio attributes associated with the volume group info
599      *
600      * @hide
601      */
602     @SystemApi
603     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3,
604             minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0)
605     @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
606     @NonNull
getAudioAttributesForVolumeGroup( @onNull CarVolumeGroupInfo groupInfo)607     public List<AudioAttributes> getAudioAttributesForVolumeGroup(
608             @NonNull CarVolumeGroupInfo groupInfo) {
609         try {
610             return mService.getAudioAttributesForVolumeGroup(groupInfo);
611         } catch (RemoteException e) {
612             return handleRemoteExceptionFromCarService(e, Collections.EMPTY_LIST);
613         }
614     }
615 
616     /**
617      * Gets array of {@link AudioAttributes} usages for a volume group in a zone.
618      *
619      * @param zoneId The zone id whose volume group is queried.
620      * @param groupId The volume group id whose associated audio usages is returned.
621      * @return Array of {@link AudioAttributes} usages for a given volume group id
622      * @hide
623      */
624     @SystemApi
625     @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
626     @AddedInOrBefore(majorVersion = 33)
getUsagesForVolumeGroupId(int zoneId, int groupId)627     public @NonNull int[] getUsagesForVolumeGroupId(int zoneId, int groupId) {
628         try {
629             return mService.getUsagesForVolumeGroupId(zoneId, groupId);
630         } catch (RemoteException e) {
631             return handleRemoteExceptionFromCarService(e, new int[0]);
632         }
633     }
634 
635     /**
636      * Determines if a particular volume group has any audio playback in a zone
637      *
638      * @param zoneId The zone id whose volume group is queried.
639      * @param groupId The volume group id whose associated audio usages is returned.
640      * @return {@code true} if the group has active playback, {@code false} otherwise
641      *
642      * @hide
643      */
644     @SystemApi
645     @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
646     @AddedInOrBefore(majorVersion = 33)
isPlaybackOnVolumeGroupActive(int zoneId, int groupId)647     public boolean isPlaybackOnVolumeGroupActive(int zoneId, int groupId) {
648         try {
649             return mService.isPlaybackOnVolumeGroupActive(zoneId, groupId);
650         } catch (RemoteException e) {
651             return handleRemoteExceptionFromCarService(e, false);
652         }
653     }
654 
655     /**
656      * Gets the audio zones currently available
657      *
658      * @return audio zone ids
659      * @hide
660      */
661     @SystemApi
662     @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS)
663     @AddedInOrBefore(majorVersion = 33)
getAudioZoneIds()664     public @NonNull List<Integer> getAudioZoneIds() {
665         try {
666             int[] zoneIdArray = mService.getAudioZoneIds();
667             List<Integer> zoneIdList = new ArrayList<Integer>(zoneIdArray.length);
668             for (int zoneIdValue : zoneIdArray) {
669                 zoneIdList.add(zoneIdValue);
670             }
671             return zoneIdList;
672         } catch (RemoteException e) {
673             return handleRemoteExceptionFromCarService(e, Collections.emptyList());
674         }
675     }
676 
677     /**
678      * Gets the audio zone id currently mapped to uId,
679      * defaults to PRIMARY_AUDIO_ZONE if no mapping exist
680      *
681      * @param uid The uid to map
682      * @return zone id mapped to uid
683      * @hide
684      */
685     @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS)
686     @AddedInOrBefore(majorVersion = 33)
getZoneIdForUid(int uid)687     public int getZoneIdForUid(int uid) {
688         try {
689             return mService.getZoneIdForUid(uid);
690         } catch (RemoteException e) {
691             return handleRemoteExceptionFromCarService(e, 0);
692         }
693     }
694 
695     /**
696      * Maps the audio zone id to uid
697      *
698      * @param zoneId The audio zone id
699      * @param uid The uid to map
700      * @return true if the uid is successfully mapped
701      * @hide
702      */
703     @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS)
704     @AddedInOrBefore(majorVersion = 33)
setZoneIdForUid(int zoneId, int uid)705     public boolean setZoneIdForUid(int zoneId, int uid) {
706         try {
707             return mService.setZoneIdForUid(zoneId, uid);
708         } catch (RemoteException e) {
709             return handleRemoteExceptionFromCarService(e, false);
710         }
711     }
712 
713     /**
714      * Clears the current zone mapping of the uid
715      *
716      * @param uid The uid to clear
717      * @return true if the zone was successfully cleared
718      * @hide
719      */
720     @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS)
721     @AddedInOrBefore(majorVersion = 33)
clearZoneIdForUid(int uid)722     public boolean clearZoneIdForUid(int uid) {
723         try {
724             return mService.clearZoneIdForUid(uid);
725         } catch (RemoteException e) {
726             return handleRemoteExceptionFromCarService(e, false);
727         }
728     }
729 
730     /**
731      * Gets the output device for a given {@link AudioAttributes} usage in zoneId.
732      *
733      * <p><b>Note:</b> To be used for routing to a specific device. Most applications should
734      * use the regular routing mechanism, which is to set audio attribute usage to
735      * an audio track.
736      *
737      * @param zoneId zone id to query for device
738      * @param usage usage where audio is routed
739      * @return Audio device info, returns {@code null} if audio device usage fails to map to
740      * an active audio device. This is different from the using an invalid value for
741      * {@link AudioAttributes} usage. In the latter case the query will fail with a
742      * RuntimeException indicating the issue.
743      *
744      * @hide
745      */
746     @SystemApi
747     @Nullable
748     @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS)
749     @AddedInOrBefore(majorVersion = 33)
getOutputDeviceForUsage(int zoneId, @AttributeUsage int usage)750     public AudioDeviceInfo getOutputDeviceForUsage(int zoneId, @AttributeUsage int usage) {
751         try {
752             String deviceAddress = mService.getOutputDeviceAddressForUsage(zoneId, usage);
753             if (deviceAddress == null) {
754                 return null;
755             }
756             AudioDeviceInfo[] outputDevices =
757                     mAudioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS);
758             for (AudioDeviceInfo info : outputDevices) {
759                 if (info.getAddress().equals(deviceAddress)) {
760                     return info;
761                 }
762             }
763             return null;
764         } catch (RemoteException e) {
765             return handleRemoteExceptionFromCarService(e, null);
766         }
767     }
768 
769     /**
770      * Gets the input devices for an audio zone
771      *
772      * @return list of input devices
773      * @hide
774      */
775     @SystemApi
776     @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS)
777     @AddedInOrBefore(majorVersion = 33)
getInputDevicesForZoneId(int zoneId)778     public @NonNull List<AudioDeviceInfo> getInputDevicesForZoneId(int zoneId) {
779         try {
780             return convertInputDevicesToDeviceInfos(
781                     mService.getInputDevicesForZoneId(zoneId),
782                     AudioManager.GET_DEVICES_INPUTS);
783         } catch (RemoteException e) {
784             return handleRemoteExceptionFromCarService(e, Collections.EMPTY_LIST);
785         }
786     }
787 
788     /** @hide */
789     @Override
790     @AddedInOrBefore(majorVersion = 33)
onCarDisconnected()791     public void onCarDisconnected() {
792         if (mService != null && !mCarVolumeCallbacks.isEmpty()) {
793             unregisterVolumeCallback();
794         }
795     }
796 
797     /** @hide */
CarAudioManager(Car car, IBinder service)798     public CarAudioManager(Car car, IBinder service) {
799         super(car);
800         mService = ICarAudio.Stub.asInterface(service);
801         mAudioManager = getContext().getSystemService(AudioManager.class);
802         mCarVolumeCallbacks = new CopyOnWriteArrayList<>();
803         mEventHandler = new EventHandler(getEventHandler().getLooper());
804     }
805 
806     /**
807      * Registers a {@link CarVolumeCallback} to receive volume change callbacks
808      * @param callback {@link CarVolumeCallback} instance, can not be null
809      * <p>
810      * Requires permission Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME
811      */
812     @AddedInOrBefore(majorVersion = 33)
registerCarVolumeCallback(@onNull CarVolumeCallback callback)813     public void registerCarVolumeCallback(@NonNull CarVolumeCallback callback) {
814         Objects.requireNonNull(callback);
815 
816         if (mCarVolumeCallbacks.isEmpty()) {
817             registerVolumeCallback();
818         }
819 
820         mCarVolumeCallbacks.add(callback);
821     }
822 
823     /**
824      * Unregisters a {@link CarVolumeCallback} from receiving volume change callbacks
825      * @param callback {@link CarVolumeCallback} instance previously registered, can not be null
826      * <p>
827      * Requires permission Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME
828      */
829     @AddedInOrBefore(majorVersion = 33)
unregisterCarVolumeCallback(@onNull CarVolumeCallback callback)830     public void unregisterCarVolumeCallback(@NonNull CarVolumeCallback callback) {
831         Objects.requireNonNull(callback);
832         if (mCarVolumeCallbacks.remove(callback) && mCarVolumeCallbacks.isEmpty()) {
833             unregisterVolumeCallback();
834         }
835     }
836 
registerVolumeCallback()837     private void registerVolumeCallback() {
838         try {
839             mService.registerVolumeCallback(mCarVolumeCallbackImpl.asBinder());
840         } catch (RemoteException e) {
841             Log.e(CarLibLog.TAG_CAR, "registerVolumeCallback failed", e);
842         }
843     }
844 
unregisterVolumeCallback()845     private void unregisterVolumeCallback() {
846         try {
847             mService.unregisterVolumeCallback(mCarVolumeCallbackImpl.asBinder());
848         } catch (RemoteException e) {
849             handleRemoteExceptionFromCarService(e);
850         }
851     }
852 
853     /**
854      * Returns the whether a volume group is muted
855      *
856      * <p><b>Note:<b/> If {@link #AUDIO_FEATURE_VOLUME_GROUP_MUTING} is disabled this will always
857      * return {@code false} as group mute is disabled.
858      *
859      * @param zoneId The zone id whose volume groups is queried.
860      * @param groupId The volume group id whose mute state is returned.
861      * @return {@code true} if the volume group is muted, {@code false}
862      * otherwise
863      *
864      * @hide
865      */
866     @SystemApi
867     @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
868     @AddedInOrBefore(majorVersion = 33)
isVolumeGroupMuted(int zoneId, int groupId)869     public boolean isVolumeGroupMuted(int zoneId, int groupId) {
870         try {
871             return mService.isVolumeGroupMuted(zoneId, groupId);
872         } catch (RemoteException e) {
873             return handleRemoteExceptionFromCarService(e, false);
874         }
875     }
876 
877     /**
878      * Sets a volume group mute
879      *
880      * <p><b>Note:<b/> If {@link #AUDIO_FEATURE_VOLUME_GROUP_MUTING} is disabled this will throw an
881      * error indicating the issue.
882      *
883      * @param zoneId The zone id whose volume groups will be changed.
884      * @param groupId The volume group id whose mute state will be changed.
885      * @param mute {@code true} to mute volume group, {@code false} otherwise
886      * @param flags One or more flags (e.g., {@link android.media.AudioManager#FLAG_SHOW_UI},
887      * {@link android.media.AudioManager#FLAG_PLAY_SOUND})
888      *
889      * @hide
890      */
891     @SystemApi
892     @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
893     @AddedInOrBefore(majorVersion = 33)
setVolumeGroupMute(int zoneId, int groupId, boolean mute, int flags)894     public void setVolumeGroupMute(int zoneId, int groupId, boolean mute, int flags) {
895         try {
896             mService.setVolumeGroupMute(zoneId, groupId, mute, flags);
897         } catch (RemoteException e) {
898             handleRemoteExceptionFromCarService(e);
899         }
900     }
901 
convertInputDevicesToDeviceInfos( List<AudioDeviceAttributes> devices, int flag)902     private List<AudioDeviceInfo> convertInputDevicesToDeviceInfos(
903             List<AudioDeviceAttributes> devices, int flag) {
904         int addressesSize = devices.size();
905         Set<String> deviceAddressMap = new HashSet<>(addressesSize);
906         for (int i = 0; i < addressesSize; ++i) {
907             AudioDeviceAttributes device = devices.get(i);
908             deviceAddressMap.add(device.getAddress());
909         }
910         List<AudioDeviceInfo> deviceInfoList = new ArrayList<>(devices.size());
911         AudioDeviceInfo[] inputDevices = mAudioManager.getDevices(flag);
912         for (int i = 0; i < inputDevices.length; ++i) {
913             AudioDeviceInfo info = inputDevices[i];
914             if (info.isSource() && deviceAddressMap.contains(info.getAddress())) {
915                 deviceInfoList.add(info);
916             }
917         }
918         return deviceInfoList;
919     }
920 
921     private final class EventHandler extends Handler {
922         private static final int MSG_GROUP_VOLUME_CHANGE = 1;
923         private static final int MSG_GROUP_MUTE_CHANGE = 2;
924         private static final int MSG_MASTER_MUTE_CHANGE = 3;
925 
EventHandler(Looper looper)926         private EventHandler(Looper looper) {
927             super(looper);
928         }
929 
930         @Override
handleMessage(Message msg)931         public void handleMessage(Message msg) {
932             switch (msg.what) {
933                 case MSG_GROUP_VOLUME_CHANGE:
934                     VolumeGroupChangeInfo volumeInfo = (VolumeGroupChangeInfo) msg.obj;
935                     handleOnGroupVolumeChanged(volumeInfo.mZoneId, volumeInfo.mGroupId,
936                             volumeInfo.mFlags);
937                     break;
938                 case MSG_GROUP_MUTE_CHANGE:
939                     VolumeGroupChangeInfo muteInfo = (VolumeGroupChangeInfo) msg.obj;
940                     handleOnGroupMuteChanged(muteInfo.mZoneId, muteInfo.mGroupId, muteInfo.mFlags);
941                     break;
942                 case MSG_MASTER_MUTE_CHANGE:
943                     handleOnMasterMuteChanged(msg.arg1, msg.arg2);
944                     break;
945                 default:
946                     Log.e(CarLibLog.TAG_CAR, "Unknown nessage not handled:" + msg.what);
947                     break;
948             }
949         }
950 
dispatchOnGroupVolumeChanged(int zoneId, int groupId, int flags)951         private void dispatchOnGroupVolumeChanged(int zoneId, int groupId, int flags) {
952             VolumeGroupChangeInfo volumeInfo = new VolumeGroupChangeInfo(zoneId, groupId, flags);
953             sendMessage(obtainMessage(MSG_GROUP_VOLUME_CHANGE, volumeInfo));
954         }
955 
dispatchOnMasterMuteChanged(int zoneId, int flags)956         private void dispatchOnMasterMuteChanged(int zoneId, int flags) {
957             sendMessage(obtainMessage(MSG_MASTER_MUTE_CHANGE, zoneId, flags));
958         }
959 
dispatchOnGroupMuteChanged(int zoneId, int groupId, int flags)960         private void dispatchOnGroupMuteChanged(int zoneId, int groupId, int flags) {
961             VolumeGroupChangeInfo volumeInfo = new VolumeGroupChangeInfo(zoneId, groupId, flags);
962             sendMessage(obtainMessage(MSG_GROUP_MUTE_CHANGE, volumeInfo));
963         }
964 
965         private class VolumeGroupChangeInfo {
966             public int mZoneId;
967             public int mGroupId;
968             public int mFlags;
969 
VolumeGroupChangeInfo(int zoneId, int groupId, int flags)970             VolumeGroupChangeInfo(int zoneId, int groupId, int flags) {
971                 mZoneId = zoneId;
972                 mGroupId = groupId;
973                 mFlags = flags;
974             }
975         }
976     }
977 
handleOnGroupVolumeChanged(int zoneId, int groupId, int flags)978     private void handleOnGroupVolumeChanged(int zoneId, int groupId, int flags) {
979         for (CarVolumeCallback callback : mCarVolumeCallbacks) {
980             callback.onGroupVolumeChanged(zoneId, groupId, flags);
981         }
982     }
983 
handleOnMasterMuteChanged(int zoneId, int flags)984     private void handleOnMasterMuteChanged(int zoneId, int flags) {
985         for (CarVolumeCallback callback : mCarVolumeCallbacks) {
986             callback.onMasterMuteChanged(zoneId, flags);
987         }
988     }
989 
handleOnGroupMuteChanged(int zoneId, int groupId, int flags)990     private void handleOnGroupMuteChanged(int zoneId, int groupId, int flags) {
991         for (CarVolumeCallback callback : mCarVolumeCallbacks) {
992             callback.onGroupMuteChanged(zoneId, groupId, flags);
993         }
994     }
995 
996     /**
997      * Callback interface to receive volume change events in a car.
998      * Extend this class and register it with {@link #registerCarVolumeCallback(CarVolumeCallback)}
999      * and unregister it via {@link #unregisterCarVolumeCallback(CarVolumeCallback)}
1000      */
1001     public abstract static class CarVolumeCallback {
1002         /**
1003          * This is called whenever a group volume is changed.
1004          * The changed-to volume index is not included, the caller is encouraged to
1005          * get the current group volume index via CarAudioManager.
1006          *
1007          * @param zoneId Id of the audio zone that volume change happens
1008          * @param groupId Id of the volume group that volume is changed
1009          * @param flags see {@link android.media.AudioManager} for flag definitions
1010          */
1011         @AddedInOrBefore(majorVersion = 33)
onGroupVolumeChanged(int zoneId, int groupId, int flags)1012         public void onGroupVolumeChanged(int zoneId, int groupId, int flags) {}
1013 
1014         /**
1015          * This is called whenever the global mute state is changed.
1016          * The changed-to global mute state is not included, the caller is encouraged to
1017          * get the current global mute state via AudioManager.
1018          *
1019          * <p><b>Note:<b/> If {@link CarAudioManager#AUDIO_FEATURE_VOLUME_GROUP_MUTING} is disabled
1020          * this will be triggered on mute changes. Otherwise, car audio mute changes will trigger
1021          * {@link #onGroupMuteChanged(int, int, int)}
1022          *
1023          * @param zoneId Id of the audio zone that global mute state change happens
1024          * @param flags see {@link android.media.AudioManager} for flag definitions
1025          */
1026         @AddedInOrBefore(majorVersion = 33)
onMasterMuteChanged(int zoneId, int flags)1027         public void onMasterMuteChanged(int zoneId, int flags) {}
1028 
1029         /**
1030          * This is called whenever a group mute state is changed.
1031          * The changed-to mute state is not included, the caller is encouraged to
1032          * get the current group mute state via CarAudioManager.
1033          *
1034          * <p><b>Note:<b/> If {@link CarAudioManager#AUDIO_FEATURE_VOLUME_GROUP_MUTING} is enabled
1035          * this will be triggered on mute changes. Otherwise, car audio mute changes will trigger
1036          * {@link #onMasterMuteChanged(int, int)}
1037          *
1038          * @param zoneId Id of the audio zone that volume change happens
1039          * @param groupId Id of the volume group that volume is changed
1040          * @param flags see {@link android.media.AudioManager} for flag definitions
1041          */
1042         @AddedInOrBefore(majorVersion = 33)
onGroupMuteChanged(int zoneId, int groupId, int flags)1043         public void onGroupMuteChanged(int zoneId, int groupId, int flags) {}
1044     }
1045 }
1046